--- /dev/null
+---
+# Things explicitly enumerated in CONTRIBUTING.md
+IndentWidth: 4
+UseTab: Never
+SpaceBeforeParens: Never
+BreakBeforeBraces: Attach
+ColumnLimit: 100
+# Docs say 100, but some places look more like 80.
+#ColumnLimit: 80
+
+# Based on minimizing diff when applying clang-format
+AccessModifierOffset: -4
+AlignConsecutiveAssignments: true
+AlignEscapedNewlines: DontAlign
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakTemplateDeclarations: Yes # MultiLine
+BreakConstructorInitializers: BeforeColon
+DerivePointerAlignment: true
+FixNamespaceComments: true
+IndentPPDirectives: AfterHash
+MaxEmptyLinesToKeep: 2
+NamespaceIndentation: Inner
+SpaceAfterTemplateKeyword: false
+
+# No way to remove all space around operators as seen in much of the code I looked at.
+#SpaceBeforeAssignmentOperators: false
+
+# Only seen some of the time (mostly just variables, not functions)
+# but clang-format only has a true/false
+#AlignConsecutiveDeclarations: true
+
+
+# Would be nice to turn on eventually, maybe?
+SortIncludes: false
+SortUsingDeclarations: false
+
+# Hard to tell what the desired config here was.
+# AllowShortFunctionsOnASingleLine: Inline
+AllowShortFunctionsOnASingleLine: None
+os: linux
+dist: xenial
language: c
-os:
- - linux
- - osx
-sudo: required
-dist: trusty
-install:
- - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./.travis/install-debian.sh; fi
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./.travis/install-macos.sh; fi
-script:
- - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./.travis/build-debian.sh; fi
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./.travis/build-macos.sh; fi
-deploy:
- - provider: releases
- api_key:
- secure: dDlkIawHcODlW9B/20/cQCtzeoocvs0hKuNngRKXKqzXLWTRq33oq/B7+39tAixWbmv6exTpijiKrRNFiSCW5Z4iwHLwaRD4XJznxw63e/Hus/dxg2Tvqx7XFpkCz8mT1Z+gZQE5YxAngeZPpI/sZbZtF1UO3yH5eLeeokZ15p26ZskQUPoYuzrTgTzYL3XfpG3F+20rNBawH1ycsCTVD/08/n31d2m3CrKAsbW7er92ek6w4fzKr7NW8WeXjrPJETVpw5fQg1Od3pRGW8dPQaJcvKQEogMp8Mm0ETYd0qigg89/giBz7QwOgmAWQ4dH+DfZH4Ojl//127QztBolMvyDMQBykWrtJoGcij05sT6K2IJr2FHeUBO12MAEdjiVvhQj3DtTzjPiZAHHDBSLWxLKWWhlhHE4pq7g1MQhqXkaAHI2BLNzwLmaowbMT0bECf9yfz6xx18h6XPQFX44oOktraobVALFlyHqeKa8zdcUt22LF6uAL1m5dxL0tny3eXCIPE4UH/RZgua/cHV9G3cUvKQa/QnFSLRhvWVSbGB+7YsHouBJcsUOOW1gmd5442XuC7mpppccRldh+GSxUk6TBJRAx7TeQ0ybDUaoco9MUqp2twv3KreR2+8Q12PDaAhfQVNEGdF3wTm1sShImjCN4VN3eSLlBEbve1QRQXM=
- skip_cleanup: true
- file: build/solvespace.dmg
- on:
- repo: solvespace/solvespace
- tags: true
- condition: "$TRAVIS_OS_NAME == osx"
+git:
+ submodules: false
+if: tag != edge
+stages:
+ - test
+ - name: clean
+ if: (NOT type IN (pull_request)) AND (branch = master)
+ - name: deploy
+ if: (NOT type IN (pull_request)) AND (branch = master OR tag IS present)
+jobs:
+ include:
+ - stage: clean
+ name: Remove Github Release
+ os: linux
+ dist: bionic
+ script: .travis/remove-edge.sh
+ - stage: test
+ name: macOS
+ os: osx
+ osx_image: xcode12.2
+ install: ".travis/install-macos.sh"
+ script: ".travis/build-macos.sh"
+ - stage: deploy
+ name: macOS
+ os: osx
+ osx_image: xcode12.2
+ install: ".travis/install-macos.sh"
+ script: ".travis/build-macos.sh release && .travis/sign-macos.sh"
+ before_deploy: source .travis/tag-edge.sh
+ deploy:
+ provider: releases
+ skip_cleanup: true
+ prerelease: true
+ overwrite: true
+ edge: true
+ name: ${TRAVIS_TAG:-edge}
+ release_notes: $TRAVIS_COMMIT_MESSAGE
+ file: build/bin/SolveSpace.dmg
+ on:
+ all_branches: true
+ - stage: test
+ name: "Ubuntu"
+ os: linux
+ dist: bionic
+ install: .travis/install-ubuntu.sh
+ script: .travis/build-ubuntu.sh
+ - stage: test
+ name: "Windows"
+ os: windows
+ install: .travis/install-windows.sh
+ script: .travis/build-windows.sh
+ - stage: deploy
+ name: "Windows"
+ os: windows
+ install: .travis/install-windows.sh
+ script: .travis/build-windows.sh release
+ before_deploy: source .travis/tag-edge.sh
+ deploy:
+ provider: releases
+ skip_cleanup: true
+ prerelease: true
+ overwrite: true
+ edge: true
+ name: ${TRAVIS_TAG:-edge}
+ release_notes: $TRAVIS_COMMIT_MESSAGE
+ file: build/bin/RelWithDebInfo/solvespace.exe
+ on:
+ all_branches: true
+ - stage: deploy
+ name: "Windows with OpenMP"
+ os: windows
+ install: .travis/install-windows.sh
+ script: .travis/build-windows.sh release openmp
+ before_deploy: source .travis/tag-edge.sh
+ deploy:
+ provider: releases
+ skip_cleanup: true
+ prerelease: true
+ overwrite: true
+ edge: true
+ name: ${TRAVIS_TAG:-edge}
+ release_notes: $TRAVIS_COMMIT_MESSAGE
+ file: build/bin/RelWithDebInfo/solvespace-openmp.exe
+ on:
+ all_branches: true
+ - &deploy-snap
+ stage: deploy
+ name: Snap amd64
+ os: linux
+ arch: amd64
+ dist: bionic
+ addons:
+ snaps:
+ - name: snapcraft
+ confinement: classic
+ script: .travis/build-snap.sh
+ deploy:
+ - provider: script
+ script: sudo .travis/deploy-snap.sh edge
+ skip_cleanup: true
+ - provider: script
+ script: sudo .travis/deploy-snap.sh edge,beta
+ skip_cleanup: true
+ on:
+ tags: true
+ - <<: *deploy-snap
+ name: Snap arm64
+ arch: arm64-graviton2
+ group: edge
+ virt: lxd
Changelog
=========
+3.0
+---
+
+New sketch features:
+ * New intersection boolean operation for solid models.
+ * New groups, revolution and helical extrusion.
+ * Extrude, lathe, translate and rotate groups can use the "assembly"
+ boolean operation, to increase performance.
+ * The solid model of extrude and lathe groups can be suppressed,
+ for splitting a single model in multiple parts to export,
+ or if only the generated entities are desired, without the mesh.
+ * Translate and rotate groups can create n-dimensional arrays using
+ the "difference" and "assembly" boolean operations.
+ * A new sketch in workplane group can be created based on existing workplane.
+ * TTF text request has two additional points on the right side, which allow
+ constraining the width of text.
+ * Image requests can now be created, similar to TTF text requests.
+ This replaces the "style → background image" feature.
+ * Irrelevant points (e.g. arc center point) are not counted when estimating
+ the bounding box used to compute chord tolerance.
+ * When adding a constraint which has a label and is redundant with another
+ constraint, the constraint is added as a reference, avoiding an error.
+ * Datum points can be copied and pasted.
+ * "Split Curves at Intersection" can now split curves at point lying on curve,
+ not just at intersection of two curves.
+ * Property browser now shows amount of degrees of freedom in group list.
+
+New constraint features:
+ * When dragging an arc or rectangle point, it will be automatically
+ constrained to other points with a click.
+ * When selecting a constraint, the requests it constraints can be selected
+ in the text window.
+ * When selecting an entity, the constraints applied to it can be selected
+ in the text window.
+ * Distance constraint labels can now be formatted to use SI prefixes.
+ Values are edited in the configured unit regardless of label format.
+ * When creating a constraint, if an exactly identical constraint already
+ exists, it is now selected instead of adding a redundant constraint.
+ * It is now possible to turn off automatic creation of horizontal/vertical
+ constraints on line segments.
+ * Automatic creation of constraints no longer happens if the constraint
+ would have been redundant with other ones.
+ * New option to open the constraint editor for newly created constraints
+ with a value.
+
+New export/import features:
+ * Link IDF circuit boards in an assembly (.emn files)
+ * Three.js: allow configuring projection for exported model, and initially
+ use the current viewport projection.
+ * Wavefront OBJ: a material file is exported alongside the model, containing
+ mesh color information.
+ * DXF/DWG: 3D DXF files are imported as construction entities, in 3d.
+ * Q3D: [Q3D](https://github.com/q3k/q3d/) triangle meshes can now be
+ exported. This format allows to easily hack on triangle mesh data created
+ in SolveSpace, supports colour information and is more space efficient than
+ most other formats.
+ * VRML (WRL) triangle meshes can now be exported, useful for e.g. [KiCAD](http://kicad.org).
+ * Export 2d section: custom styled entities that lie in the same
+ plane as the exported section are included.
+
+New rendering features:
+ * The "Show/hide hidden lines" button is now a tri-state button that allows
+ showing all lines (on top of shaded mesh), stippling occluded lines
+ or not drawing them at all.
+ * The "Show/hide outlines" button is now independent from "Show/hide edges".
+
+New measurement/analysis features:
+ * New choice for base unit, meters.
+ * New command for measuring total length of selected entities,
+ "Analyze → Measure Perimeter".
+ * New command for measuring center of mass, with live updates as the sketch
+ changes, "Analyze → Center of Mass".
+ * New option for displaying areas of closed contours.
+ * When calculating volume of the mesh, volume of the solid from the current
+ group is now shown alongside total volume of all solids.
+ * When calculating area, and faces are selected, calculate area of those faces
+ instead of the closed contour in the sketch.
+ * When selecting a point and a line, projected distance to current
+ workplane is displayed.
+
+Other new features:
+ * Added ExportBackgroundColor in configuration for EPS, PDF, and SVG files.
+ * Improvements to the text window for selected entities and constraints.
+ * Ambient light source added in text window to allow flat shaded renderings.
+ * New command-line interface, for batch exporting and more.
+ * The graphical interface now supports HiDPI screens on every OS.
+ * New option to lock Z axis to be always vertical, like in SketchUp.
+ * New button to hide all construction entities.
+ * New link to match the on-screen size of the sketch with its actual size,
+ "view → set to full scale".
+ * When zooming to fit, constraints are also considered.
+ * Ctrl-clicking entities now deselects them, as the inverse of clicking.
+ * When clicking on an entity that shares a place with other entities,
+ the entity from the current group is selected.
+ * When dragging an entity that shares a place with other entities,
+ the entity from a request is selected. For example, dragging a point on
+ a face of an extrusion coincident with the source sketch plane will
+ drag the point from the source sketch.
+ * The default font for TTF text is now Bitstream Vera Sans, which is
+ included in the resources such that it is available on any OS.
+ * In expressions, numbers can contain the digit group separator, "_".
+ * The "=" key is bound to "Zoom In", like "+" key.
+ * The numpad decimal separator key is bound to "." regardless of locale.
+ * On Windows, full-screen mode is implemented.
+ * On Linux, native file chooser dialog can be used.
+ * New edit menu items "Line Styles", "View Projection" and "Configuration"
+ that are shortcuts to the respective configuration screens.
+ * New cmake build options using -DENABLE_OPENMP=yes and -DENABLE_LTO=yes
+ to enable support for multi-threading and link-time optimization.
+
+Bugs fixed:
+ * Fixed broken --view options for command line thumbnail image creation.
+ * Some errors in Triangulation of surfaces.
+ * Some NURNS boolean operations that failed particularly on surfaces
+ created with Lathe, Revolve, or Helix.
+ * Segfault in Remove Spline Point context menu.
+ * A point in 3d constrained to any line whose length is free no longer
+ causes the line length to collapse.
+ * Curve-line constraints (in 3d), parallel constraints (in 3d), and
+ same orientation constraints are more robust.
+ * Adding some constraints (vertical, midpoint, etc) twice errors out
+ immediately, instead of later and in a confusing way.
+ * Constraining a newly placed point to a hovered entity does not cause
+ spurious changes in the sketch.
+ * Points highlighted with "Analyze → Show Degrees of Freedom" are drawn
+ on top of all other geometry.
+ * A step rotate/translate group using a group forced to triangle mesh
+ as a source group also gets forced to triangle mesh.
+ * Paste Transformed with a negative scale does not invert arcs.
+ * The tangent arc now modifies the original entities instead of deleting
+ them, such that their constraints are retained.
+ * When linking a sketch file, missing custom styles are now imported from
+ the linked file.
+ * 3Dconnexion SpaceMouse should now work (on Windows and macOS X).
+ * Improved NURBS boolean operations on curved surfaces in some cases.
+ * Show only usable fonts in the font selector.
+
+2.x
+---
+
+Bug fixes:
+ * Do not crash when changing an unconstrained lathe group between
+ union and difference modes.
+
2.3
---
# cmake configuration
-cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
-cmake_policy(VERSION 3.1.0)
+if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
+ message(FATAL_ERROR
+ "In-tree builds are not supported; please perform an out-of-tree build:\n"
+ " rm -rf CMakeCache.txt CMakeFiles/\n"
+ " mkdir build && cd build && cmake ..")
+endif()
+
+cmake_minimum_required(VERSION 3.7.2 FATAL_ERROR)
+if(NOT CMAKE_VERSION VERSION_LESS 3.11.0)
+ cmake_policy(VERSION 3.11.0)
+endif()
+if(NOT CMAKE_VERSION VERSION_LESS 3.9)
+ # LTO/IPO with non-Intel compilers on Linux requires policy CMP0069 to be set to NEW.
+ # Set it explicitly until cmake_minimum_required is raised to >= 3.9.
+ cmake_policy(SET CMP0069 NEW)
+endif()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_SOURCE_DIR}/cmake/")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
-include(CheckIncludeFile)
-
# for /MT on MSVC
set(CMAKE_USER_MAKE_RULES_OVERRIDE
"${CMAKE_SOURCE_DIR}/cmake/c_flag_overrides.cmake")
set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX
"${CMAKE_SOURCE_DIR}/cmake/cxx_flag_overrides.cmake")
+if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
+endif()
+
# project
# NOTE TO PACKAGERS: The embedded git commit hash is critical for rapid bug triage when the builds
# set(GIT_COMMIT_HASH 0000000000000000000000000000000000000000)
project(solvespace)
-set(solvespace_VERSION_MAJOR 2)
-set(solvespace_VERSION_MINOR 3)
+set(solvespace_VERSION_MAJOR 3)
+set(solvespace_VERSION_MINOR 0)
string(SUBSTRING "${GIT_COMMIT_HASH}" 0 8 solvespace_GIT_HASH)
-if(NOT WIN32 AND NOT APPLE)
- set(GUI gtk2 CACHE STRING "GUI toolkit to use (one of: gtk2 gtk3)")
+set(ENABLE_GUI ON CACHE BOOL
+ "Whether the graphical interface is enabled")
+set(ENABLE_CLI ON CACHE BOOL
+ "Whether the command line interface is enabled")
+set(ENABLE_TESTS ON CACHE BOOL
+ "Whether the test suite will be built and run")
+set(ENABLE_COVERAGE OFF CACHE BOOL
+ "Whether code coverage information will be collected")
+set(ENABLE_SANITIZERS OFF CACHE BOOL
+ "Whether to enable Clang's AddressSanitizer and UndefinedBehaviorSanitizer")
+set(ENABLE_OPENMP OFF CACHE BOOL
+ "Whether geometric operations will be parallelized using OpenMP")
+set(ENABLE_LTO OFF CACHE BOOL
+ "Whether interprocedural (global) optimizations are enabled")
+
+set(OPENGL 3 CACHE STRING "OpenGL version to use (one of: 1 3)")
+
+set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
+set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
+if("${CMAKE_GENERATOR}" STREQUAL "Xcode")
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_BINARY_DIR}/bin>)
endif()
-# compiler
-
-if(WIN32)
- add_definitions(
- -D_CRT_SECURE_NO_DEPRECATE
- -D_CRT_SECURE_NO_WARNINGS
- -D_SCL_SECURE_NO_WARNINGS
- -D_WIN32_WINNT=0x500
- -D_WIN32_IE=_WIN32_WINNT
- -DISOLATION_AWARE_ENABLED
- -DWIN32
- -DWIN32_LEAN_AND_MEAN
- -DUNICODE
- -DNOMINMAX
- -D_UNICODE)
+if(NOT CMAKE_C_COMPILER_ID STREQUAL CMAKE_CXX_COMPILER_ID)
+ message(FATAL_ERROR "C and C++ compilers should be supplied by the same vendor")
endif()
-if(MSVC)
- # Many versions of MSVC do not have the (C99) inline keyword, instead
- # they have their own __inline; this breaks `static inline` functions.
- # We do not want to care and so we fix this with a definition.
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline")
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
+ # GCC 4.8/4.9 ship with broken but present <regex>. meh.
+ message(FATAL_ERROR "GCC 5.0+ is required")
+ endif()
endif()
-if(CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
- set(WARNING_FLAGS "-Wall -Wextra -Wno-unused-parameter")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${WARNING_FLAGS}")
+# common compiler flags
+include(CheckCXXCompilerFlag)
+
+set(FILE_PREFIX_MAP "-ffile-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.")
+check_cxx_compiler_flag("${FILE_PREFIX_MAP}" HAS_FILE_PREFIX_MAP)
+if(HAS_FILE_PREFIX_MAP)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FILE_PREFIX_MAP}")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FILE_PREFIX_MAP}")
endif()
if(MINGW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")
endif()
-if(APPLE)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
+# Ensure that all platforms use 64-bit IEEE floating point operations for consistency;
+# this is most important for the testsuite, which compares savefiles directly
+# and depends on consistent rounding of intermediate results.
+if(CMAKE_SYSTEM_PROCESSOR STREQUAL "i686" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "X86")
+ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+ set(FLOAT_FLAGS "-mfpmath=sse -msse2")
+ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(FLOAT_FLAGS "-msse2")
+ endif()
+
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLOAT_FLAGS}")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLOAT_FLAGS}")
endif()
-if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
- set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}")
+if(ENABLE_LTO)
+ include(CheckIPOSupported)
+ check_ipo_supported()
+ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
-if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
+if(ENABLE_OPENMP)
+ find_package( OpenMP REQUIRED )
+ include(FindOpenMP)
+ if(OPENMP_FOUND)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
+ message(STATUS "found OpenMP, compiling with flags: " ${OpenMP_CXX_FLAGS} )
+ endif()
+endif()
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}")
endif()
-if(SANITIZE)
- if(NOT (CMAKE_C_COMPILER_ID STREQUAL Clang AND CMAKE_CXX_COMPILER_ID STREQUAL Clang))
- message(ERROR "Sanitizers are only available in Clang/Clang++")
+if(ENABLE_SANITIZERS)
+ if(NOT SANITIZERS)
+ set(SANITIZERS "address;undefined")
+ endif()
+
+ if("thread" IN_LIST SANITIZERS)
+ list(REMOVE_ITEM SANITIZERS "thread")
+ list(APPEND SANITIZE_OPTIONS thread)
endif()
- set(SANITIZE_FLAGS "-O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls")
- set(SANITIZE_FLAGS "${SANITIZE_FLAGS} -fsanitize=address,undefined,integer")
+ if("address" IN_LIST SANITIZERS)
+ list(REMOVE_ITEM SANITIZERS "address")
+ list(APPEND SANITIZE_OPTIONS address)
+ endif()
+ if("undefined" IN_LIST SANITIZERS)
+ list(REMOVE_ITEM SANITIZERS "undefined")
+ list(APPEND SANITIZE_OPTIONS alignment bounds)
+ list(APPEND SANITIZE_OPTIONS shift signed-integer-overflow integer-divide-by-zero)
+ list(APPEND SANITIZE_OPTIONS null bool enum)
+ list(APPEND SANITIZE_OPTIONS return)
+ endif()
+ if(SANITIZERS)
+ message(FATAL_ERROR "Unknown sanitizer(s) ${SANITIZERS}")
+ else()
+ message(STATUS "Using sanitizer options ${SANITIZE_OPTIONS}")
+ endif()
+
+ string(REPLACE ";" "," SANITIZE_OPTIONS "${SANITIZE_OPTIONS}")
+
+ if (NOT APPLE)
+ set(SANITIZE_FLAGS "-O1 -fsanitize=${SANITIZE_OPTIONS} -fno-sanitize-recover=address,undefined")
+ else()
+ set(SANITIZE_FLAGS "-O1 -fsanitize=${SANITIZE_OPTIONS}")
+ endif()
+
+ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(SANITIZE_FLAGS "${SANITIZE_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls")
+ elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+ set(SANITIZE_FLAGS "${SANITIZE_FLAGS} -fuse-ld=gold")
+ else()
+ message(FATAL_ERROR "Sanitizers are only available when using GCC or Clang")
+ endif()
+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZE_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZE_FLAGS}")
endif()
-# dependencies
+# common dependencies
-find_package(OpenGL REQUIRED)
+if(APPLE)
+ set(CMAKE_FIND_FRAMEWORK LAST)
+endif()
message(STATUS "Using in-tree libdxfrw")
add_subdirectory(extlib/libdxfrw)
-if(WIN32)
- # Configure Freetype first. If done later, it will notice that
- # zlib is available, try to use it and promptly break on MSVC
- # in a very obscure way. Given that the only use of zlib, bzip2
- # and png support is in support for extremely obsolete Unix fonts,
- # we don't care.
- find_package(Freetype)
+message(STATUS "Using in-tree flatbuffers")
+set(FLATBUFFERS_BUILD_FLATLIB ON CACHE BOOL "")
+set(FLATBUFFERS_BUILD_FLATC ON CACHE BOOL "")
+set(FLATBUFFERS_BUILD_FLATHASH OFF CACHE BOOL "")
+set(FLATBUFFERS_BUILD_TESTS OFF CACHE BOOL "")
+add_subdirectory(extlib/flatbuffers EXCLUDE_FROM_ALL)
+
+message(STATUS "Using in-tree q3d")
+add_subdirectory(extlib/q3d)
+set(Q3D_INCLUDE_DIR ${CMAKE_BINARY_DIR}/extlib/q3d)
+
+message(STATUS "Using in-tree mimalloc")
+set(MI_OVERRIDE OFF CACHE BOOL "")
+set(MI_BUILD_SHARED OFF CACHE BOOL "")
+set(MI_BUILD_OBJECT OFF CACHE BOOL "")
+set(MI_BUILD_TESTS OFF CACHE BOOL "")
+add_subdirectory(extlib/mimalloc EXCLUDE_FROM_ALL)
+set(MIMALLOC_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/extlib/mimalloc/include)
+
+if(WIN32 OR APPLE)
+ # On Win32 and macOS we use vendored packages, since there is little to no benefit
+ # to trying to find system versions. In particular, trying to link to libraries from
+ # Homebrew or macOS system libraries into the .app file is highly likely to result
+ # in incompatibilities after upgrades.
+
+ include(FindVendoredPackage)
+ include(AddVendoredSubdirectory)
+
+ set(FORCE_VENDORED_ZLIB ON)
+ set(FORCE_VENDORED_PNG ON)
+ set(FORCE_VENDORED_Freetype ON)
+
+ find_vendored_package(ZLIB zlib
+ ZLIB_LIBRARY zlibstatic
+ ZLIB_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/extlib/zlib)
+ list(APPEND ZLIB_INCLUDE_DIR ${CMAKE_BINARY_DIR}/extlib/zlib)
+
+ find_vendored_package(PNG libpng
+ SKIP_INSTALL_ALL ON
+ PNG_LIBRARY png_static
+ PNG_PNG_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/extlib/libpng)
+ list(APPEND PNG_PNG_INCLUDE_DIR ${CMAKE_BINARY_DIR}/extlib/libpng)
+
+ find_vendored_package(Freetype freetype
+ WITH_ZLIB OFF
+ WITH_BZip2 OFF
+ WITH_PNG OFF
+ WITH_HarfBuzz OFF
+ FREETYPE_LIBRARY freetype
+ FREETYPE_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extlib/freetype/include)
+
+ message(STATUS "Using in-tree pixman")
+ add_vendored_subdirectory(extlib/pixman)
+ set(PIXMAN_FOUND YES)
+ set(PIXMAN_LIBRARY pixman)
+ set(PIXMAN_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extlib/pixman/pixman)
+ list(APPEND PIXMAN_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/extlib/pixman/pixman)
+
+ message(STATUS "Using in-tree cairo")
+ add_vendored_subdirectory(extlib/cairo)
+ set(CAIRO_FOUND YES)
+ set(CAIRO_LIBRARIES cairo)
+ set(CAIRO_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extlib/cairo/src)
+ list(APPEND CAIRO_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/extlib/cairo/src)
+else()
+ # On Linux and BSDs we're a good citizen and link to system libraries.
+ find_package(Backtrace)
+ find_package(PkgConfig REQUIRED)
+ find_package(ZLIB REQUIRED)
+ find_package(PNG REQUIRED)
+ find_package(Freetype REQUIRED)
+ pkg_check_modules(CAIRO REQUIRED cairo)
+endif()
+
+# GUI dependencies
+
+if(ENABLE_GUI)
+ if(WIN32)
+ if(OPENGL STREQUAL "3")
+ message(STATUS "Using in-tree ANGLE")
+ set(ANGLE_STATIC ON CACHE INTERNAL "")
+ set(ANGLE_ENABLE_D3D9 ON CACHE INTERNAL "")
+ set(ANGLE_ENABLE_D3D11 ON CACHE INTERNAL "")
+ set(ANGLE_ENABLE_OPENGL ON CACHE INTERNAL "")
+ set(ANGLE_ENABLE_ESSL ON CACHE INTERNAL "")
+ set(ANGLE_ENABLE_GLSL ON CACHE INTERNAL "")
+ set(ANGLE_ENABLE_HLSL ON CACHE INTERNAL "")
+ add_vendored_subdirectory(extlib/angle)
+ set(OPENGL_LIBRARIES EGL GLESv2)
+ set(OPENGL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/extlib/angle/include)
+ else()
+ find_package(OpenGL REQUIRED)
+ endif()
+
+ if(MSVC AND ${CMAKE_SIZEOF_VOID_P} EQUAL 4)
+ message(STATUS "Using prebuilt SpaceWare")
+ set(SPACEWARE_FOUND TRUE)
+ set(SPACEWARE_INCLUDE_DIR
+ "${CMAKE_SOURCE_DIR}/extlib/si")
+ set(SPACEWARE_LIBRARIES
+ "${CMAKE_SOURCE_DIR}/extlib/si/siapp.lib")
+ endif()
+ elseif(APPLE)
+ find_package(OpenGL REQUIRED)
+ find_library(APPKIT_LIBRARY AppKit REQUIRED)
+ else()
+ find_package(OpenGL REQUIRED)
+ find_package(SpaceWare)
+ pkg_check_modules(FONTCONFIG REQUIRED fontconfig)
+ pkg_check_modules(JSONC REQUIRED json-c)
+ pkg_check_modules(GTKMM REQUIRED gtkmm-3.0>=3.18 pangomm-1.4 x11)
+ endif()
+endif()
- if(NOT FREETYPE_FOUND)
- message(STATUS "Using in-tree libfreetype")
+# code coverage
- add_subdirectory(extlib/libfreetype EXCLUDE_FROM_ALL)
+if(ENABLE_COVERAGE)
+ if(CMAKE_CXX_COMPILER_ID STREQUAL GNU)
+ find_program(GCOV gcov)
+ elseif(CMAKE_CXX_COMPILER_ID MATCHES Clang)
+ find_program(LLVM_COV llvm-cov)
- set(FREETYPE_LIBRARY
- freetype)
- set(FREETYPE_INCLUDE_DIRS
- "${CMAKE_SOURCE_DIR}/extlib/libfreetype/include")
- find_package(Freetype REQUIRED)
+ if(LLVM_COV)
+ set(GCOV ${CMAKE_CURRENT_BINARY_DIR}/llvm-gcov.sh)
+ file(WRITE ${GCOV} "#!/bin/sh -e\n${LLVM_COV} gcov $*")
+ execute_process(COMMAND chmod +x ${GCOV})
+ endif()
endif()
- find_package(ZLIB)
+ find_program(LCOV lcov)
+ find_program(GENHTML genhtml)
+ if(NOT GCOV OR NOT LCOV OR NOT GENHTML)
+ message(FATAL_ERROR "gcov/llvm-cov and lcov are required for producing coverage reports")
+ endif()
+endif()
- if(NOT ZLIB_FOUND)
- message(STATUS "Using in-tree zlib")
- add_subdirectory(extlib/zlib EXCLUDE_FROM_ALL)
+# translations
- message(STATUS "Using in-tree libpng")
- set(ZLIB_LIBRARY
- zlibstatic)
- set(ZLIB_INCLUDE_DIR
- "${CMAKE_SOURCE_DIR}/extlib/zlib"
- "${CMAKE_BINARY_DIR}/extlib/zlib")
- find_package(ZLIB REQUIRED)
- endif()
+find_program(XGETTEXT xgettext)
+find_program(MSGINIT msginit)
+find_program(MSGMERGE msgmerge)
+if(XGETTEXT AND MSGINIT AND MSGMERGE)
+ set(HAVE_GETTEXT TRUE)
+else()
+ message(WARNING "Gettext not found, translations will not be updated")
+ set(HAVE_GETTEXT FALSE)
+endif()
- find_package(PNG)
+# solvespace-only compiler flags
- if(NOT PNG_FOUND)
- message(STATUS "Using in-tree libpng")
+if(WIN32)
+ add_definitions(
+ -D_CRT_SECURE_NO_DEPRECATE
+ -D_CRT_SECURE_NO_WARNINGS
+ -D_SCL_SECURE_NO_WARNINGS
+ -DWINVER=0x0501
+ -D_WIN32_WINNT=0x0501
+ -D_WIN32_IE=_WIN32_WINNT
+ -DISOLATION_AWARE_ENABLED
+ -DWIN32
+ -DWIN32_LEAN_AND_MEAN
+ -DUNICODE
+ -D_UNICODE
+ -DNOMINMAX
+ -D_USE_MATH_DEFINES)
+endif()
- set(SKIP_INSTALL_ALL
- ON)
- add_subdirectory(extlib/libpng EXCLUDE_FROM_ALL)
+if(MSVC)
+ # Many versions of MSVC do not have the (C99) inline keyword, instead
+ # they have their own __inline; this breaks `static inline` functions.
+ # We do not want to care and so we fix this with a definition.
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline")
+ # Same for the (C99) __func__ special variable; we use it only in C++ code.
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D__func__=__FUNCTION__")
- set(PNG_LIBRARY
- png16_static)
- set(PNG_PNG_INCLUDE_DIR
- "${CMAKE_SOURCE_DIR}/extlib/libpng"
- "${CMAKE_BINARY_DIR}/extlib/libpng")
- find_package(PNG REQUIRED)
- endif()
+ # We rely on these /we flags. They correspond to the GNU-style flags below as
+ # follows: /w4062=-Wswitch
+ set(WARNING_FLAGS "${WARNING_FLAGS} /we4062")
+endif()
- if(NOT MINGW)
- message(STATUS "Using prebuilt SpaceWare")
- set(SPACEWARE_FOUND TRUE)
- set(SPACEWARE_INCLUDE_DIR
- "${CMAKE_SOURCE_DIR}/extlib/si")
- set(SPACEWARE_LIBRARIES
- "${CMAKE_SOURCE_DIR}/extlib/si/siapp.lib")
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(WARNING_FLAGS "-Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers")
+ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(WARNING_FLAGS "${WARNING_FLAGS} -Wfloat-conversion")
endif()
-elseif(APPLE)
- set(CMAKE_FIND_FRAMEWORK LAST)
+ # We rely on these -Werror flags.
+ set(WARNING_FLAGS "${WARNING_FLAGS} -Werror=switch")
+endif()
- find_package(PNG REQUIRED)
- find_package(Freetype REQUIRED)
- find_library(APPKIT_LIBRARY AppKit REQUIRED)
-else() # Linux and compatible systems
- find_package(SpaceWare)
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
- # Use freedesktop's pkg-config to locate everything.
- find_package(PkgConfig REQUIRED)
- pkg_check_modules(ZLIB REQUIRED zlib)
- pkg_check_modules(PNG REQUIRED libpng)
- pkg_check_modules(FONTCONFIG REQUIRED fontconfig)
- pkg_check_modules(JSONC REQUIRED json-c)
- pkg_check_modules(GLEW REQUIRED glew)
- pkg_check_modules(FREETYPE REQUIRED freetype2)
-
- set(HAVE_GTK TRUE)
- if(GUI STREQUAL "gtk3")
- set(HAVE_GTK3 TRUE)
- pkg_check_modules(GTKMM REQUIRED gtkmm-3.0 pangomm-1.4 x11)
- elseif(GUI STREQUAL "gtk2")
- set(HAVE_GTK2 TRUE)
- pkg_check_modules(GTKMM REQUIRED gtkmm-2.4 pangomm-1.4 x11)
- else()
- message(FATAL_ERROR "GUI unrecognized: ${GUI}")
+if(WIN32)
+ set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -l0")
+endif()
+
+if(ENABLE_COVERAGE)
+ if(NOT (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR
+ CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
+ message(FATAL_ERROR "Code coverage is only available on GCC and Clang")
+ endif()
+
+ if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
+ message(FATAL_ERROR "Code coverage produces reliable results only on Debug builds")
endif()
+
+ # With -fexceptions, every call becomes a branch. While technically accurate,
+ # this is not useful for us.
+ set(COVERAGE_FLAGS -fno-exceptions --coverage)
+ set(COVERAGE_LIBRARY --coverage)
endif()
-# components
+# application components
-add_subdirectory(tools)
+add_subdirectory(res)
add_subdirectory(src)
add_subdirectory(exposed)
+if(ENABLE_TESTS)
+ add_subdirectory(test)
+endif()
+if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
+ add_subdirectory(bench)
+else()
+ message(STATUS "Benchmarking disabled in debug builds.")
+endif()
--- /dev/null
+Contributing to SolveSpace
+==========================
+
+Contributing bug reports
+------------------------
+
+Bug reports are always welcome! When reporting a bug, please include the following:
+
+ * The version of SolveSpace (use Help → About...);
+ * The operating system;
+ * The save file that reproduces the incorrect behavior, or, if trivial or impossible,
+ instructions for reproducing it.
+
+GitHub does not allow attaching `*.slvs` files, but it does allow attaching `*.zip` files,
+so any savefiles should first be archived.
+
+Signing the CLA
+---------------
+
+To contribute code, translations, artwork, or other resources to SolveSpace, it is necessary to
+sign a [Contributor License Agreement](https://cla-assistant.io/solvespace/solvespace).
+
+Contributing translations
+-------------------------
+
+To contribute a translation, not a lot is necessary—at a minimum, you need to be able
+to edit .po files with a tool such as [poedit](https://poedit.net/). Once you have
+such a tool installed, take `res/messages.pot` and start translating!
+
+However, if you want to see your translation in action, a little more work is necessary.
+First, you need to be able to build SolveSpace; see [README](README.md). After that:
+
+ * Copy `res/messages.pot` to `res/locales/xx_YY.po`, where `xx` is an ISO 639-1
+ country code, and `YY` is an ISO 3166-1 language code.
+ * Add a line `xx-YY,LCID,Name` to `res/locales.txt`, where `xx-YY` have the same
+ meaning as above, `LCID` is a Windows Language Code Identifier ([MS-LCID][]
+ has a complete list), and `Name` is the full name of your locale in your language.
+ * Add `locales/xx_YY.po` in `res/CMakeLists.txt`—search for `locales/en_US.po`
+ to see where it should be added.
+
+You're done! Recompile SolveSpace and you should be able to select your translation
+via Help → Language.
+
+[MS-LCID]: https://msdn.microsoft.com/en-us/library/cc233965.aspx
+
+Contributing code
+-----------------
+
+SolveSpace is written in C++, and currently targets all compilers compliant with C++11.
+This includes GCC 5 and later, Clang 3.3 and later, and Visual Studio 12 (2013) and later.
+
+### High-level conventions
+
+#### Portability
+
+SolveSpace aims to consist of two general parts: a fully portable core, and platform-specific
+UI and support code. Anything outside of `src/platform/` should only use standard C++11,
+and rely on `src/platform/unixutil.cpp` and `src/platform/w32util.cpp` to interact with
+the OS where this cannot be done through the C++11 standard library.
+
+#### Libraries
+
+SolveSpace primarily relies on the C++11 STL. STL has well-known drawbacks, but is also
+widely supported, used, and understood. SolveSpace also includes a fair amount of use of
+bespoke containers List and IdList; these provide STL iterators, and can be used when
+convenient, such as when reusing other code.
+
+One notable departure here is the STL I/O threads. SolveSpace does not use STL I/O threads
+for two reasons: (i) the interface is borderline unusable, and (ii) on Windows it is not
+possible to open files with Unicode paths through STL.
+
+When using external libraries (other than to access platform features), the libraries
+should satisfy the following conditions:
+
+ * Portable, and preferably not interacting with the platform at all;
+ * Can be included as a CMake subproject, to facilitate Windows, Android, etc. builds;
+ * Use a license less restrictive than GPL (BSD/MIT, Apache2, MPL, etc.)
+
+#### String encoding
+
+Internally, SolveSpace exclusively stores and uses UTF-8 for all purposes; any `std::string`
+may be assumed to be encoded in UTF-8. On Windows, UTF-8 strings are converted to and from
+wide strings at the boundary; see [UTF-8 Everywhere][utf8] for details.
+
+[utf8]: http://utf8everywhere.org/
+
+#### String formatting
+
+For string formatting, a wrapper around `sprintf`, `ssprintf`, is used. A notable
+pitfall when using it is trying to pass an `std::string` argument without first converting
+it to a C string with `.c_str()`.
+
+#### Filesystem access
+
+For filesystem access, the C standard library is used. The `ssfopen` and `ssremove`
+wrappers are provided that accept UTF-8 encoded paths.
+
+#### Assertions
+
+To ensure that internal invariants hold, the `ssassert` function is used, e.g.
+`ssassert(!isFoo, "Unexpected foo condition");`. Unlike the standard `assert` function,
+the `ssassert` function is always enabled, even in release builds. It is more valuable
+to discover a bug through a crash than to silently generate incorrect results, and crashes
+do not result in losing more than a few minutes of work thanks to the autosave feature.
+
+### Use of C++ features
+
+The conventions described in this section should be used for all new code, but there is a lot
+of existing code in SolveSpace that does not use them. This is fine; don't touch it if it works,
+but if you need to modify it anyway, might as well modernize it.
+
+#### Exceptions
+
+Exceptions are not used primarily because SolveSpace's testsuite uses measurement
+of branch coverage, important for the critical parts such as the geometric kernel.
+Every function call with exceptions enabled introduces a branch, making branch coverage
+measurement useless.
+
+#### Operator overloading
+
+Operator overloading is not used primarily for historical reasons. Instead, method such
+as `Plus` are used.
+
+#### Member visibility
+
+Member visibility is not used for implementation hiding. Every member field and function
+is `public`.
+
+#### Constructors
+
+Constructors are not used for initialization, chiefly because indicating an error
+in a constructor would require throwing an exception, nor does it use constructors for
+blanket zero-initialization because of the performance impact of doing this for common
+POD classes like `Vector`.
+
+Instances can be zero-initialized using the aggregate-initialization syntax, e.g. `Foo foo = {};`.
+This zero-initializes the POD members and default-initializes the non-POD members, generally
+being an equivalent of `memset(&foo, 0, sizeof(foo));` but compatible with STL containers.
+
+#### Input- and output-arguments
+
+Functions accepting an input argument take it either by-value (`Vector v`) or
+by-const-reference (`const Vector &v`). Generally, passing by-value is safer as the value
+cannot be aliased by something else, but passing by-const-reference is faster, as a copy is
+eliminated. Small values should always be passed by-value, and otherwise functions that do not
+capture pointers into their arguments should take them by-const-reference. Use your judgement.
+
+Functions accepting an output argument always take it by-pointer (`Vector *v`). This makes
+it immediately visible at the call site as it is seen that the address is taken. Arguments
+are never passed by-reference, except when needed for interoperability with STL, etc.
+
+#### Iteration
+
+`foreach`-style iteration is preferred for both STL and `List`/`IdList` containers as it indicates
+intent clearly, as opposed to `for`-style.
+
+#### Const correctness
+
+Functions that do not mutate `this` should be marked as `const`; when iterating a collection
+without mutating any of its elements, `for(const Foo &elem : collection)` is preferred to indicate
+the intent.
+
+### Coding style
+
+Code is formatted by the following rules:
+
+ * Code is indented using 4 spaces, with no trailing spaces, and lines are wrapped
+ at 100 columns;
+ * Braces are placed at the end of the line with the declaration or control flow statement;
+ * Braces are used with every control flow statement, even if there is only one statement
+ in the body;
+ * There is no space after control flow keywords (`if`, `while`, etc.);
+ * Identifiers are formatted in camel case; variables start with a lowercase letter
+ (`exampleVariable`) and functions start with an uppercase letter (`ExampleFunction`).
+
+For example:
+
+```c++
+std::string SolveSpace::Dirname(std::string filename) {
+ int slash = filename.rfind(PATH_SEP);
+ if(slash >= 0) {
+ return filename.substr(0, slash);
+ }
+
+ return "";
+}
+```
+
+If you install [clang-format][], this style can be automatically applied by staging your changes
+with `git add -u`, running `git clang-format`, and staging any changes it made again.
+
+[clang-format]: https://clang.llvm.org/docs/ClangFormat.html
+
+Debugging code
+--------------
+
+SolveSpace releases are thoroughly tested but sometimes they contain crash
+bugs anyway. The reason for such crashes can be determined only if the executable
+was built with debug information.
+
+### Debugging a released version
+
+The Linux distributions usually include separate debug information packages.
+On a Debian derivative (e.g. Ubuntu), these can be installed with:
+
+ apt-get install solvespace-dbg
+
+The macOS releases include the debug information, and no further action
+is needed.
+
+The Windows releases include the debug information on the GitHub
+[release downloads page](https://github.com/solvespace/solvespace/releases).
+
+### Debugging a custom build
+
+If you are building SolveSpace yourself on macOS, use the XCode
+CMake generator, then open the project in XCode as usual, select
+the Debug build scheme, and build the project:
+
+ cd build
+ cmake .. -G Xcode [other cmake args...]
+
+If you are building SolveSpace yourself on any Unix-like platform,
+configure or re-configure SolveSpace to produce a debug build, and
+then build it:
+
+ cd build
+ cmake .. -DCMAKE_BUILD_TYPE=Debug [other cmake args...]
+ make
+
+If you are building SolveSpace yourself using the Visual Studio IDE,
+select Debug from the Solution Configurations list box on the toolbar,
+and build the solution.
+
+### Debugging with gdb
+
+gdb is a debugger that is mostly used on Linux. First, run SolveSpace
+under debugging:
+
+ gdb [path to solvespace executable]
+ (gdb) run
+
+Then, reproduce the crash. After the crash, attach the output in
+the console, as well as output of the following gdb commands to
+a bug report:
+
+ (gdb) backtrace
+ (gdb) info locals
+
+If the crash is not easy to reproduce, please generate a core file,
+which you can use to resume the debugging session later, and provide
+any other information that is requested:
+
+ (gdb) generate-core-file
+
+This will generate a large file called like `core.1234` in the current
+directory; it can be later re-loaded using `gdb --core core.1234`.
+
+### Debugging with lldb
+
+lldb is a debugger that is mostly used on macOS. First, run SolveSpace
+under debugging:
+
+ lldb [path to solvespace executable]
+ (lldb) run
+
+Then, reproduce the crash. After the crash, attach the output in
+the console, as well as output of the following gdb commands to
+a bug report:
+
+ (lldb) backtrace all
+ (lldb) frame variable
+
+If the crash is not easy to reproduce, please generate a core file,
+which you can use to resume the debugging session later, and provide
+any other information that is requested:
+
+ (lldb) process save-core "core"
+
+This will generate a large file called `core` in the current
+directory; it can be later re-loaded using `lldb -c core`.
+
+### Debugging GUI-related bugs on Linux
+
+There are several environment variables available that make crashes
+earlier and errors more informative. Before running SolveSpace, run
+the following commands in your shell:
+
+ export G_DEBUG=fatal_warnings
+ export LIBGL_DEBUG=1
+ export MESA_DEBUG=1
+<img src="https://avatars1.githubusercontent.com/u/18541596?s=70&v=4" width="70" height="70" alt="SolveSpace Logo" align="left">
+
SolveSpace
==========
+[](https://travis-ci.com/solvespace/solvespace)
+[](https://snapcraft.io/solvespace)
+[](https://snapcraft.io/solvespace)
This repository contains the source code of [SolveSpace][], a parametric
2d/3d CAD.
[solvespace]: http://solvespace.com
+Community
+---------
+
+The official SolveSpace [website][sswebsite] has [tutorials][sstutorial],
+[reference manual][ssref] and a [forum][ssforum]; there is also an official
+IRC channel [#solvespace at irc.freenode.net][ssirc].
+
+[sswebsite]: http://solvespace.com/
+[ssref]: http://solvespace.com/ref.pl
+[sstutorial]: http://solvespace.com/tutorial.pl
+[ssforum]: http://solvespace.com/forum.pl
+[ssirc]: https://webchat.freenode.net/?channels=solvespace
+
Installation
------------
-### Mac OS X (>=10.6 64-bit), Debian (>=jessie) and Ubuntu (>=trusty)
+### Via official binary packages
-Binary packages for Mac OS X and Debian derivatives are available
-via [GitHub releases][rel].
+_Official_ release binary packages for macOS (>=10.6 64-bit) and Windows (>=Vista 32-bit) are
+available via [GitHub releases][rel]. These packages are automatically built by
+the SolveSpace maintainers for each stable release.
[rel]: https://github.com/solvespace/solvespace/releases
-### Other systems
+### Via Snap Store
+
+Builds from master are automatically released to the `edge` channel in the Snap Store. Those packages contain the latest improvements, but receive less testing than release builds.
+
+Future official releases will appear in the `stable` channel.
+
+[](https://snapcraft.io/solvespace)
+
+Or install from a terminal:
+
+```
+snap install --edge solvespace
+```
+
+### Via third-party binary packages
+
+_Third-party_ nightly binary packages for Debian and Ubuntu are available
+via [notesalexp.org][notesalexp]. These packages are automatically built from non-released
+source code. The SolveSpace maintainers do not control the contents of these packages
+and cannot guarantee their functionality.
+
+[notesalexp]: https://notesalexp.org/packages/en/source/solvespace/
+
+### Via source code
See below.
### Building for Linux
-You will need CMake, libpng, zlib, json-c, fontconfig, freetype, gtkmm 2.4,
-pangomm 1.4, OpenGL, OpenGL GLU and OpenGL GLEW, and optionally, the Space Navigator
-client library.
+You will need the usual build tools, CMake, zlib, libpng, cairo, freetype.
+To build the GUI, you will need fontconfig, gtkmm 3.0 (version 3.16 or later), pangomm 1.4,
+OpenGL and OpenGL GLU, and optionally, the Space Navigator client library.
On a Debian derivative (e.g. Ubuntu) these can be installed with:
- apt-get install libpng12-dev libjson-c-dev libfreetype6-dev \
- libfontconfig1-dev libgtkmm-2.4-dev libpangomm-1.4-dev \
- libgl-dev libglu-dev libglew-dev libspnav-dev cmake
+ sudo apt install git build-essential cmake zlib1g-dev libpng-dev \
+ libcairo2-dev libfreetype6-dev libjson-c-dev \
+ libfontconfig1-dev libgtkmm-3.0-dev libpangomm-1.4-dev \
+ libgl-dev libglu-dev libspnav-dev
+
+On a Redhat derivative (e.g. Fedora) the dependencies can be installed with:
-Before building, check out the necessary submodules:
+ sudo dnf install git gcc-c++ cmake zlib-devel libpng-devel \
+ cairo-devel freetype-devel json-c-devel \
+ fontconfig-devel gtkmm30-devel pangomm-devel \
+ mesa-libGL-devel mesa-libGLU-devel libspnav-devel
- git submodule update --init extlib/libdxfrw
+Before building, check out the project and the necessary submodules:
+
+ git clone https://github.com/solvespace/solvespace
+ cd solvespace
+ git submodule update --init extlib/libdxfrw extlib/flatbuffers extlib/q3d extlib/mimalloc
After that, build SolveSpace as following:
mkdir build
cd build
- cmake ..
+ cmake .. -DCMAKE_BUILD_TYPE=Release
make
sudo make install
-A fully functional port to GTK3 is available, but not recommended
-for use due to bugs in this toolkit.
+The graphical interface is built as `build/bin/solvespace`, and the command-line interface
+is built as `build/bin/solvespace-cli`. It is possible to build only the command-line interface
+by passing the `-DENABLE_GUI=OFF` flag to the cmake invocation.
### Building for Windows
-You will need CMake, a Windows cross-compiler, and Wine with binfmt support.
-On a Debian derivative (e.g. Ubuntu) these can be installed with:
+Ubuntu will require 20.04 or above. Cross-compiling with WSL is also confirmed to work.
- apt-get install cmake mingw-w64 wine-binfmt
+You will need the usual build tools, CMake, a Windows cross-compiler, and flatc. On a Debian derivative (e.g. Ubuntu) these can be installed with:
-Before building, check out the necessary submodules:
+ apt-get install git build-essential cmake mingw-w64 libflatbuffers-dev
+Before building, check out the project and the necessary submodules:
+
+ git clone https://github.com/solvespace/solvespace
+ cd solvespace
git submodule update --init
-After that, build 32-bit SolveSpace as following:
+Build 64-bit SolveSpace with the following:
mkdir build
cd build
- cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw32.cmake ..
- make solvespace
+ cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw64.cmake \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DFLATC=$(which flatc)
+ make
+
+The graphical interface is built as `build/bin/solvespace.exe`, and the command-line interface
+is built as `build/bin/solvespace-cli.exe`.
+
+Space Navigator support will not be available.
-Or, build 64-bit SolveSpace as following:
+If using Ubuntu to cross-compile, Ubuntu 17.10 or newer (or, alternatively, MinGW from the Ubuntu
+17.10 repositories) is required.
+
+Building on macOS
+-----------------
+
+You will need git, XCode tools and CMake. Git and CMake can be installed
+via [Homebrew][]:
+
+ brew install git cmake
+
+XCode has to be installed via AppStore or [the Apple website][appledeveloper];
+it requires a free Apple ID.
+
+Before building, check out the project and the necessary submodules:
+
+ git clone https://github.com/solvespace/solvespace
+ cd solvespace
+ git submodule update --init
+
+After that, build SolveSpace as following:
mkdir build
cd build
- cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw64.cmake ..
- make solvespace
+ cmake .. -DCMAKE_BUILD_TYPE=Release
+ make
-The application is built as `build/src/solvespace.exe`.
+Alternatively, generate an XCode project, open it, and build the "Release" scheme:
-Space Navigator support will not be available.
+ mkdir build
+ cd build
+ cmake .. -G Xcode
+
+The application is built in `build/bin/SolveSpace.app`, the graphical interface executable
+is `build/bin/SolveSpace.app/Contents/MacOS/SolveSpace`, and the command-line interface executable
+is `build/bin/SolveSpace.app/Contents/MacOS/solvespace-cli`.
-Building on Mac OS X
---------------------
+[homebrew]: https://brew.sh/
+[appledeveloper]: https://developer.apple.com/download/
-You will need XCode tools, CMake, libpng and Freetype. Assuming you use
-[homebrew][], these can be installed with:
+Building on OpenBSD
+-------------------
- brew install cmake libpng freetype
+You will need git, cmake, libexecinfo, libpng, gtk3mm and pangomm.
+These can be installed from the ports tree:
-XCode has to be installed via AppStore; it requires a free Apple ID.
+ pkg_add -U git cmake libexecinfo png json-c gtk3mm pangomm
-Before building, check out the necessary submodules:
+Before building, check out the project and the necessary submodules:
- git submodule update --init extlib/libdxfrw
+ git clone https://github.com/solvespace/solvespace
+ cd solvespace
+ git submodule update --init extlib/libdxfrw extlib/flatbuffers extlib/q3d extlib/mimalloc
After that, build SolveSpace as following:
mkdir build
cd build
- cmake ..
+ cmake .. -DCMAKE_BUILD_TYPE=Release
make
-The app bundle is built in `build/src/solvespace.app`.
-
-[homebrew]: http://brew.sh/
+Unfortunately, on OpenBSD, the produced executables are not filesystem location independent
+and must be installed before use. By default, the graphical interface is installed to
+`/usr/local/bin/solvespace`, and the command-line interface is built as
+`/usr/local/bin/solvespace-cli`. It is possible to build only the command-line interface
+by passing the `-DENABLE_GUI=OFF` flag to the cmake invocation.
Building on Windows
-------------------
-You will need [cmake][cmakewin] and Visual C++.
+You will need [git][gitwin], [cmake][cmakewin] and a C++ compiler
+(either Visual C++ or MinGW). If using Visual C++, Visual Studio 2015
+or later is required.
-### GUI build
+### Building with Visual Studio IDE
Check out the git submodules. Create a directory `build` in
the source tree and point cmake-gui to the source tree and that directory.
Press "Configure" and "Generate", then open `build\solvespace.sln` with
Visual C++ and build it.
-### Command-line build
+### Building with Visual Studio in a command prompt
First, ensure that git and cl (the Visual C++ compiler driver) are in your
`%PATH%`; the latter is usually done by invoking `vcvarsall.bat` from your
Visual Studio install. Then, run the following in cmd or PowerShell:
+ git clone https://github.com/solvespace/solvespace
+ cd solvespace
git submodule update --init
mkdir build
cd build
- cmake .. -G "NMake Makefiles"
+ cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release
nmake
-### MSVC build
+### Building with MinGW
It is also possible to build SolveSpace using [MinGW][mingw], though
Space Navigator support will be disabled.
First, ensure that git and gcc are in your `$PATH`. Then, run the following
in bash:
+ git clone https://github.com/solvespace/solvespace
+ cd solvespace
git submodule update --init
mkdir build
cd build
- cmake ..
+ cmake .. -DCMAKE_BUILD_TYPE=Release
make
+[gitwin]: https://git-scm.com/download/win
[cmakewin]: http://www.cmake.org/download/#latest
[mingw]: http://www.mingw.org/
+Contributing
+------------
+
+See the [guide for contributors](CONTRIBUTING.md) for the best way to file issues, contribute code,
+and debug SolveSpace.
+
License
-------
-SolveSpace is distributed under the terms of the [GPL3 license](COPYING.txt).
+SolveSpace is distributed under the terms of the [GPL v3 license](COPYING.txt). It is possible
+to license SolveSpace for use in a commercial application; to do so,
+[contact](http://solvespace.com/contact.pl) the developers.
--- /dev/null
+This file outlines third party licenses that apply to the files and resources
+listed with them. All file paths are relative to the SolveSpace project root.
+
+When redistributing SolveSpace, make sure to also reproduce this file such that
+the paths indicating the scope of each license remain intact.
+================================================================================
+
+ Bitstream Vera, ./res/fonts/BitstreamVeraSans-Roman-builtin.ttf
+ ~
+Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a
+trademark of Bitstream, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+the fonts accompanying this license (“Fonts”) and associated documentation files
+(the “Font Software”), to reproduce and distribute the Font Software, including
+without limitation the rights to use, copy, merge, publish, distribute, and/or
+sell copies of the Font Software, and to permit persons to whom the Font
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright and trademark notices and this permission notice shall be
+included in all copies of one or more of the Font Software typefaces.
+
+The Font Software may be modified, altered, or added to, and in particular the
+designs of glyphs or characters in the Fonts may be modified and additional
+glyphs or characters may be added to the Fonts, only if the fonts are renamed to
+names not containing either the words “Bitstream” or the word “Vera”.
+
+This License becomes null and void to the extent applicable to Fonts or Font
+Software that has been modified and is distributed under the “Bitstream Vera”
+names.
+
+The Font Software may be sold as part of a larger software package but no copy
+of one or more of the Font Software typefaces may be sold by itself.
+
+THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR
+OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT,
+INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR
+FROM OTHER DEALINGS IN THE FONT SOFTWARE.
+
+Except as contained in this notice, the names of GNOME, the GNOME Foundation,
+and Bitstream Inc., shall not be used in advertising or otherwise to promote the
+sale, use or other dealings in this Font Software without prior written
+authorization from the GNOME Foundation or Bitstream Inc., respectively. For
+further information, contact: fonts at gnome dot org.
+
+ -----------
+++ /dev/null
-version: 2.2.{build}
-clone_depth: 1
-before_build:
- - git submodule update --init
- - mkdir build
- - cd build
- - set tag=x%APPVEYOR_REPO_TAG_NAME%
- - if %tag:~,2% == xv (set BUILD_TYPE=RelWithDebInfo) else (set BUILD_TYPE=Debug)
- - cmake -G"Visual Studio 12" -T v120_xp -DCMAKE_BUILD_TYPE=%BUILD_TYPE% ..
-build_script:
- - msbuild "src\solvespace.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
-artifacts:
- - path: build\src\Debug\solvespace.exe
- name: solvespace.exe
- - path: build\src\Debug\solvespace.pdb
- name: solvespace.pdb
- - path: build\src\RelWithDebInfo\solvespace.exe
- name: solvespace.exe
- - path: build\src\RelWithDebInfo\solvespace.pdb
- name: solvespace.pdb
-deploy:
- - provider: GitHub
- auth_token:
- secure: P9/pf2nM+jlWKe7pCjMp41HycBNP/+5AsmE/TETrDUoBOa/9WFHelqdVFrbRn9IC
- description: ""
- artifact: solvespace.exe
- on:
- appveyor_repo_tag: true
--- /dev/null
+# benchmark runner
+
+foreach(pkg_config_lib CAIRO)
+ include_directories(${${pkg_config_lib}_INCLUDE_DIRS})
+ link_directories(${${pkg_config_lib}_LIBRARY_DIRS})
+endforeach()
+
+add_executable(solvespace-benchmark
+ harness.cpp
+ $<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
+
+target_link_libraries(solvespace-benchmark
+ solvespace-core
+ solvespace-headless)
+
+add_dependencies(solvespace-benchmark
+ resources)
--- /dev/null
+//-----------------------------------------------------------------------------
+// Our harness for running benchmarks.
+//
+// Copyright 2016 whitequark
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+
+static bool RunBenchmark(std::function<void()> setupFn,
+ std::function<bool()> benchFn,
+ std::function<void()> teardownFn,
+ size_t minIter = 5, double minTime = 5.0) {
+ // Warmup
+ setupFn();
+ if(!benchFn()) {
+ fprintf(stderr, "Benchmark failed\n");
+ return false;
+ }
+ teardownFn();
+
+ // Benchmark
+ size_t iter = 0;
+ double time = 0.0;
+ while(iter < minIter || time < minTime) {
+ setupFn();
+ auto testStartTime = std::chrono::steady_clock::now();
+ benchFn();
+ auto testEndTime = std::chrono::steady_clock::now();
+ teardownFn();
+
+ std::chrono::duration<double> testTime = testEndTime - testStartTime;
+ time += testTime.count();
+ iter += 1;
+ }
+
+ // Report
+ fprintf(stdout, "Iterations: %zd\n", iter);
+ fprintf(stdout, "Time: %.3f s\n", time);
+ fprintf(stdout, "Per iter.: %.3f s\n", time / (double)iter);
+
+ return true;
+}
+
+int main(int argc, char **argv) {
+ std::vector<std::string> args = Platform::InitCli(argc, argv);
+
+ std::string mode;
+ Platform::Path filename;
+ if(args.size() == 3) {
+ mode = args[1];
+ filename = Platform::Path::From(args[2]);
+ } else {
+ fprintf(stderr, "Usage: %s [mode] [filename]\n", args[0].c_str());
+ fprintf(stderr, "Mode can be one of: load.\n");
+ return 1;
+ }
+
+ bool result = false;
+ if(mode == "load") {
+ result = RunBenchmark(
+ [] {
+ SS.Init();
+ },
+ [&] {
+ if(!SS.LoadFromFile(filename))
+ return false;
+ SS.AfterNewFile();
+ return true;
+ },
+ [] {
+ SK.Clear();
+ SS.Clear();
+ });
+ } else {
+ fprintf(stderr, "Unknown mode \"%s\"\n", mode.c_str());
+ }
+
+ return (result == true ? 0 : 1);
+}
--- /dev/null
+# Equivalent to add_subdirectory(... EXCLUDE_FROM_ALL), but also disables
+# all warnings.
+
+include(DisableWarnings)
+
+function(add_vendored_subdirectory PATH)
+ disable_warnings()
+
+ add_subdirectory(${PATH} EXCLUDE_FROM_ALL)
+endfunction()
--- /dev/null
+# Disables all warnings on MSVC and GNU-compatible compilers.
+
+function(disable_warnings)
+ if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w" PARENT_SCOPE)
+ elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W0" PARENT_SCOPE)
+ endif()
+
+ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w" PARENT_SCOPE)
+ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0" PARENT_SCOPE)
+ endif()
+endfunction()
#
# SPACEWARE_INCLUDE_DIR - header location
# SPACEWARE_LIBRARIES - library to link against
-# SPACEWARE_FOUND - true if pugixml was found.
+# SPACEWARE_FOUND - true if libspnav was found.
if(UNIX)
--- /dev/null
+# Find the given library in the system locations, or build in-tree if not found.
+#
+# Arguments:
+# PKG_NAME - name of the package as passed to find_package
+# PKG_PATH - name of the source tree relative to extlib/
+#
+# The rest of the arguments are VARIABLE VALUE pairs. If the library is not found,
+# every VARIABLE will be set to VALUE and find_package will be rerun with the REQUIRED flag.
+# Regardless of where the library was found, only the specified VARIABLEs that start with
+# ${PKG_NAME} will be set in the parent scope.
+#
+# All warnings in the in-tree package are disabled.
+
+include(DisableWarnings)
+
+function(find_vendored_package PKG_NAME PKG_PATH)
+ if(NOT FORCE_VENDORED_${PKG_NAME})
+ find_package(${PKG_NAME})
+ endif()
+
+ set(cfg_name)
+ foreach(item ${ARGN})
+ if(NOT cfg_name)
+ set(cfg_name ${item})
+ else()
+ set(${cfg_name} ${item} CACHE INTERNAL "")
+ set(cfg_name)
+ endif()
+ endforeach()
+
+ disable_warnings()
+
+ string(TOUPPER ${PKG_NAME} VAR_NAME)
+ if(NOT ${VAR_NAME}_FOUND)
+ message(STATUS "Using in-tree ${PKG_PATH}")
+ set(${VAR_NAME}_IN_TREE YES CACHE INTERNAL "")
+
+ add_subdirectory(extlib/${PKG_PATH} EXCLUDE_FROM_ALL)
+ find_package(${PKG_NAME} REQUIRED)
+ elseif(${VAR_NAME}_IN_TREE)
+ add_subdirectory(extlib/${PKG_PATH} EXCLUDE_FROM_ALL)
+ endif()
+
+ # Now put everything we just discovered into the cache.
+ set(cfg_name)
+ foreach(item ${ARGN} ${VAR_NAME}_FOUND)
+ if(NOT cfg_name)
+ set(cfg_name ${item})
+ else()
+ if(cfg_name MATCHES "^${VAR_NAME}")
+ set(${cfg_name} "${${cfg_name}}" CACHE INTERNAL "")
+ endif()
+ set(cfg_name)
+ endif()
+ endforeach()
+endfunction()
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
+ <key>CFBundleIdentifier</key>
+ <string>com.solvespace</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
- <string>solvespace</string>
+ <string>SolveSpace</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
- <string>${solvespace_VERSION_MAJOR}.${solvespace_VERSION_MINOR}</string>
+ <string>${solvespace_VERSION_MAJOR}.${solvespace_VERSION_MINOR}~${solvespace_GIT_HASH}</string>
<key>CFBundleShortVersionString</key>
<string>${solvespace_VERSION_MAJOR}.${solvespace_VERSION_MINOR}</string>
<key>NSHumanReadableCopyright</key>
- <string>© 2008-2015 Jonathan Westhues and other authors</string>
+ <string>© 2008-2016 Jonathan Westhues and other authors</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSMainNibFile</key>
-SET(CMAKE_SYSTEM_NAME Windows)
+set(CMAKE_SYSTEM_NAME Windows)
-SET(TRIPLE i686-w64-mingw32)
+set(TRIPLE i686-w64-mingw32)
-SET(CMAKE_C_COMPILER ${TRIPLE}-gcc)
-SET(CMAKE_CXX_COMPILER ${TRIPLE}-g++)
-SET(CMAKE_RC_COMPILER ${TRIPLE}-windres)
+set(CMAKE_C_COMPILER ${TRIPLE}-gcc)
+set(CMAKE_CXX_COMPILER ${TRIPLE}-g++)
+set(CMAKE_RC_COMPILER ${TRIPLE}-windres)
-SET(CMAKE_FIND_ROOT_PATH /usr/${TRIPLE})
+set(CMAKE_FIND_ROOT_PATH /usr/${TRIPLE})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+set(ENV{PKG_CONFIG_LIBDIR} /usr/${TRIPLE}/lib/pkgconfig)
-SET(CMAKE_SYSTEM_NAME Windows)
+set(CMAKE_SYSTEM_NAME Windows)
-SET(TRIPLE x86_64-w64-mingw32)
+set(TRIPLE x86_64-w64-mingw32)
-SET(CMAKE_C_COMPILER ${TRIPLE}-gcc)
-SET(CMAKE_CXX_COMPILER ${TRIPLE}-g++)
-SET(CMAKE_RC_COMPILER ${TRIPLE}-windres)
+set(CMAKE_C_COMPILER ${TRIPLE}-gcc)
+set(CMAKE_CXX_COMPILER ${TRIPLE}-g++)
+set(CMAKE_RC_COMPILER ${TRIPLE}-windres)
-SET(CMAKE_FIND_ROOT_PATH /usr/${TRIPLE})
+set(CMAKE_FIND_ROOT_PATH /usr/${TRIPLE})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+set(ENV{PKG_CONFIG_LIBDIR} /usr/${TRIPLE}/lib/pkgconfig)
--- /dev/null
+
+CREATING NEW GROUPS in 3D
+=================================
+
+New Transformed Entity Types:
+-----------------------------
+New construction tools may require new transformations of entities - Points,
+Normals, and Faces. A number of "Types" exist to transform an entity to a new
+location. The term "Type" refers to the type of transformation an entity was
+created from, and should not be confused with the 3 kinds of Entity (there are
+other entities but they rely on others of the 3 base types for position and
+orientation information, so they don't directly transform). A list of
+Types is at the top of the EntityBase class definition:
+
+ enum class Type : uint32_t {
+ POINT_IN_3D = 2000,
+ POINT_IN_2D = 2001,
+ POINT_N_TRANS = 2010,
+ POINT_N_ROT_TRANS = 2011,
+ POINT_N_COPY = 2012,
+ POINT_N_ROT_AA = 2013,
+ POINT_N_ROT_AXIS_TRANS = 2014,
+
+ NORMAL_IN_3D = 3000,
+ NORMAL_IN_2D = 3001,
+ NORMAL_N_COPY = 3010,
+ NORMAL_N_ROT = 3011,
+ NORMAL_N_ROT_AA = 3012,
+
+ FACE_NORMAL_PT = 5000,
+ FACE_XPROD = 5001,
+ FACE_N_ROT_TRANS = 5002,
+ FACE_N_TRANS = 5003,
+ FACE_N_ROT_AA = 5004,
+
+Some of the point definitions with _N_ in the name are points defined by
+N application of a transformation. The number of times a particular entity
+is transformed is given in the member variable "timesApplied". The following
+is a dectription of the various transformation Types.
+
+POINT_N_TRANS: Translates a point by a vector defined by param[0],param[1],param[2]
+ the vector is multiplied by timesApplied.
+
+POINT_N_ROT_TRANS: Rotates a point via quaternion param[3],param[4],param[5],param[6]
+ and then translates it by vector param[0],param[1],param[2]
+
+POINT_N_COPY: A non-transformed copy of a point - numeric copy?
+
+POINT_N_ROT_AA: A point rotated arount point param[0],param[1],param[2] Where the
+ angle is given by param[3]*timesApplied (times 2?) and the axis
+ of rotation defined by param[4],param[5],param[6]
+
+POINT_N_ROT_AXIS_TRANS: Same as POINT_N_ROT_AA but after rotation, the point is
+ translated along the rotation axis by distance param[7].
+
+NORMAL_N_COPY A non-transformed copy of a normal - numeric copy?
+
+NORMAL_N_ROT: A normal rotated by a quaternion defeined by param[0],param[1],param[2],param[3]
+
+NORMAL_N_ROT_AA A normal rotated by timesApplied*param[0] around the axis
+ specified by param[1],param[2],parma[3]
+
+FACE_N_ROT_TRANS A face rotated then translated. Rotation is defined by
+ quaternion param[3],param[4],param[5],param[6]. The translation
+ is defined by param[0],param[1],param[2].
+
+FACE_N_TRANS: Translates a face by a vector defined by param[0],param[1],param[2]
+ faces are defined by a point and a normal, so this just moves the point.
+
+FACE_N_ROT_AA Face rotated about point param[0],param[1],param[2]. The axis
+ is param[4],param[5],param[6]. Angle is timesApplied*param[3].
+
+
+All entities are copied by the function Group::CopyEntity() which has a CopyAs
+parameter to indicate which kind of copy is to be made. The mapping from CopyAs to
+Entity Types can be found in that function. Most point types get copied the same
+way depending on CopyAs. Several of the normals get copied to the same new
+entity Type because they are unaffected by translation - you don't care if CopyAs
+specified a translation with rotation or just a rotation, a normal is affected the
+same in either case. The mapping from entity type to new entity type has to be
+decoded from the cases and if-else logic in that function.
+
+It is important that a transformation be properly applied to all three of the
+fundamental entities - points, normals, and surfaces.
+
+FUNCTIONS THAT MAY NEED TO BE EXTENDED when new entity types are defined:
+
+These functions have default cases, so they only need to be extended to return True:
+EntityBase::IsPoint()
+EntityBase::IsNormal()
+EntityBAse::IsFace()
+
+For new normal transforms the following should be filled in:
+Quaternion EntityBase::NormalGetNum()
+void EntityBase::NormalForceTo(Quaternion q)
+ExprQuaternion EntityBase::NormalGetExprs()
+
+For points:
+Vector EntityBase::PointGetNum()
+void EntityBase::PointForceTo(Vector p)
+ExprVector EntityBase::PointGetExprs()
+
+For Faces:
+ExprVector EntityBase::FaceGetNormalExprs()
+Vector EntityBase::FaceGetNormalNum()
+ExprVector EntityBase::FaceGetPointExprs()
+Vector EntityBase::FaceGetPointNum()
+
+The basic model for these transformed entities is that the group containing them
+will have parameters that define the transformed entity in terms of the one it
+was copied from. For example, in PointGetNum() under the case POINT_N_TRANS we see:
+
+ Vector trans = Vector::From(param[0], param[1], param[2]);
+ p = numPoint.Plus(trans.ScaledBy(timesApplied));
+ break;
+
+This take the original "numPoint" and adds a vector created from 3 parameters and
+multiplied by "timesApplied". This is used in the step-translating groups. It's also
+used in extrude groups where there are constraints on the 3 parameters to keep the
+vector perpendicular to the sketch it was extruded from. This function returns a
+numerical version of the copied point, hence "GetNum" in the name.
+
+Another function PointGetExprs() returns the same coordinates of the point but
+returns an expression vector. When the user applies a constraint to a point, this
+function is called to get algebraic expressions for the point that are suitable for
+use in the solver. Most points will not be constrained directly, so expressions
+are not needed for them. It is also notable that the expressions are not part of
+the entity itself.
+
+The ForceTo() functions are shortcuts for using the solver. They are passed the
+desired location of a point (or orientation of a normal...) and have the opportunity
+to back-calculate what the group parameters should be to place it there. This is
+used for mouse dragging of copied entites. It is notable that the constraints will
+still be applied afterward, but this is a good shortcut.
+
+When creating a new entity transformation, the first thing to do is define the
+numerical copy (xxxGetNum). That should allow display. Dragging will not work
+until ForceTo is implemented, and user constraints will not work until GetExprs()
+is completed. Most of these functions have an assert that will fire if the required
+new case is missing. This is may not be a complete list of everything you need to
+make new entity transformations.
+
+One thing of note, parameters in both entities and groups are stored by their handle.
+An entity can have up to 8 parameters to define how it is transformed from another
+entity. By convention this array of parameter handles matches that of the group but
+this is probably not strictly necessary.
+
+ADDING GROUP CONSTRAINTS:
+The example above where a point is copied via POINT_N_TRANS for both EXTRUDE and
+STEP TRANSLATING groups is a good example here. For the EXTRUDE case, two constraint
+equations are added to keep the offset vector perpendicular to the sketch. These
+equations are created in Group::GenerateEquations() and are nothing more than an
+expression which is implicitly set equal to zero in the solver.
+
*
* Copyright 2008-2013 Jonathan Westhues.
*---------------------------------------------------------------------------*/
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
#ifdef WIN32
# include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#ifdef HAVE_STDINT_H
-# include <stdint.h>
-#endif
+#include <stdint.h>
#include <slvs.h>
* An example of a constraint in 3d. We create a single group, with some
* entities and constraints.
*---------------------------------------------------------------------------*/
-void Example3d(void)
+void Example3d()
{
/* This will contain a single group, which will arbitrarily number 1. */
Slvs_hGroup g = 1;
* along the reference frame's xy plane. In a second group, we create some
* entities in that group and dimension them.
*---------------------------------------------------------------------------*/
-void Example2d(void)
+void Example2d()
{
Slvs_hGroup g;
double qw, qx, qy, qz;
}
}
-int main(void)
+int main()
{
sys.param = CheckMalloc(50*sizeof(sys.param[0]));
sys.entity = CheckMalloc(50*sizeof(sys.entity[0]));
Many constraints can apply either in 3d, or in a workplane. This is
determined by the wrkpl member of the constraint. If that member is set
to SLVS_FREE_IN_3D, then the constraint applies in 3d. If that member
-is set equal to a workplane, the the constraint applies projected into
-that workplane. (For example, a constraint on the distance between two
-points actually applies to the projected distance).
+is set equal to a workplane, the constraint applies projected into that
+workplane. (For example, a constraint on the distance between two points
+actually applies to the projected distance).
Constraints that may be used in 3d or projected into a workplane are
marked with a single star (*). Constraints that must always be used with
================
The solver is provided as a DLL, and will be usable with most
-Windows-based developement tools. Examples are provided:
+Windows-based development tools. Examples are provided:
in C/C++ - CDemo.c
End Function
' After a failing call to Solve(), this returns the list of
- ' constraints, identified by ther handle, that would fix the
+ ' constraints, identified by their handle, that would fix the
' system if they were deleted. This list will be populated only
' if calculateFaileds is True in the Solve() call.
Public Function GetFaileds() As List(Of UInteger)
//! Special codes for colors
enum ColorCodes {
+ ColorByBlock = 0,
+ Red = 1,
+ Yellow = 2,
+ Green = 3,
+ Cyan = 4,
+ Blue = 5,
+ Magenta = 6,
+ White = 7,
+ Gray = 8,
+ Brown = 15,
+ LRed = 23,
+ LGreen = 121,
+ LCyan = 131,
+ LBlue = 163,
+ LMagenta = 221,
+ Black = 250,
+ LGray = 252,
ColorByLayer = 256,
- ColorByBlock = 0
+
};
//! Spaces
*/
class DRW_Coord {
public:
- DRW_Coord() { x = 0; y = 0; z = 0; }
+ DRW_Coord() = default;
DRW_Coord(double ix, double iy, double iz) {
x = ix; y = iy; z = iz;
}
- DRW_Coord operator = (const DRW_Coord& data) {
- x = data.x; y = data.y; z = data.z;
- return *this;
- }
/*!< convert to unitary vector */
void unitize(){
double dist;
}
public:
- double x;
- double y;
- double z;
+ double x = 0;
+ double y = 0;
+ double z = 0;
};
#include "intern/drw_dbg.h"
-//! Calculate arbitary axis
+//! Calculate arbitrary axis
/*!
-* Calculate arbitary axis for apply extrusions
+* Calculate arbitrary axis for apply extrusions
* @author Rallaz
*/
void DRW_Entity::calculateAxis(DRW_Coord extPoint){
extAxisY.unitize();
}
-//! Extrude a point using arbitary axis
+//! Extrude a point using arbitrary axis
/*!
-* apply extrusion in a point using arbitary axis (previous calculated)
+* apply extrusion in a point using arbitrary axis (previous calculated)
* @author Rallaz
*/
void DRW_Entity::extrudePoint(DRW_Coord extPoint, DRW_Coord *point){
return ret;
DRW_DBG("\n***************************** parsing text *********************************************\n");
- // DataFlags RC Used to determine presence of subsquent data, set to 0xFF for R14-
+ // DataFlags RC Used to determine presence of subsequent data, set to 0xFF for R14-
duint8 data_flags = 0x00;
if (version > DRW::AC1014) {//2000+
- data_flags = buf->getRawChar8(); /* DataFlags RC Used to determine presence of subsquent data */
+ data_flags = buf->getRawChar8(); /* DataFlags RC Used to determine presence of subsequent data */
DRW_DBG("data_flags: "); DRW_DBG(data_flags); DRW_DBG("\n");
if ( !(data_flags & 0x01) ) { /* Elevation RD --- present if !(DataFlags & 0x01) */
basePoint.z = buf->getRawDouble();
DRW_UNUSED(ext_ht);
/* Extents wid BD Undocumented and not present in DXF or entget The extents
rectangle, when rotated the same as the text, fits the actual text image on
- the screen (altough we've seen it include an extra row of text in height). */
+ the screen (although we've seen it include an extra row of text in height). */
double ext_wid = buf->getBitDouble();
DRW_UNUSED(ext_wid);
/* Text TV 1 All text in one long string (without '\n's 3 for line wrapping).
DRW_DBG("\ndef line: "); DRW_DBG(angleL); DRW_DBG(","); DRW_DBG(ptL.x); DRW_DBG(","); DRW_DBG(ptL.y);
DRW_DBG(","); DRW_DBG(offL.x); DRW_DBG(","); DRW_DBG(offL.y); DRW_DBG(","); DRW_DBG(angleL);
for (duint16 i = 0 ; i < numDashL; ++i){
- double lenghtL = buf->getBitDouble();
- DRW_DBG(","); DRW_DBG(lenghtL);
+ double lengthL = buf->getBitDouble();
+ DRW_DBG(","); DRW_DBG(lengthL);
}
}//end deflines
} //end not solid
case 51:
hdir = reader->getDouble();
break;
+ case 210:
+ extPoint.x = reader->getDouble();
+ break;
+ case 220:
+ extPoint.y = reader->getDouble();
+ break;
+ case 230:
+ extPoint.z = reader->getDouble();
+ break;
default:
DRW_Entity::parseCode(code, reader);
break;
frozenLyCount = buf->getBitLong();
DRW_DBG("Frozen Layer count?: "); DRW_DBG(frozenLyCount); DRW_DBG("\n");
DRW_DBG("Status Flags?: "); DRW_DBG(buf->getBitLong()); DRW_DBG("\n");
- //RLZ: Warning needed separate string bufer
+ //RLZ: Warning needed separate string buffer
DRW_DBG("Style sheet?: "); DRW_DBG(sBuf->getVariableText(version, false)); DRW_DBG("\n");
DRW_DBG("Render mode?: "); DRW_DBG(buf->getRawChar8()); DRW_DBG("\n");
DRW_DBG("UCS OMore...: "); DRW_DBG(buf->getBit()); DRW_DBG("\n");
DRW_DBG("ShadePlot Mode...: "); DRW_DBG(buf->getBitShort()); DRW_DBG("\n");
}
if (version > DRW::AC1018) {//2007+
- DRW_DBG("Use def Ligth...: "); DRW_DBG(buf->getBit()); DRW_DBG("\n");
- DRW_DBG("Def ligth tipe?: "); DRW_DBG(buf->getRawChar8()); DRW_DBG("\n");
+ DRW_DBG("Use def Light...: "); DRW_DBG(buf->getBit()); DRW_DBG("\n");
+ DRW_DBG("Def light type?: "); DRW_DBG(buf->getRawChar8()); DRW_DBG("\n");
DRW_DBG("Brightness: "); DRW_DBG(buf->getBitDouble()); DRW_DBG("\n");
DRW_DBG("Contrast: "); DRW_DBG(buf->getBitDouble()); DRW_DBG("\n");
// DRW_DBG("Ambient Cmc or Enc: "); DRW_DBG(buf->getCmColor(version)); DRW_DBG("\n");
}
void update() {
- numedges = objlist.size();
+ numedges = (int)objlist.size();
}
public:
double getDir() const { return rot;} /*!< rotation angle of the dimension text, code 53 (optional) default 0 */
void setDir(const double d) { rot = d;}
- DRW_Coord getExtrusion(){return extPoint;} /*!< extrusion, code 210, 220 & 230 */
+ DRW_Coord getExtrusion() const {return extPoint;} /*!< extrusion, code 210, 220 & 230 */
void setExtrusion(const DRW_Coord p) {extPoint =p;}
- std::string getName(){return name;} /*!< Name of the block that contains the entities, code 2 */
+ std::string getName() const {return name;} /*!< Name of the block that contains the entities, code 2 */
void setName(const std::string s) {name = s;}
// int getType(){ return type;} /*!< Dimension type, code 70 */
bool hasActualMeasurement() const { return hasActual; }
DRW_Coord getDiameter1Point() const {return getPt5();} /*!< First definition point for diameter, code 15, 25 & 35 */
void setDiameter1Point(const DRW_Coord p){setPt5(p);}
- DRW_Coord getDiameter2Point() const {return getDefPoint();} /*!< Oposite point for diameter, code 10, 20 & 30 */
+ DRW_Coord getDiameter2Point() const {return getDefPoint();} /*!< Opposite point for diameter, code 10, 20 & 30 */
void setDiameter2Point(const DRW_Coord p){setDefPoint(p);}
double getLeaderLength() const {return getRa40();} /*!< Leader length, code 40 */
void setLeaderLength(const double d) {setRa40(d);}
writer->writeInt16(70, varInt);
else
writer->writeInt16(70, 0);
- writer->writeString(9, "$DIMSAH");
- if (getInt("$DIMSAH", &varInt))
- writer->writeInt16(70, varInt);
- else
- writer->writeInt16(70, 0);
+ writer->writeString(9, "$DIMSAH");
+ if (getInt("$DIMSAH", &varInt))
+ writer->writeInt16(70, varInt);
+ else
+ writer->writeInt16(70, 0);
writer->writeString(9, "$DIMBLK1");
if (getStr("$DIMBLK1", &varStr))
if (ver == DRW::AC1009)
writer->writeInt16(70, varInt);
} else
writer->writeInt16(70, 6);
+ if (getStr("$TDCREATE", &varStr)) {
+ writer->writeString(9, "$TDCREATE");
+ writer->writeString(40, varStr);
+ }
if (ver > DRW::AC1009) {
writer->writeString(9, "$UCSBASE");
if (getStr("$UCSBASE", &varStr))
else
writer->writeDouble(40, 50.0);
writer->writeString(9, "$CAMERAHEIGHT");
- if (getDouble("$CAMERAHEIGTH", &varDouble))
+ if (getDouble("$CAMERAHEIGHT", &varDouble))
writer->writeDouble(40, varDouble);
else
writer->writeDouble(40, 0.0);
DRW_DBG("\nbyte size of data: "); DRW_DBG(size);
if (version > DRW::AC1021 && mv > 3) { //2010+
duint32 hSize = buf->getRawLong32();
- endBitPos += 32; //start bit: + 4 hight size
- DRW_DBG("\n2010+ & MV> 3, higth 32b: "); DRW_DBG(hSize);
+ endBitPos += 32; //start bit: + 4 height size
+ DRW_DBG("\n2010+ & MV> 3, height 32b: "); DRW_DBG(hSize);
}
//RLZ TODO add $ACADVER var & $DWGCODEPAGE & $MEASUREMENT
//RLZ TODO EN 2000 falta $CELWEIGHT, $ENDCAPS, $EXTNAMES $JOINSTYLE $LWDISPLAY $PSTYLEMODE $TDUCREATE $TDUUPDATE $XEDIT
// vars["TDUSRTIMER"]=new DRW_Variant(40, buf->getBitLong());//RLZ: TODO convert to day.msec
// vars["TDUSRTIMER"]=new DRW_Variant(40, buf->getBitLong());//RLZ: TODO convert to day.msec
vars["CECOLOR"]=new DRW_Variant(62, buf->getCmColor(version));//RLZ: TODO read CMC or EMC color
- dwgHandle HANDSEED = buf->getHandle();//allways present in data stream
+ dwgHandle HANDSEED = buf->getHandle();//always present in data stream
DRW_DBG("\nHANDSEED: "); DRW_DBGHL(HANDSEED.code, HANDSEED.size, HANDSEED.ref);
dwgHandle CLAYER = hBbuf->getHandle();
DRW_DBG("\nCLAYER: "); DRW_DBGHL(CLAYER.code, CLAYER.size, CLAYER.ref);
}
}
- buf->setPosition(size+16+4); //readed size +16 start sentinel + 4 size
+ buf->setPosition(size+16+4); //read size +16 start sentinel + 4 size
if (version > DRW::AC1021 && mv > 3) { //2010+
buf->getRawLong32();//advance 4 bytes (hisize)
}
//! Update line type
/*!
-* Update the size and length of line type acording to the path
+* Update the size and length of line type according to the path
* @author Rallaz
*/
/*TODO: control max length permited */
void DRW_LType::update(){
double d =0;
- size = path.size();
+ size = (int)path.size();
for (int i = 0; i< size; i++){
d += fabs(path.at(i));
}
* bit 1 (1) show out of limits
* bit 2 (2) adaptive grid
* bit 3 (4) allow subdivision
- * bit 4 (8) follow dinamic SCP
+ * bit 4 (8) follow dynamic SCP
**/
};
//first entry in this table are 0xA1
#define CPOFFSET932 0xFEC0
//#define CP1LENGHT932 63
-#define CPLENGHT932 7724
+#define CPLENGTH932 7724
#define NOTFOUND932 0x30FB
//Table 932 one byte are
//first entry in this tables are 0x80
#define CPOFFSET936 0x80
-#define CPLENGHT936 21791
+#define CPLENGTH936 21791
#define NOTFOUND936 0x003F
//Table 949 one byte
//first entry in this table are 0x80
#define CPOFFSET949 0x80
-#define CPLENGHT949 17048
+#define CPLENGTH949 17048
#define NOTFOUND949 0x003F
//Table 949 one byte
//first entry in this table are 0x80
#define CPOFFSET950 0x80
-#define CPLENGHT950 13503
+#define CPLENGTH950 13503
#define NOTFOUND950 0x003F
//Table 950 one byte
//first entry in all tables are 0x80
#define CPOFFSET 0x80
-#define CPLENGHTCOMMON 128
+#define CPLENGTHCOMMON 128
//Table 874
static const int DRW_Table874[] = {
} else if (versionStr == "AC1012" || versionStr == "AC1014"
|| versionStr == "AC1015" || versionStr == "AC1018") {
setVersion(DRW::AC1015, dxfFormat);
+ } else {
+ setVersion(DRW::AC1021, dxfFormat);
}
- setVersion(DRW::AC1021, dxfFormat);
}
void DRW_TextCodec::setCodePage(std::string *c, bool dxfFormat){
delete conv;
if (version == DRW::AC1009 || version == DRW::AC1015) {
if (cp == "ANSI_874")
- conv = new DRW_ConvTable(DRW_Table874, CPLENGHTCOMMON);
+ conv = new DRW_ConvTable(DRW_Table874, CPLENGTHCOMMON);
else if (cp == "ANSI_932")
conv = new DRW_Conv932Table(DRW_Table932, DRW_LeadTable932,
- DRW_DoubleTable932, CPLENGHT932);
+ DRW_DoubleTable932, CPLENGTH932);
else if (cp == "ANSI_936")
conv = new DRW_ConvDBCSTable(DRW_Table936, DRW_LeadTable936,
- DRW_DoubleTable936, CPLENGHT936);
+ DRW_DoubleTable936, CPLENGTH936);
else if (cp == "ANSI_949")
conv = new DRW_ConvDBCSTable(DRW_Table949, DRW_LeadTable949,
- DRW_DoubleTable949, CPLENGHT949);
+ DRW_DoubleTable949, CPLENGTH949);
else if (cp == "ANSI_950")
conv = new DRW_ConvDBCSTable(DRW_Table950, DRW_LeadTable950,
- DRW_DoubleTable950, CPLENGHT950);
+ DRW_DoubleTable950, CPLENGTH950);
else if (cp == "ANSI_1250")
- conv = new DRW_ConvTable(DRW_Table1250, CPLENGHTCOMMON);
+ conv = new DRW_ConvTable(DRW_Table1250, CPLENGTHCOMMON);
else if (cp == "ANSI_1251")
- conv = new DRW_ConvTable(DRW_Table1251, CPLENGHTCOMMON);
+ conv = new DRW_ConvTable(DRW_Table1251, CPLENGTHCOMMON);
else if (cp == "ANSI_1253")
- conv = new DRW_ConvTable(DRW_Table1253, CPLENGHTCOMMON);
+ conv = new DRW_ConvTable(DRW_Table1253, CPLENGTHCOMMON);
else if (cp == "ANSI_1254")
- conv = new DRW_ConvTable(DRW_Table1254, CPLENGHTCOMMON);
+ conv = new DRW_ConvTable(DRW_Table1254, CPLENGTHCOMMON);
else if (cp == "ANSI_1255")
- conv = new DRW_ConvTable(DRW_Table1255, CPLENGHTCOMMON);
+ conv = new DRW_ConvTable(DRW_Table1255, CPLENGTHCOMMON);
else if (cp == "ANSI_1256")
- conv = new DRW_ConvTable(DRW_Table1256, CPLENGHTCOMMON);
+ conv = new DRW_ConvTable(DRW_Table1256, CPLENGTHCOMMON);
else if (cp == "ANSI_1257")
- conv = new DRW_ConvTable(DRW_Table1257, CPLENGHTCOMMON);
+ conv = new DRW_ConvTable(DRW_Table1257, CPLENGTHCOMMON);
else if (cp == "ANSI_1258")
- conv = new DRW_ConvTable(DRW_Table1258, CPLENGHTCOMMON);
+ conv = new DRW_ConvTable(DRW_Table1258, CPLENGTHCOMMON);
else if (cp == "UTF-8") { //DXF older than 2007 are write in win codepages
cp = "ANSI_1252";
conv = new DRW_Converter(NULL, 0);
} else
- conv = new DRW_ConvTable(DRW_Table1252, CPLENGHTCOMMON);
+ conv = new DRW_ConvTable(DRW_Table1252, CPLENGTHCOMMON);
} else {
if (dxfFormat)
conv = new DRW_Converter(NULL, 0);//utf16 to utf8
j = i+l;
i = j - 1;
notFound = true;
- for (int k=0; k<cpLenght; k++){
+ for (int k=0; k<cpLength; k++){
if(table[k] == code) {
result += CPOFFSET + k; //translate from table
notFound = false;
return std::string((char*)ret);
}
-/** 's' is a string with at least 4 bytes lenght
-** returned 'b' is byte lenght of encoded char: 2,3 or 4
+/** 's' is a string with at least 4 bytes length
+** returned 'b' is byte length of encoded char: 2,3 or 4
**/
int DRW_Converter::decodeNum(std::string s, int *b){
int code= 0;
j = i+l;
i = j - 1;
notFound = true;
- for (int k=0; k<cpLenght; k++){
+ for (int k=0; k<cpLength; k++){
if(doubleTable[k][1] == code) {
int data = doubleTable[k][0];
char d[3];
}
if (notFound && ( code<0xF8 || (code>0x390 && code<0x542) ||
(code>0x200F && code<0x9FA1) || code>0xF928 )) {
- for (int k=0; k<cpLenght; k++){
+ for (int k=0; k<cpLength; k++){
if(doubleTable[k][1] == code) {
int data = doubleTable[k][0];
char d[3];
std::string DRW_ConvUTF16::fromUtf8(std::string *s){
DRW_UNUSED(s);
- //RLZ: to be writen (only needed for write dwg 2007+)
+ //RLZ: to be written (only needed for write dwg 2007+)
return std::string();
}
{
public:
DRW_Converter(const int *t, int l){table = t;
- cpLenght = l;}
+ cpLength = l;}
virtual ~DRW_Converter(){}
virtual std::string fromUtf8(std::string *s) {return *s;}
virtual std::string toUtf8(std::string *s);
std::string encodeNum(int c);
int decodeNum(std::string s, int *b);
const int *table;
- int cpLenght;
+ int cpLength;
};
class DRW_ConvUTF16 : public DRW_Converter {
/**Reads tree Bits returns a char (3B) for R24 **/
//to be written
-/**Reads compresed Short (max. 16 + 2 bits) little-endian order, returns a UNsigned 16 bits (BS) **/
+/**Reads compressed Short (max. 16 + 2 bits) little-endian order, returns a UNsigned 16 bits (BS) **/
duint16 dwgBuffer::getBitShort(){
duint8 b = get2Bits();
if (b == 0)
else
return 256;
}
-/**Reads compresed Short (max. 16 + 2 bits) little-endian order, returns a signed 16 bits (BS) **/
+/**Reads compressed Short (max. 16 + 2 bits) little-endian order, returns a signed 16 bits (BS) **/
dint16 dwgBuffer::getSBitShort(){
duint8 b = get2Bits();
if (b == 0)
return 256;
}
-/**Reads compresed 32 bits Int (max. 32 + 2 bits) little-endian order, returns a signed 32 bits (BL) **/
+/**Reads compressed 32 bits Int (max. 32 + 2 bits) little-endian order, returns a signed 32 bits (BL) **/
//to be written
dint32 dwgBuffer::getBitLong(){
dint8 b = get2Bits();
return 0;
}
-/**Reads compresed 64 bits Int (max. 56 + 3 bits) little-endian order, returns a unsigned 64 bits (BLL) **/
+/**Reads compressed 64 bits Int (max. 56 + 3 bits) little-endian order, returns a unsigned 64 bits (BLL) **/
duint64 dwgBuffer::getBitLongLong(){
dint8 b = get3Bits();
duint64 ret=0;
return ret;
}
-/**Reads compresed Double (max. 64 + 2 bits) returns a floating point double of 64 bits (BD) **/
+/**Reads compressed Double (max. 64 + 2 bits) returns a floating point double of 64 bits (BD) **/
double dwgBuffer::getBitDouble(){
dint8 b = get2Bits();
if (b == 1)
return 0.0;
}
-/**Reads 3 compresed Double (max. 64 + 2 bits) returns a DRW_Coord of floating point double of 64 bits (3BD) **/
+/**Reads 3 compressed Double (max. 64 + 2 bits) returns a DRW_Coord of floating point double of 64 bits (3BD) **/
DRW_Coord dwgBuffer::get3BitDouble(){
DRW_Coord crd;
crd.x = getBitDouble();
return ret;
}
-/**Reads modular unsigner int, char based, compresed form, little-endian order, returns a unsigned int (U-MC) **/
+/**Reads modular unsigner int, char based, compressed form, little-endian order, returns a unsigned int (U-MC) **/
duint32 dwgBuffer::getUModularChar(){
std::vector<duint8> buffer;
duint32 result =0;
return result;
}
-/**Reads modular int, char based, compresed form, little-endian order, returns a signed int (MC) **/
+/**Reads modular int, char based, compressed form, little-endian order, returns a signed int (MC) **/
dint32 dwgBuffer::getModularChar(){
bool negative = false;
std::vector<dint8> buffer;
return result;
}
-/**Reads modular int, short based, compresed form, little-endian order, returns a unsigned int (MC) **/
+/**Reads modular int, short based, compressed form, little-endian order, returns a unsigned int (MC) **/
dint32 dwgBuffer::getModularShort(){
// bool negative = false;
std::vector<dint16> buffer;
return ext;
}
-/**Reads compresed Double with default (max. 64 + 2 bits) returns a floating point double of 64 bits (DD) **/
+/**Reads compressed Double with default (max. 64 + 2 bits) returns a floating point double of 64 bits (DD) **/
double dwgBuffer::getDefaultDouble(double d){
dint8 b = get2Bits();
if (b == 0)
* address, address in file stream
* dataSize, data size for this page
* startOffset, start offset for this page
- * cSize, compresed size of data
- * uSize, uncompresed size of data
+ * cSize, compressed size of data
+ * uSize, uncompressed size of data
* 2007: page Id, pageCount & pages
* size, size in file
* dataSize
- * startOffset, start position in decompresed data stream
- * cSize, compresed size of data
- * uSize, uncompresed size of data
+ * startOffset, start position in decompressed data stream
+ * cSize, compressed size of data
+ * uSize, uncompressed size of data
* address, address in file stream
* */
class dwgPageInfo {
duint64 size; //in file stream, for rd18, rd21
duint64 dataSize; //for rd18, rd21
duint32 startOffset; //for rd18, rd21
- duint64 cSize; //compresed page size, for rd21
- duint64 uSize; //uncompresed page size, for rd21
+ duint64 cSize; //compressed page size, for rd21
+ duint64 uSize; //uncompressed page size, for rd21
};
// sections of file
/* 2000-: No pages, only section Id, size & address in file
* 2004+: Id, Section Id
- * size, total size of uncompresed data
+ * size, total size of uncompressed data
* pageCount & pages, number of pages in section
* maxSize, max decompressed Size per page
- * compresed, (1 = no, 2 = yes, normally 2)
+ * compressed, (1 = no, 2 = yes, normally 2)
* encrypted, (0 = no, 1 = yes, 2 = unknown)
* name, read & stored but not used
- * 2007: same as 2004+ except encoding, saved in compresed field
+ * 2007: same as 2004+ except encoding, saved in compressed field
* */
class dwgSectionInfo {
public:
dwgSectionInfo(){
- compresed = 1;//1=no, 2=yes
+ compressed = 1;//1=no, 2=yes
encrypted = 0;//???
pageCount = 0;
Id=-1;
~dwgSectionInfo(){}
dint32 Id; //section Id, 2000- rd15 rd18
std::string name; //section name rd18
- duint32 compresed;//is compresed? 1=no, 2=yes rd18, rd21(encoding)
+ duint32 compressed;//is compressed? 1=no, 2=yes rd18, rd21(encoding)
duint32 encrypted;//encrypted (doc: 0=no, 1=yes, 2=unkn) on read: objects 0 and encrypted yes rd18
std::map<duint32, dwgPageInfo >pages;//index, size, offset
duint64 size;//size of section, 2000- rd15, rd18, rd21 (data size)
public:
std::map<duint32, objHandle>ObjectMap;
- std::map<duint32, objHandle>objObjectMap; //stores the ojects & entities not read in readDwgEntities
- std::map<duint32, objHandle>remainingMap; //stores the ojects & entities not read in all proces, for debug only
+ std::map<duint32, objHandle>objObjectMap; //stores the objects & entities not read in readDwgEntities
+ std::map<duint32, objHandle>remainingMap; //stores the objects & entities not read in all processes, for debug only
std::map<duint32, DRW_LType*> ltypemap;
std::map<duint32, DRW_Layer*> layermap;
std::map<duint32, DRW_Block*> blockmap;
} else { DRW_DBG(", "); j++; }
} DRW_DBG("\n");
#endif
- DRW_DBG("decompresing "); DRW_DBG(compSize); DRW_DBG(" bytes in "); DRW_DBG(decompSize); DRW_DBG(" bytes\n");
+ DRW_DBG("decompressing "); DRW_DBG(compSize); DRW_DBG(" bytes in "); DRW_DBG(decompSize); DRW_DBG(" bytes\n");
dwgCompressor comp;
comp.decompress18(tmpCompSec, decompSec, compSize, decompSize);
#ifdef DRW_DBG_DUMP
DRW_DBG("\n header checksum= "); DRW_DBGH(bufHdr.getRawLong32());
DRW_DBG("\n data checksum= "); DRW_DBGH(bufHdr.getRawLong32()); DRW_DBG("\n");
- //get compresed data
+ //get compressed data
duint8 *cData = new duint8[pi.cSize];
if (!fileBuf->setPosition(pi.address+32))
return false;
duint8* oData = objData + pi.startOffset;
pi.uSize = si.maxSize;
- DRW_DBG("decompresing "); DRW_DBG(pi.cSize); DRW_DBG(" bytes in "); DRW_DBG(pi.uSize); DRW_DBG(" bytes\n");
+ DRW_DBG("decompressing "); DRW_DBG(pi.cSize); DRW_DBG(" bytes in "); DRW_DBG(pi.uSize); DRW_DBG(" bytes\n");
dwgCompressor comp;
comp.decompress18(cData, oData, pi.cSize, pi.uSize);
delete[]cData;
if (! fileBuf->setPosition(11))
return false;
maintenanceVersion = fileBuf->getRawChar8();
- DRW_DBG("maintenance verion= "); DRW_DBGH(maintenanceVersion);
+ DRW_DBG("maintenance version= "); DRW_DBGH(maintenanceVersion);
DRW_DBG("\nbyte at 0x0C= "); DRW_DBGH(fileBuf->getRawChar8());
previewImagePos = fileBuf->getRawLong32(); //+ page header size (0x20).
DRW_DBG("\npreviewImagePos (seekerImageData) = "); DRW_DBG(previewImagePos);
duint8 *tmpDecompSec = new duint8[decompSize];
parseSysPage(tmpDecompSec, decompSize);
-//parses "Section page map" decompresed data
+//parses "Section page map" decompressed data
dwgBuffer buff2(tmpDecompSec, decompSize, &decoder);
duint32 address = 0x100;
//stores temporaly info of all pages:
secInfo.maxSize = buff3.getRawLong32();
DRW_DBG("\nMax Decompressed Size= "); DRW_DBGH(secInfo.maxSize);
DRW_DBG("\nunknown long= "); DRW_DBGH(buff3.getRawLong32());
- secInfo.compresed = buff3.getRawLong32();
- DRW_DBG("\nis Compressed? 1:no, 2:yes= "); DRW_DBGH(secInfo.compresed);
+ secInfo.compressed = buff3.getRawLong32();
+ DRW_DBG("\nis Compressed? 1:no, 2:yes= "); DRW_DBGH(secInfo.compressed);
secInfo.Id = buff3.getRawLong32();
DRW_DBG("\nSection Id= "); DRW_DBGH(secInfo.Id);
secInfo.encrypted = buff3.getRawLong32();
DRW_DBG("\ndata size in bytes "); DRW_DBG(size);
if (version > DRW::AC1021 && maintenanceVersion > 3) { //2010+
duint32 hSize = dataBuf.getRawLong32();
- DRW_DBG("\n2010+ & MV> 3, higth 32b: "); DRW_DBG(hSize);
+ DRW_DBG("\n2010+ & MV> 3, height 32b: "); DRW_DBG(hSize);
}
duint32 bitSize = 0;
if (version > DRW::AC1021) {//2007+
if (! fileBuf->setPosition(11))
return false;
maintenanceVersion = fileBuf->getRawChar8();
- DRW_DBG("maintenance verion= "); DRW_DBGH(maintenanceVersion);
+ DRW_DBG("maintenance version= "); DRW_DBGH(maintenanceVersion);
DRW_DBG("\nbyte at 0x0C= "); DRW_DBG(fileBuf->getRawChar8());
previewImagePos = fileBuf->getRawLong32();
DRW_DBG("previewImagePos (seekerImageData) = "); DRW_DBG(previewImagePos);
dwgCompressor::decompress21(tmpPageRS, pageData, pi.cSize, pi.uSize);
#ifdef DRW_DBG_DUMP
- DRW_DBG("\n\nSection OBJECTS decompresed data=\n");
+ DRW_DBG("\n\nSection OBJECTS decompressed data=\n");
for (unsigned int i=0, j=0; i< pi.uSize;i++) {
DRW_DBGH( (unsigned char)pageData[i]);
if (j == 7) { DRW_DBG("\n"); j = 0;
fileHdrData = new duint8[fileHdrDataLength];
fileHdrBuf.getBytes(fileHdrData, fileHdrDataLength);
}else {
- DRW_DBG("\ndwgReader21:: file header are compresed:\n");
+ DRW_DBG("\ndwgReader21:: file header are compressed:\n");
duint8 *compByteStr = new duint8[fileHdrCompLength];
fileHdrBuf.getBytes(compByteStr, fileHdrCompLength);
fileHdrData = new duint8[fileHdrDataLength];
duint64 SectionNameLength = SectionsMapBuf.getRawLong64();
DRW_DBG("\nSectionNameLength = "); DRW_DBG(SectionNameLength);
DRW_DBG("\nUnknown = "); DRW_DBGH(SectionsMapBuf.getRawLong64());
- secInfo.compresed = SectionsMapBuf.getRawLong64();
- DRW_DBG("\nEncoding (compresed) = "); DRW_DBGH(secInfo.compresed);
+ secInfo.compressed = SectionsMapBuf.getRawLong64();
+ DRW_DBG("\nEncoding (compressed) = "); DRW_DBGH(secInfo.compressed);
secInfo.pageCount = SectionsMapBuf.getRawLong64();
DRW_DBG("\nPage count= "); DRW_DBGH(secInfo.pageCount);
secInfo.name = SectionsMapBuf.getUCSStr(SectionNameLength);
duint32 compOffset;
duint32 litCount;
- pos=0; //current position in compresed buffer
- rpos=0; //current position in resulting decompresed buffer
+ pos=0; //current position in compressed buffer
+ rpos=0; //current position in resulting decompressed buffer
litCount = litLength18();
- //copy first lileral lenght
+ //copy first literal length
for (duint32 i=0; i < litCount; ++i) {
bufD[rpos++] = bufC[pos++];
}
DRW_DBG(pos);DRW_DBG(", Dpos: ");DRW_DBG(rpos);DRW_DBG("\n");
return; //fails, not valid
}
- //copy "compresed data", TODO Needed verify out of bounds
+ //copy "compressed data", TODO Needed verify out of bounds
duint32 remaining = sizeD - (litCount+rpos);
if (remaining < compBytes){
compBytes = remaining;
for (duint32 i=0, j= rpos - compOffset -1; i < compBytes; i++) {
bufD[rpos++] = bufD[j++];
}
- //copy "uncompresed data", TODO Needed verify out of bounds
+ //copy "uncompressed data", TODO Needed verify out of bounds
for (duint32 i=0; i < litCount; i++) {
bufD[rpos++] = bufC[pos++];
}
copyCompBytes21(cbuf, dbuf, length, srcIndex, dstIndex);
srcIndex += length;
dstIndex += length;
- if (dstIndex >=dsize) break; //check if last chunk are compresed & terminate
+ if (dstIndex >=dsize) break; //check if last chunk are compressed & terminate
length = 0;
opCode = cbuf[srcIndex++];
std::string getCodePage(){ return decoder.getCodePage();}
protected:
- virtual bool readCode(int *code) = 0; //return true if sucesful (not EOF)
+ virtual bool readCode(int *code) = 0; //return true if successful (not EOF)
virtual bool readString(std::string *text) = 0;
virtual bool readString() = 0;
virtual bool readInt16() = 0;
} else
writer->writeUtf8Caps(2, ent->name);
writer->writeInt16(70, ent->flags);
- if ( version == DRW::AC1009 || !(ent->dimpost.empty()) )
+ if ( version <= DRW::AC1009 || !(ent->dimpost.empty()) )
writer->writeUtf8String(3, ent->dimpost);
- if ( version == DRW::AC1009 || !(ent->dimapost.empty()) )
+ if ( version <= DRW::AC1009 || !(ent->dimapost.empty()) )
writer->writeUtf8String(4, ent->dimapost);
- if ( version == DRW::AC1009 || !(ent->dimblk.empty()) )
+ if ( version <= DRW::AC1009 || !(ent->dimblk.empty()) )
writer->writeUtf8String(5, ent->dimblk);
- if ( version == DRW::AC1009 || !(ent->dimblk1.empty()) )
+ if ( version <= DRW::AC1009 || !(ent->dimblk1.empty()) )
writer->writeUtf8String(6, ent->dimblk1);
- if ( version == DRW::AC1009 || !(ent->dimblk2.empty()) )
+ if ( version <= DRW::AC1009 || !(ent->dimblk2.empty()) )
writer->writeUtf8String(7, ent->dimblk2);
writer->writeDouble(40, ent->dimscale);
writer->writeDouble(41, ent->dimasz);
if (version > DRW::AC1009) {
writer->writeString(100, "AcDbPolyline");
}
- ent->vertexnum = ent->vertlist.size();
+ ent->vertexnum = (int)ent->vertlist.size();
writer->writeInt32(90, ent->vertexnum);
writer->writeInt16(70, ent->flags);
writer->writeDouble(43, ent->width);
bool dxfRW::writePolyline(DRW_Polyline *ent) {
writer->writeString(0, "POLYLINE");
writeEntity(ent);
+ bool is3d = false;
if (version > DRW::AC1009) {
- if (ent->flags & 8 || ent->flags & 16)
- writer->writeString(100, "AcDb2dPolyline");
- else
+ if (ent->flags & 8 || ent->flags & 16) {
writer->writeString(100, "AcDb3dPolyline");
+ is3d = true;
+ } else {
+ writer->writeString(100, "AcDb2dPolyline");
+ }
} else
writer->writeInt16(66, 1);
writer->writeDouble(10, 0.0);
writer->writeDouble(230, crd.z);
}
- int vertexnum = ent->vertlist.size();
- for (int i = 0; i< vertexnum; i++){
+ size_t vertexnum = ent->vertlist.size();
+ for (size_t i = 0; i < vertexnum; i++) {
DRW_Vertex *v = ent->vertlist.at(i);
writer->writeString(0, "VERTEX");
writeEntity(ent);
if (version > DRW::AC1009)
writer->writeString(100, "AcDbVertex");
+ if(is3d) {
+ writer->writeString(100, "AcDb3dPolylineVertex");
+ } else {
+ writer->writeString(100, "AcDb2dVertex");
+ }
if ( (v->flags & 128) && !(v->flags & 64) ) {
writer->writeDouble(10, 0);
writer->writeDouble(20, 0);
writer->writeString(2, ent->name);
writer->writeInt16(70, ent->solid);
writer->writeInt16(71, ent->associative);
- ent->loopsnum = ent->looplist.size();
+ ent->loopsnum = (int)ent->looplist.size();
writer->writeInt16(91, ent->loopsnum);
//write paths data
for (int i = 0; i< ent->loopsnum; i++){
}
writer->writeString(100, "AcDbEntity");
}
- writer->writeString(8, "0");
+ writer->writeString(8, bk->layer);
if (version > DRW::AC1009) {
writer->writeString(100, "AcDbBlockEnd");
}
}
writer->writeString(100, "AcDbEntity");
}
- writer->writeString(8, "0");
+ writer->writeString(8, bk->layer);
if (version > DRW::AC1009) {
writer->writeString(100, "AcDbBlockBegin");
writer->writeUtf8String(2, bk->name);
writer->writeInt16(72, 65);
writer->writeInt16(73, 0);
writer->writeDouble(40, 0.0);
-//Aplication linetypes
+//Application linetypes
iface->writeLTypes();
writer->writeString(0, "ENDTAB");
/*** LAYER ***/
writer->writeInt16(70, 1); //end table def
wlayer0 =false;
iface->writeLayers();
- if (!wlayer0) {
+ if (!wlayer0 && version > DRW::AC1009) {
DRW_Layer lay0;
lay0.name = "0";
writeLayer(&lay0);
writer->writeInt16(281, 0);
}
}
- /* allways call writeBlockRecords to iface for prepare unnamed blocks */
+ /* always call writeBlockRecords to iface for prepare unnamed blocks */
iface->writeBlockRecords();
if (version > DRW::AC1009) {
writer->writeString(0, "ENDTAB");
--- /dev/null
+# default behavior is to always use unix style line endings
+* text eol=lf
+*.png binary
+*.pdn binary
+*.sln binary
+*.suo binary
+*.vcproj binary
+*.patch binary
+*.dll binary
+*.lib binary
--- /dev/null
+ide/vs20??/*.db
+ide/vs20??/*.opendb
+ide/vs20??/*.user
+ide/vs20??/*.vcxproj.filters
+ide/vs20??/.vs
+out/
+docs/
+*.zip
--- /dev/null
+cmake_minimum_required(VERSION 3.0)
+project(libmimalloc C CXX)
+
+set(CMAKE_C_STANDARD 11)
+set(CMAKE_CXX_STANDARD 17)
+
+option(MI_SECURE "Use full security mitigations (like guard pages, allocation randomization, double-free mitigation, and free-list corruption detection)" OFF)
+option(MI_DEBUG_FULL "Use full internal heap invariant checking in DEBUG mode (expensive)" OFF)
+option(MI_PADDING "Enable padding to detect heap block overflow (used only in DEBUG mode)" ON)
+option(MI_OVERRIDE "Override the standard malloc interface (e.g. define entry points for malloc() etc)" ON)
+option(MI_XMALLOC "Enable abort() call on memory allocation failure by default" OFF)
+option(MI_SHOW_ERRORS "Show error and warning messages by default (only enabled by default in DEBUG mode)" OFF)
+option(MI_USE_CXX "Use the C++ compiler to compile the library (instead of the C compiler)" OFF)
+option(MI_SEE_ASM "Generate assembly files" OFF)
+option(MI_INTERPOSE "Use interpose to override standard malloc on macOS" ON)
+option(MI_OSX_ZONE "Use malloc zone to override standard malloc on macOS" OFF) # enables interpose as well
+option(MI_LOCAL_DYNAMIC_TLS "Use slightly slower, dlopen-compatible TLS mechanism (Unix)" OFF)
+option(MI_BUILD_SHARED "Build shared library" ON)
+option(MI_BUILD_STATIC "Build static library" ON)
+option(MI_BUILD_OBJECT "Build object library" ON)
+option(MI_BUILD_TESTS "Build test executables" ON)
+option(MI_DEBUG_TSAN "Build with thread sanitizer (needs clang)" OFF)
+option(MI_DEBUG_UBSAN "Build with undefined-behavior sanitizer (needs clang++)" OFF)
+option(MI_CHECK_FULL "Use full internal invariant checking in DEBUG mode (deprecated, use MI_DEBUG_FULL instead)" OFF)
+
+include("cmake/mimalloc-config-version.cmake")
+
+set(mi_sources
+ src/stats.c
+ src/random.c
+ src/os.c
+ src/arena.c
+ src/region.c
+ src/segment.c
+ src/page.c
+ src/alloc.c
+ src/alloc-aligned.c
+ src/alloc-posix.c
+ src/heap.c
+ src/options.c
+ src/init.c)
+
+# -----------------------------------------------------------------------------
+# Converience: set default build type depending on the build directory
+# -----------------------------------------------------------------------------
+
+if (NOT CMAKE_BUILD_TYPE)
+ if ("${CMAKE_BINARY_DIR}" MATCHES ".*(D|d)ebug$" OR MI_DEBUG_FULL MATCHES "ON")
+ message(STATUS "No build type selected, default to: Debug")
+ set(CMAKE_BUILD_TYPE "Debug")
+ else()
+ message(STATUS "No build type selected, default to: Release")
+ set(CMAKE_BUILD_TYPE "Release")
+ endif()
+endif()
+
+if("${CMAKE_BINARY_DIR}" MATCHES ".*(S|s)ecure$")
+ message(STATUS "Default to secure build")
+ set(MI_SECURE "ON")
+endif()
+
+# -----------------------------------------------------------------------------
+# Process options
+# -----------------------------------------------------------------------------
+
+if(CMAKE_C_COMPILER_ID MATCHES "MSVC|Intel")
+ set(MI_USE_CXX "ON")
+endif()
+
+if(MI_OVERRIDE MATCHES "ON")
+ message(STATUS "Override standard malloc (MI_OVERRIDE=ON)")
+ if(APPLE)
+ if(MI_OSX_ZONE MATCHES "ON")
+ # use zone's on macOS
+ message(STATUS " Use malloc zone to override malloc (MI_OSX_ZONE=ON)")
+ list(APPEND mi_sources src/alloc-override-osx.c)
+ list(APPEND mi_defines MI_OSX_ZONE=1)
+ if(NOT MI_INTERPOSE MATCHES "ON")
+ message(STATUS " (enabling INTERPOSE as well since zone's require this)")
+ set(MI_INTERPOSE "ON")
+ endif()
+ endif()
+ if(MI_INTERPOSE MATCHES "ON")
+ # use interpose on macOS
+ message(STATUS " Use interpose to override malloc (MI_INTERPOSE=ON)")
+ list(APPEND mi_defines MI_INTERPOSE)
+ endif()
+ endif()
+endif()
+
+if(MI_SECURE MATCHES "ON")
+ message(STATUS "Set full secure build (MI_SECURE=ON)")
+ list(APPEND mi_defines MI_SECURE=4)
+endif()
+
+if(MI_SEE_ASM MATCHES "ON")
+ message(STATUS "Generate assembly listings (MI_SEE_ASM=ON)")
+ list(APPEND mi_cflags -save-temps)
+endif()
+
+if(MI_CHECK_FULL MATCHES "ON")
+ message(STATUS "The MI_CHECK_FULL option is deprecated, use MI_DEBUG_FULL instead")
+ set(MI_DEBUG_FULL "ON")
+endif()
+
+if(MI_DEBUG_FULL MATCHES "ON")
+ message(STATUS "Set debug level to full internal invariant checking (MI_DEBUG_FULL=ON)")
+ list(APPEND mi_defines MI_DEBUG=3) # full invariant checking
+endif()
+
+if(MI_PADDING MATCHES "OFF")
+ message(STATUS "Disable padding of heap blocks in debug mode (MI_PADDING=OFF)")
+ list(APPEND mi_defines MI_PADDING=0)
+endif()
+
+if(MI_XMALLOC MATCHES "ON")
+ message(STATUS "Enable abort() calls on memory allocation failure (MI_XMALLOC=ON)")
+ list(APPEND mi_defines MI_XMALLOC=1)
+endif()
+
+if(MI_SHOW_ERRORS MATCHES "ON")
+ message(STATUS "Enable printing of error and warning messages by default (MI_SHOW_ERRORS=ON)")
+ list(APPEND mi_defines MI_SHOW_ERRORS=1)
+endif()
+
+if(MI_DEBUG_TSAN MATCHES "ON")
+ if(CMAKE_C_COMPILER_ID MATCHES "Clang")
+ message(STATUS "Build with thread sanitizer (MI_DEBUG_TSAN=ON)")
+ list(APPEND mi_cflags -fsanitize=thread -g -O1)
+ list(APPEND CMAKE_EXE_LINKER_FLAGS -fsanitize=thread)
+ else()
+ message(WARNING "Can only use thread sanitizer with clang (MI_DEBUG_TSAN=ON but ignored)")
+ endif()
+endif()
+
+if(MI_DEBUG_UBSAN MATCHES "ON")
+ if(CMAKE_BUILD_TYPE MATCHES "Debug")
+ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ message(STATUS "Build with undefined-behavior sanitizer (MI_DEBUG_UBSAN=ON)")
+ list(APPEND mi_cflags -fsanitize=undefined -g)
+ list(APPEND CMAKE_EXE_LINKER_FLAGS -fsanitize=undefined)
+ if (MI_USE_CXX MATCHES "OFF")
+ message(STATUS "(switch to use C++ due to MI_DEBUG_UBSAN)")
+ set(MI_USE_CXX "ON")
+ endif()
+ else()
+ message(WARNING "Can only use undefined-behavior sanitizer with clang++ (MI_DEBUG_UBSAN=ON but ignored)")
+ endif()
+ else()
+ message(WARNING "Can only use thread sanitizer with a debug build (CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})")
+ endif()
+endif()
+
+if(MI_USE_CXX MATCHES "ON")
+ message(STATUS "Use the C++ compiler to compile (MI_USE_CXX=ON)")
+ set_source_files_properties(${mi_sources} PROPERTIES LANGUAGE CXX )
+ set_source_files_properties(src/static.c test/test-api.c test/test-stress PROPERTIES LANGUAGE CXX )
+ if(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang|Clang")
+ list(APPEND mi_cflags -Wno-deprecated)
+ endif()
+ if(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
+ list(APPEND mi_cflags -Kc++)
+ endif()
+endif()
+
+# Compiler flags
+if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU")
+ list(APPEND mi_cflags -Wall -Wextra -Wno-unknown-pragmas -fvisibility=hidden)
+ if(CMAKE_C_COMPILER_ID MATCHES "GNU")
+ list(APPEND mi_cflags -Wno-invalid-memory-model)
+ endif()
+endif()
+
+if(CMAKE_C_COMPILER_ID MATCHES "Intel")
+ list(APPEND mi_cflags -Wall -fvisibility=hidden)
+endif()
+
+if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel" AND NOT CMAKE_SYSTEM_NAME MATCHES "Haiku")
+ if(MI_LOCAL_DYNAMIC_TLS MATCHES "ON")
+ list(APPEND mi_cflags -ftls-model=local-dynamic)
+ else()
+ list(APPEND mi_cflags -ftls-model=initial-exec)
+ endif()
+endif()
+
+# Architecture flags
+if(${CMAKE_HOST_SYSTEM_PROCESSOR} MATCHES "arm")
+ list(APPEND mi_cflags -march=native)
+endif()
+
+# extra needed libraries
+if(WIN32)
+ list(APPEND mi_libraries psapi shell32 user32 bcrypt)
+else()
+ if(NOT ${CMAKE_C_COMPILER} MATCHES "android")
+ list(APPEND mi_libraries pthread)
+ find_library(LIBRT rt)
+ if(LIBRT)
+ list(APPEND mi_libraries ${LIBRT})
+ endif()
+ endif()
+endif()
+
+# -----------------------------------------------------------------------------
+# Install and output names
+# -----------------------------------------------------------------------------
+
+set(mi_install_dir "${CMAKE_INSTALL_PREFIX}/lib/mimalloc-${mi_version}")
+if(MI_SECURE MATCHES "ON")
+ set(mi_basename "mimalloc-secure")
+else()
+ set(mi_basename "mimalloc")
+endif()
+string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LC)
+if(NOT(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel)$"))
+ set(mi_basename "${mi_basename}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version
+endif()
+if(MI_BUILD_SHARED)
+ list(APPEND mi_build_targets "shared")
+endif()
+if(MI_BUILD_STATIC)
+ list(APPEND mi_build_targets "static")
+endif()
+if(MI_BUILD_OBJECT)
+ list(APPEND mi_build_targets "object")
+endif()
+if(MI_BUILD_TESTS)
+ list(APPEND mi_build_targets "tests")
+endif()
+message(STATUS "")
+message(STATUS "Library base name: ${mi_basename}")
+message(STATUS "Build type : ${CMAKE_BUILD_TYPE_LC}")
+message(STATUS "Install directory: ${mi_install_dir}")
+message(STATUS "Build targets : ${mi_build_targets}")
+message(STATUS "")
+
+# -----------------------------------------------------------------------------
+# Main targets
+# -----------------------------------------------------------------------------
+
+# shared library
+if(MI_BUILD_SHARED)
+ add_library(mimalloc SHARED ${mi_sources})
+ set_target_properties(mimalloc PROPERTIES VERSION ${mi_version} OUTPUT_NAME ${mi_basename} )
+ target_compile_definitions(mimalloc PRIVATE ${mi_defines} MI_SHARED_LIB MI_SHARED_LIB_EXPORT)
+ target_compile_options(mimalloc PRIVATE ${mi_cflags})
+ target_link_libraries(mimalloc PUBLIC ${mi_libraries})
+ target_include_directories(mimalloc PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:${mi_install_dir}/include>
+ )
+ if(WIN32)
+ # On windows copy the mimalloc redirection dll too.
+ target_link_libraries(mimalloc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect.lib)
+ add_custom_command(TARGET mimalloc POST_BUILD
+ COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect.dll" $<TARGET_FILE_DIR:mimalloc>
+ COMMENT "Copy mimalloc-redirect.dll to output directory")
+ endif()
+
+ install(TARGETS mimalloc EXPORT mimalloc DESTINATION ${mi_install_dir} LIBRARY)
+ install(EXPORT mimalloc DESTINATION ${mi_install_dir}/cmake)
+endif()
+
+# static library
+if (MI_BUILD_STATIC)
+ add_library(mimalloc-static STATIC ${mi_sources})
+ set_property(TARGET mimalloc-static PROPERTY POSITION_INDEPENDENT_CODE ON)
+ target_compile_definitions(mimalloc-static PRIVATE ${mi_defines} MI_STATIC_LIB)
+ target_compile_options(mimalloc-static PRIVATE ${mi_cflags})
+ target_link_libraries(mimalloc-static PUBLIC ${mi_libraries})
+ target_include_directories(mimalloc-static PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:${mi_install_dir}/include>
+ )
+ if(WIN32)
+ # When building both static and shared libraries on Windows, a static library should use a
+ # different output name to avoid the conflict with the import library of a shared one.
+ string(REPLACE "mimalloc" "mimalloc-static" mi_output_name ${mi_basename})
+ set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_output_name})
+ else()
+ set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_basename})
+ endif()
+
+ install(TARGETS mimalloc-static EXPORT mimalloc DESTINATION ${mi_install_dir})
+endif()
+
+# install include files
+install(FILES include/mimalloc.h DESTINATION ${mi_install_dir}/include)
+install(FILES include/mimalloc-override.h DESTINATION ${mi_install_dir}/include)
+install(FILES include/mimalloc-new-delete.h DESTINATION ${mi_install_dir}/include)
+install(FILES cmake/mimalloc-config.cmake DESTINATION ${mi_install_dir}/cmake)
+install(FILES cmake/mimalloc-config-version.cmake DESTINATION ${mi_install_dir}/cmake)
+
+if(NOT WIN32 AND MI_BUILD_SHARED)
+ # install a symlink in the /usr/local/lib to the versioned library
+ set(mi_symlink "${CMAKE_SHARED_MODULE_PREFIX}${mi_basename}${CMAKE_SHARED_LIBRARY_SUFFIX}")
+ set(mi_soname "mimalloc-${mi_version}/${mi_symlink}.${mi_version}")
+ install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${mi_soname} ${mi_symlink} WORKING_DIRECTORY ${mi_install_dir}/..)")
+ install(CODE "MESSAGE(\"-- Symbolic link: ${CMAKE_INSTALL_PREFIX}/lib/${mi_symlink} -> ${mi_soname}\")")
+endif()
+
+# single object file for more predictable static overriding
+if (MI_BUILD_OBJECT)
+ add_library(mimalloc-obj OBJECT src/static.c)
+ set_property(TARGET mimalloc-obj PROPERTY POSITION_INDEPENDENT_CODE ON)
+ target_compile_definitions(mimalloc-obj PRIVATE ${mi_defines})
+ target_compile_options(mimalloc-obj PRIVATE ${mi_cflags})
+ target_include_directories(mimalloc-obj PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:${mi_install_dir}/include>
+ )
+
+ # the following seems to lead to cmake warnings/errors on some systems, disable for now :-(
+ # install(TARGETS mimalloc-obj EXPORT mimalloc DESTINATION ${mi_install_dir})
+
+ # the FILES expression can also be: $<TARGET_OBJECTS:mimalloc-obj>
+ # but that fails cmake versions less than 3.10 so we leave it as is for now
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/mimalloc-obj.dir/src/static.c${CMAKE_C_OUTPUT_EXTENSION}
+ DESTINATION ${mi_install_dir}
+ RENAME ${mi_basename}${CMAKE_C_OUTPUT_EXTENSION} )
+endif()
+
+# -----------------------------------------------------------------------------
+# API surface testing
+# -----------------------------------------------------------------------------
+
+if (MI_BUILD_TESTS MATCHES "ON")
+ add_executable(mimalloc-test-api test/test-api.c)
+ target_compile_definitions(mimalloc-test-api PRIVATE ${mi_defines})
+ target_compile_options(mimalloc-test-api PRIVATE ${mi_cflags})
+ target_include_directories(mimalloc-test-api PRIVATE include)
+ target_link_libraries(mimalloc-test-api PRIVATE mimalloc-static ${mi_libraries})
+
+ add_executable(mimalloc-test-stress test/test-stress.c)
+ target_compile_definitions(mimalloc-test-stress PRIVATE ${mi_defines})
+ target_compile_options(mimalloc-test-stress PRIVATE ${mi_cflags})
+ target_include_directories(mimalloc-test-stress PRIVATE include)
+ target_link_libraries(mimalloc-test-stress PRIVATE mimalloc ${mi_libraries})
+
+ enable_testing()
+ add_test(test_api, mimalloc-test-api)
+ add_test(test_stress, mimalloc-test-stress)
+endif()
+
+# -----------------------------------------------------------------------------
+# Set override properties
+# -----------------------------------------------------------------------------
+if (MI_OVERRIDE MATCHES "ON")
+ if (MI_BUILD_SHARED)
+ target_compile_definitions(mimalloc PRIVATE MI_MALLOC_OVERRIDE)
+ endif()
+ if(NOT WIN32)
+ # It is only possible to override malloc on Windows when building as a DLL.
+ if (MI_BUILD_STATIC)
+ target_compile_definitions(mimalloc-static PRIVATE MI_MALLOC_OVERRIDE)
+ endif()
+ if (MI_BUILD_OBJECT)
+ target_compile_definitions(mimalloc-obj PRIVATE MI_MALLOC_OVERRIDE)
+ endif()
+ endif()
+endif()
--- /dev/null
+MIT License
+
+Copyright (c) 2019 Microsoft Corporation, Daan Leijen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null
+# Starter pipeline
+# Start with a minimal pipeline that you can customize to build and deploy your code.
+# Add steps that build, run tests, deploy, and more:
+# https://aka.ms/yaml
+
+trigger:
+- master
+- dev
+
+jobs:
+- job:
+ displayName: Windows
+ pool:
+ vmImage:
+ windows-2019
+ strategy:
+ matrix:
+ Debug:
+ BuildType: debug
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON
+ MSBuildConfiguration: Debug
+ Release:
+ BuildType: release
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release
+ MSBuildConfiguration: Release
+ Secure:
+ BuildType: secure
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=ON
+ MSBuildConfiguration: Release
+ steps:
+ - task: CMake@1
+ inputs:
+ workingDirectory: $(BuildType)
+ cmakeArgs: .. $(cmakeExtraArgs)
+ - task: MSBuild@1
+ inputs:
+ solution: $(BuildType)/libmimalloc.sln
+ configuration: '$(MSBuildConfiguration)'
+ - script: |
+ cd $(BuildType)
+ ctest
+ displayName: CTest
+# - upload: $(Build.SourcesDirectory)/$(BuildType)
+# artifact: mimalloc-windows-$(BuildType)
+
+- job:
+ displayName: Linux
+ pool:
+ vmImage:
+ ubuntu-16.04
+ strategy:
+ matrix:
+ Debug:
+ CC: gcc
+ CXX: g++
+ BuildType: debug
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON
+ Release:
+ CC: gcc
+ CXX: g++
+ BuildType: release
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release
+ Secure:
+ CC: gcc
+ CXX: g++
+ BuildType: secure
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=ON
+ Debug++:
+ CC: gcc
+ CXX: g++
+ BuildType: debug-cxx
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_USE_CXX=ON
+ Debug Clang:
+ CC: clang
+ CXX: clang++
+ BuildType: debug-clang
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON
+ Release Clang:
+ CC: clang
+ CXX: clang++
+ BuildType: release-clang
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release
+ Secure Clang:
+ CC: clang
+ CXX: clang++
+ BuildType: secure-clang
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=ON
+ Debug++ Clang:
+ CC: clang
+ CXX: clang++
+ BuildType: debug-clang-cxx
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_USE_CXX=ON
+ steps:
+ - task: CMake@1
+ inputs:
+ workingDirectory: $(BuildType)
+ cmakeArgs: .. $(cmakeExtraArgs)
+ - script: make -j$(nproc) -C $(BuildType)
+ displayName: Make
+ - script: make test -C $(BuildType)
+ displayName: CTest
+# - upload: $(Build.SourcesDirectory)/$(BuildType)
+# artifact: mimalloc-ubuntu-$(BuildType)
+
+- job:
+ displayName: macOS
+ pool:
+ vmImage:
+ macOS-10.14
+ strategy:
+ matrix:
+ Debug:
+ BuildType: debug
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON
+ Release:
+ BuildType: release
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release
+ Secure:
+ BuildType: secure
+ cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=ON
+ steps:
+ - task: CMake@1
+ inputs:
+ workingDirectory: $(BuildType)
+ cmakeArgs: .. $(cmakeExtraArgs)
+ - script: make -j$(sysctl -n hw.ncpu) -C $(BuildType)
+ displayName: Make
+ - script: make test -C $(BuildType)
+ displayName: CTest
+# - upload: $(Build.SourcesDirectory)/$(BuildType)
+# artifact: mimalloc-macos-$(BuildType)
--- /dev/null
+set(mi_version_major 1)
+set(mi_version_minor 6)
+set(mi_version ${mi_version_major}.${mi_version_minor})
+
+set(PACKAGE_VERSION ${mi_version})
+if("${PACKAGE_FIND_VERSION_MAJOR}" EQUAL "${mi_version_major}")
+ if ("${PACKAGE_FIND_VERSION_MINOR}" EQUAL "${mi_version_minor}")
+ set(PACKAGE_VERSION_EXACT TRUE)
+ elseif("${PACKAGE_FIND_VERSION_MINOR}" LESS "${mi_version_minor}")
+ set(PACKAGE_VERSION_COMPATIBLE TRUE)
+ else()
+ set(PACKAGE_VERSION_UNSUITABLE TRUE)
+ endif()
+else()
+ set(PACKAGE_VERSION_UNSUITABLE TRUE)
+endif()
--- /dev/null
+include(${CMAKE_CURRENT_LIST_DIR}/mimalloc.cmake)
+get_filename_component(MIMALLOC_TARGET_DIR "${CMAKE_CURRENT_LIST_DIR}" PATH)
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='167.731pt' version='1.1' viewBox='52.938 54.996 381.624 167.731' width='381.624pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip1'>
+<path d='M82.148 206.586H434.164V81.34H82.148Z'/>
+</clipPath>
+<use id='g3-40' transform='scale(1.143)' xlink:href='#g0-40'/>
+<use id='g3-41' transform='scale(1.143)' xlink:href='#g0-41'/>
+<use id='g3-78' transform='scale(1.143)' xlink:href='#g0-78'/>
+<use id='g3-97' transform='scale(1.143)' xlink:href='#g0-97'/>
+<use id='g3-98' transform='scale(1.143)' xlink:href='#g0-98'/>
+<use id='g3-99' transform='scale(1.143)' xlink:href='#g0-99'/>
+<use id='g3-100' transform='scale(1.143)' xlink:href='#g0-100'/>
+<use id='g3-101' transform='scale(1.143)' xlink:href='#g0-101'/>
+<use id='g3-102' transform='scale(1.143)' xlink:href='#g0-102'/>
+<use id='g3-105' transform='scale(1.143)' xlink:href='#g0-105'/>
+<use id='g3-108' transform='scale(1.143)' xlink:href='#g0-108'/>
+<use id='g3-109' transform='scale(1.143)' xlink:href='#g0-109'/>
+<use id='g3-110' transform='scale(1.143)' xlink:href='#g0-110'/>
+<use id='g3-111' transform='scale(1.143)' xlink:href='#g0-111'/>
+<use id='g3-112' transform='scale(1.143)' xlink:href='#g0-112'/>
+<use id='g3-114' transform='scale(1.143)' xlink:href='#g0-114'/>
+<use id='g3-115' transform='scale(1.143)' xlink:href='#g0-115'/>
+<use id='g3-116' transform='scale(1.143)' xlink:href='#g0-116'/>
+<use id='g3-119' transform='scale(1.143)' xlink:href='#g0-119'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g1-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g1-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g1-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g1-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g1-108'/>
+<path d='M6.581 -2.663C6.581 -3.327 6.402 -4.08 5.317 -4.08C4.564 -4.08 4.142 -3.622 3.927 -3.344C3.865 -3.524 3.676 -4.08 2.762 -4.08C2.053 -4.08 1.623 -3.667 1.417 -3.398V-4.035H0.726V0H1.47V-2.188C1.47 -2.78 1.704 -3.497 2.385 -3.497C3.282 -3.497 3.282 -2.86 3.282 -2.6V0H4.026V-2.188C4.026 -2.78 4.259 -3.497 4.94 -3.497C5.837 -3.497 5.837 -2.86 5.837 -2.6V0H6.581V-2.663Z' id='g1-109'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g1-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g1-118'/>
+<use id='g2-46' transform='scale(0.714)' xlink:href='#g0-46'/>
+<use id='g2-48' transform='scale(0.714)' xlink:href='#g0-48'/>
+<use id='g2-49' transform='scale(0.714)' xlink:href='#g0-49'/>
+<use id='g2-50' transform='scale(0.714)' xlink:href='#g0-50'/>
+<use id='g2-51' transform='scale(0.714)' xlink:href='#g0-51'/>
+<use id='g2-52' transform='scale(0.714)' xlink:href='#g0-52'/>
+<use id='g2-53' transform='scale(0.714)' xlink:href='#g0-53'/>
+<use id='g2-54' transform='scale(0.714)' xlink:href='#g0-54'/>
+<use id='g2-55' transform='scale(0.714)' xlink:href='#g0-55'/>
+<use id='g2-56' transform='scale(0.714)' xlink:href='#g0-56'/>
+<use id='g2-57' transform='scale(0.714)' xlink:href='#g0-57'/>
+<use id='g2-120' transform='scale(0.714)' xlink:href='#g0-120'/>
+<path d='M1.445 -1.245C1.445 -1.41 1.305 -1.549 1.141 -1.549S0.837 -1.41 0.837 -1.245S0.976 -0.941 1.141 -0.941S1.445 -1.081 1.445 -1.245Z' id='g4-1'/>
+<path d='M2.127 -5.23C2.008 -5.23 1.995 -5.23 1.911 -5.154C1.032 -4.387 0.586 -3.145 0.586 -1.743C0.586 -0.425 0.983 0.844 1.904 1.653C1.995 1.743 2.008 1.743 2.127 1.743H2.462C2.441 1.73 1.764 1.151 1.444 0.063C1.276 -0.481 1.193 -1.053 1.193 -1.743C1.193 -4.156 2.322 -5.112 2.462 -5.23H2.127Z' id='g0-40'/>
+<path d='M0.746 1.743C0.865 1.743 0.879 1.743 0.962 1.667C1.841 0.9 2.287 -0.342 2.287 -1.743C2.287 -3.062 1.89 -4.331 0.969 -5.14C0.879 -5.23 0.865 -5.23 0.746 -5.23H0.411C0.432 -5.216 1.109 -4.638 1.43 -3.55C1.597 -3.006 1.681 -2.434 1.681 -1.743C1.681 0.669 0.551 1.625 0.411 1.743H0.746Z' id='g0-41'/>
+<path d='M1.339 -0.628H0.711V0H1.339V-0.628Z' id='g0-46'/>
+<path d='M3.403 -2.267C3.403 -2.601 3.403 -3.417 3.075 -3.989C2.72 -4.617 2.183 -4.721 1.848 -4.721C1.534 -4.721 0.99 -4.624 0.642 -4.024C0.307 -3.466 0.293 -2.706 0.293 -2.267C0.293 -1.75 0.321 -1.116 0.614 -0.586C0.921 -0.021 1.437 0.146 1.848 0.146C2.545 0.146 2.929 -0.258 3.138 -0.697C3.382 -1.193 3.403 -1.834 3.403 -2.267ZM1.848 -0.314C1.555 -0.314 1.22 -0.481 1.046 -0.983C0.907 -1.409 0.9 -1.848 0.9 -2.357C0.9 -2.999 0.9 -4.261 1.848 -4.261S2.797 -2.999 2.797 -2.357C2.797 -1.897 2.797 -1.374 2.629 -0.928C2.434 -0.425 2.078 -0.314 1.848 -0.314Z' id='g0-48'/>
+<path d='M2.239 -4.721H2.085C1.632 -4.303 1.06 -4.275 0.642 -4.261V-3.822C0.914 -3.829 1.262 -3.843 1.611 -3.982V-0.439H0.683V0H3.166V-0.439H2.239V-4.721Z' id='g0-49'/>
+<path d='M1.974 -0.537C1.89 -0.537 1.806 -0.53 1.723 -0.53H0.928L2.008 -1.485C2.134 -1.597 2.476 -1.855 2.608 -1.967C2.915 -2.246 3.327 -2.608 3.327 -3.215C3.327 -4.003 2.741 -4.721 1.743 -4.721C1.004 -4.721 0.544 -4.324 0.307 -3.612L0.635 -3.201C0.795 -3.787 1.039 -4.24 1.646 -4.24C2.232 -4.24 2.678 -3.829 2.678 -3.201C2.678 -2.622 2.336 -2.294 1.918 -1.897C1.778 -1.757 1.402 -1.444 1.255 -1.304C1.053 -1.123 0.572 -0.656 0.37 -0.481V0H3.327V-0.537H1.974Z' id='g0-50'/>
+<path d='M0.697 -3.578C0.983 -4.135 1.485 -4.289 1.82 -4.289C2.232 -4.289 2.538 -4.052 2.538 -3.654C2.538 -3.285 2.287 -2.831 1.757 -2.741C1.723 -2.734 1.695 -2.734 1.234 -2.699V-2.239H1.778C2.441 -2.239 2.685 -1.716 2.685 -1.276C2.685 -0.732 2.35 -0.314 1.806 -0.314C1.311 -0.314 0.746 -0.551 0.398 -0.997L0.307 -0.544C0.711 -0.091 1.276 0.146 1.82 0.146C2.734 0.146 3.389 -0.537 3.389 -1.269C3.389 -1.841 2.929 -2.301 2.378 -2.462C2.908 -2.734 3.18 -3.201 3.18 -3.654C3.18 -4.247 2.573 -4.721 1.827 -4.721C1.213 -4.721 0.704 -4.4 0.411 -3.982L0.697 -3.578Z' id='g0-51'/>
+<path d='M2.762 -1.165H3.487V-1.625H2.762V-4.575H2.071L0.209 -1.625V-1.165H2.162V0H2.762V-1.165ZM0.802 -1.625C1.011 -1.953 2.211 -3.815 2.211 -4.233V-1.625H0.802Z' id='g0-52'/>
+<path d='M1.144 -4.094H3.075V-4.575H0.586V-1.967H1.095C1.262 -2.343 1.59 -2.511 1.904 -2.511C2.19 -2.511 2.622 -2.315 2.622 -1.43C2.622 -0.516 2.043 -0.314 1.688 -0.314C1.227 -0.314 0.781 -0.558 0.544 -0.955L0.279 -0.537C0.621 -0.112 1.137 0.146 1.688 0.146C2.608 0.146 3.327 -0.565 3.327 -1.416C3.327 -2.28 2.685 -2.971 1.918 -2.971C1.618 -2.971 1.353 -2.866 1.144 -2.692V-4.094Z' id='g0-53'/>
+<path d='M3.062 -4.582C2.685 -4.721 2.42 -4.721 2.287 -4.721C1.227 -4.721 0.307 -3.724 0.307 -2.253C0.307 -0.363 1.158 0.146 1.862 0.146C2.427 0.146 2.72 -0.119 2.936 -0.342C3.382 -0.816 3.389 -1.311 3.389 -1.555C3.389 -2.469 2.894 -3.229 2.218 -3.229C1.534 -3.229 1.165 -2.873 0.962 -2.671C1.053 -3.626 1.541 -4.289 2.294 -4.289C2.434 -4.289 2.713 -4.275 3.062 -4.142V-4.582ZM0.969 -1.534C0.969 -1.576 0.969 -1.681 0.976 -1.716C0.976 -2.19 1.276 -2.769 1.897 -2.769C2.748 -2.769 2.748 -1.792 2.748 -1.555C2.748 -1.29 2.748 -0.997 2.559 -0.704C2.392 -0.453 2.183 -0.314 1.862 -0.314C1.123 -0.314 1.004 -1.227 0.969 -1.534Z' id='g0-54'/>
+<path d='M1.723 -4.038C1.806 -4.038 1.89 -4.045 1.974 -4.045H2.852C1.792 -3.006 1.116 -1.548 1.116 0.07H1.771C1.771 -1.967 2.762 -3.431 3.389 -4.087V-4.575H0.307V-4.038H1.723Z' id='g0-55'/>
+<path d='M2.385 -2.469C2.845 -2.615 3.285 -2.985 3.285 -3.501C3.285 -4.135 2.678 -4.721 1.848 -4.721S0.411 -4.135 0.411 -3.501C0.411 -2.978 0.865 -2.608 1.311 -2.469C0.697 -2.28 0.307 -1.806 0.307 -1.269C0.307 -0.523 0.969 0.146 1.848 0.146S3.389 -0.523 3.389 -1.269C3.389 -1.806 2.992 -2.28 2.385 -2.469ZM1.848 -2.699C1.353 -2.699 0.948 -2.985 0.948 -3.494C0.948 -3.94 1.262 -4.289 1.848 -4.289C2.427 -4.289 2.748 -3.94 2.748 -3.494C2.748 -2.999 2.357 -2.699 1.848 -2.699ZM1.848 -0.314C1.367 -0.314 0.941 -0.621 0.941 -1.276C0.941 -1.904 1.346 -2.239 1.848 -2.239S2.755 -1.897 2.755 -1.276C2.755 -0.621 2.322 -0.314 1.848 -0.314Z' id='g0-56'/>
+<path d='M0.537 -0.174C0.879 0.077 1.193 0.146 1.52 0.146C2.497 0.146 3.389 -0.837 3.389 -2.336C3.389 -4.24 2.545 -4.721 1.876 -4.721C1.255 -4.721 0.969 -4.428 0.767 -4.226C0.321 -3.773 0.307 -3.292 0.307 -3.02C0.307 -2.12 0.795 -1.346 1.478 -1.346C2.267 -1.346 2.699 -1.869 2.734 -1.911C2.636 -0.802 2.092 -0.314 1.52 -0.314C1.158 -0.314 0.934 -0.446 0.774 -0.579L0.537 -0.174ZM2.713 -3.027C2.72 -2.985 2.72 -2.915 2.72 -2.873C2.72 -2.357 2.406 -1.806 1.799 -1.806C1.534 -1.806 1.325 -1.883 1.144 -2.169C0.962 -2.441 0.948 -2.706 0.948 -3.02C0.948 -3.292 0.948 -3.605 1.165 -3.912C1.311 -4.122 1.52 -4.289 1.869 -4.289C2.545 -4.289 2.692 -3.473 2.713 -3.027Z' id='g0-57'/>
+<path d='M1.646 -4.84H0.697V0H1.283V-4.289H1.29L3.578 0H4.526V-4.84H3.94V-0.551H3.933L1.646 -4.84Z' id='g0-78'/>
+<path d='M2.971 -2.008C2.971 -2.72 2.427 -3.201 1.736 -3.201C1.297 -3.201 0.962 -3.11 0.572 -2.901L0.614 -2.392C0.844 -2.545 1.186 -2.755 1.736 -2.755C2.043 -2.755 2.364 -2.525 2.364 -2.001V-1.723C1.332 -1.688 0.314 -1.471 0.314 -0.823C0.314 -0.474 0.551 0.07 1.165 0.07C1.465 0.07 2.015 0.007 2.385 -0.265V0H2.971V-2.008ZM2.364 -0.99C2.364 -0.851 2.364 -0.669 2.12 -0.523C1.897 -0.398 1.625 -0.391 1.548 -0.391C1.165 -0.391 0.872 -0.565 0.872 -0.83C0.872 -1.276 2.05 -1.318 2.364 -1.332V-0.99Z' id='g0-97'/>
+<path d='M1.179 -4.84H0.593V0H1.2V-0.328C1.353 -0.195 1.688 0.07 2.197 0.07C2.957 0.07 3.571 -0.642 3.571 -1.555C3.571 -2.399 3.089 -3.166 2.392 -3.166C1.953 -3.166 1.527 -3.027 1.179 -2.769V-4.84ZM1.2 -2.197C1.2 -2.308 1.2 -2.392 1.444 -2.552C1.548 -2.615 1.736 -2.706 1.974 -2.706C2.441 -2.706 2.964 -2.392 2.964 -1.555C2.964 -0.704 2.385 -0.391 1.897 -0.391C1.639 -0.391 1.395 -0.509 1.2 -0.823V-2.197Z' id='g0-98'/>
+<path d='M3.034 -0.76C2.685 -0.537 2.308 -0.411 1.876 -0.411C1.234 -0.411 0.858 -0.928 0.858 -1.555C0.858 -2.092 1.137 -2.72 1.897 -2.72C2.371 -2.72 2.594 -2.622 2.95 -2.399L3.041 -2.901C2.622 -3.11 2.441 -3.201 1.897 -3.201C0.851 -3.201 0.251 -2.357 0.251 -1.548C0.251 -0.697 0.921 0.07 1.869 0.07C2.357 0.07 2.776 -0.077 3.075 -0.251L3.034 -0.76Z' id='g0-99'/>
+<path d='M3.229 -4.84H2.643V-2.797C2.197 -3.124 1.743 -3.166 1.541 -3.166C0.809 -3.166 0.251 -2.434 0.251 -1.548S0.802 0.07 1.52 0.07C1.953 0.07 2.357 -0.126 2.622 -0.363V0H3.229V-4.84ZM2.622 -0.865C2.448 -0.579 2.183 -0.391 1.848 -0.391C1.36 -0.391 0.858 -0.732 0.858 -1.541C0.858 -2.413 1.451 -2.706 1.925 -2.706C2.204 -2.706 2.441 -2.587 2.622 -2.35V-0.865Z' id='g0-100'/>
+<path d='M2.999 -0.76C2.608 -0.481 2.169 -0.391 1.869 -0.391C1.262 -0.391 0.802 -0.886 0.781 -1.527H3.068C3.068 -1.848 3.034 -2.315 2.762 -2.713C2.511 -3.068 2.092 -3.201 1.75 -3.201C0.9 -3.201 0.244 -2.455 0.244 -1.569C0.244 -0.676 0.941 0.07 1.862 0.07C2.267 0.07 2.685 -0.049 3.041 -0.265L2.999 -0.76ZM0.83 -1.946C0.99 -2.504 1.402 -2.741 1.75 -2.741C2.057 -2.741 2.511 -2.594 2.643 -1.946H0.83Z' id='g0-101'/>
+<path d='M1.325 -2.657H2.12V-3.096H1.304V-3.898C1.304 -4.38 1.743 -4.449 1.974 -4.449C2.12 -4.449 2.308 -4.428 2.566 -4.331V-4.84C2.385 -4.882 2.169 -4.91 1.981 -4.91C1.262 -4.91 0.739 -4.394 0.739 -3.703V-3.096H0.202V-2.657H0.739V0H1.325V-2.657Z' id='g0-102'/>
+<path d='M1.227 -4.784H0.523V-4.08H1.227V-4.784ZM1.172 -3.096H0.586V0H1.172V-3.096Z' id='g0-105'/>
+<path d='M1.172 -4.84H0.586V0H1.172V-4.84Z' id='g0-108'/>
+<path d='M5.3 -2.064C5.3 -2.608 5.14 -3.166 4.282 -3.166C3.696 -3.166 3.333 -2.824 3.166 -2.601C3.096 -2.79 2.922 -3.166 2.225 -3.166C1.827 -3.166 1.444 -3.006 1.137 -2.636V-3.145H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-1.695C3.243 -2.155 3.438 -2.706 3.975 -2.706C4.693 -2.706 4.693 -2.218 4.693 -2.015V0H5.3V-2.064Z' id='g0-109'/>
+<path d='M3.243 -2.064C3.243 -2.608 3.082 -3.166 2.225 -3.166C1.827 -3.166 1.444 -3.006 1.137 -2.636V-3.145H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-2.064Z' id='g0-110'/>
+<path d='M3.487 -1.527C3.487 -2.448 2.755 -3.201 1.848 -3.201S0.209 -2.441 0.209 -1.527C0.209 -0.642 0.948 0.07 1.848 0.07C2.755 0.07 3.487 -0.642 3.487 -1.527ZM1.848 -0.411C1.297 -0.411 0.816 -0.816 0.816 -1.604S1.332 -2.741 1.848 -2.741C2.371 -2.741 2.88 -2.378 2.88 -1.604C2.88 -0.809 2.385 -0.411 1.848 -0.411Z' id='g0-111'/>
+<path d='M1.2 -0.328C1.569 0.007 1.967 0.07 2.204 0.07C2.943 0.07 3.571 -0.635 3.571 -1.555C3.571 -2.392 3.11 -3.166 2.42 -3.166C2.106 -3.166 1.583 -3.075 1.179 -2.762V-3.096H0.593V1.353H1.2V-0.328ZM1.2 -2.315C1.36 -2.511 1.632 -2.685 1.967 -2.685C2.525 -2.685 2.964 -2.169 2.964 -1.555C2.964 -0.865 2.441 -0.391 1.897 -0.391C1.792 -0.391 1.618 -0.404 1.437 -0.551C1.227 -0.711 1.2 -0.816 1.2 -0.948V-2.315Z' id='g0-112'/>
+<path d='M1.179 -1.485C1.179 -2.239 1.806 -2.643 2.42 -2.65V-3.166C1.834 -3.159 1.409 -2.873 1.13 -2.504V-3.145H0.593V0H1.179V-1.485Z' id='g0-114'/>
+<path d='M2.545 -2.985C2.071 -3.18 1.723 -3.201 1.471 -3.201C1.297 -3.201 0.244 -3.201 0.244 -2.273C0.244 -1.946 0.425 -1.764 0.516 -1.681C0.76 -1.437 1.053 -1.381 1.423 -1.311C1.75 -1.248 2.127 -1.179 2.127 -0.844C2.127 -0.404 1.548 -0.404 1.451 -0.404C1.004 -0.404 0.586 -0.565 0.307 -0.76L0.209 -0.237C0.446 -0.119 0.872 0.07 1.451 0.07C1.764 0.07 2.071 0.021 2.329 -0.167C2.587 -0.363 2.671 -0.669 2.671 -0.907C2.671 -1.032 2.657 -1.304 2.364 -1.569C2.106 -1.799 1.855 -1.848 1.52 -1.911C1.109 -1.988 0.788 -2.05 0.788 -2.357C0.788 -2.755 1.297 -2.755 1.402 -2.755C1.799 -2.755 2.106 -2.671 2.455 -2.49L2.545 -2.985Z' id='g0-115'/>
+<path d='M1.311 -2.657H2.343V-3.096H1.311V-3.982H0.774V-3.096H0.139V-2.657H0.753V-0.893C0.753 -0.425 0.872 0.07 1.374 0.07S2.26 -0.091 2.469 -0.188L2.35 -0.635C2.12 -0.467 1.876 -0.411 1.681 -0.411C1.388 -0.411 1.311 -0.697 1.311 -1.018V-2.657Z' id='g0-116'/>
+<path d='M4.951 -3.096H4.407C4.345 -2.901 3.954 -1.723 3.738 -0.997C3.682 -0.795 3.612 -0.572 3.592 -0.411H3.585C3.543 -0.697 3.299 -1.451 3.285 -1.499L2.769 -3.096H2.239C2.036 -2.497 1.513 -0.934 1.458 -0.425H1.451C1.395 -0.921 0.879 -2.462 0.767 -2.797C0.711 -2.964 0.711 -2.978 0.676 -3.096H0.105L1.123 0H1.709C1.716 -0.028 1.904 -0.579 2.148 -1.353C2.253 -1.695 2.462 -2.364 2.497 -2.671L2.504 -2.678C2.518 -2.532 2.559 -2.378 2.608 -2.204S2.706 -1.841 2.755 -1.681L3.292 0H3.933L4.951 -3.096Z' id='g0-119'/>
+<path d='M1.932 -1.597L3.285 -3.096H2.671L1.681 -1.953L0.669 -3.096H0.042L1.437 -1.597L0 0H0.621L1.681 -1.311L2.783 0H3.41L1.932 -1.597Z' id='g0-120'/>
+</defs>
+<g id='page1'>
+<path d='M140.82 215.441V206.586M199.488 215.441V206.586M258.156 215.441V206.586M316.824 215.441V206.586M375.496 215.441V206.586M140.82 72.48V81.34M199.488 72.48V81.34M258.156 72.48V81.34M316.824 72.48V81.34M375.496 72.48V81.34' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M111.484 210.836V206.586M170.152 210.836V206.586M228.824 210.836V206.586M287.492 210.836V206.586M346.16 210.836V206.586M404.828 210.836V206.586M111.484 77.086V81.34M170.152 77.086V81.34M228.824 77.086V81.34M287.492 77.086V81.34M346.16 77.086V81.34M404.828 77.086V81.34' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 206.586H86.402M82.148 175.273H86.402M82.148 143.961H86.402M82.148 112.648H86.402M82.148 81.34H86.402M434.164 206.586H429.91M434.164 175.273H429.91M434.164 143.961H429.91M434.164 112.648H429.91M434.164 81.34H429.91' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 206.586V81.34H434.164V206.586H82.148Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -11.54 34.954)'>
+<use x='114.487' xlink:href='#g3-99' y='186.027'/>
+<use x='118.25' xlink:href='#g3-102' y='186.027'/>
+<use x='120.838' xlink:href='#g3-114' y='186.027'/>
+<use x='123.73' xlink:href='#g3-97' y='186.027'/>
+<use x='127.798' xlink:href='#g3-99' y='186.027'/>
+</g>
+<g transform='matrix(1 0 0 1 45.565 34.954)'>
+<use x='114.487' xlink:href='#g3-108' y='186.027'/>
+<use x='116.507' xlink:href='#g3-101' y='186.027'/>
+<use x='120.271' xlink:href='#g3-97' y='186.027'/>
+<use x='124.339' xlink:href='#g3-110' y='186.027'/>
+<use x='128.711' xlink:href='#g3-78' y='186.027'/>
+</g>
+<g transform='matrix(1 0 0 1 106.188 34.954)'>
+<use x='114.487' xlink:href='#g3-114' y='186.027'/>
+<use x='117.379' xlink:href='#g3-101' y='186.027'/>
+<use x='121.142' xlink:href='#g3-100' y='186.027'/>
+<use x='125.515' xlink:href='#g3-105' y='186.027'/>
+<use x='127.535' xlink:href='#g3-115' y='186.027'/>
+</g>
+<g transform='matrix(1 0 0 1 159.716 34.954)'>
+<use x='114.487' xlink:href='#g3-108' y='186.027'/>
+<use x='116.507' xlink:href='#g3-97' y='186.027'/>
+<use x='120.34' xlink:href='#g3-114' y='186.027'/>
+<use x='123.232' xlink:href='#g3-115' y='186.027'/>
+<use x='126.478' xlink:href='#g3-111' y='186.027'/>
+<use x='130.712' xlink:href='#g3-110' y='186.027'/>
+<use x='135.085' xlink:href='#g3-78' y='186.027'/>
+</g>
+<g transform='matrix(1 0 0 1 215.596 34.954)'>
+<use x='114.487' xlink:href='#g3-109' y='186.027'/>
+<use x='121.211' xlink:href='#g3-115' y='186.027'/>
+<use x='124.458' xlink:href='#g3-116' y='186.027'/>
+<use x='127.516' xlink:href='#g3-114' y='186.027'/>
+<use x='130.408' xlink:href='#g3-101' y='186.027'/>
+<use x='134.171' xlink:href='#g3-115' y='186.027'/>
+<use x='137.418' xlink:href='#g3-115' y='186.027'/>
+<use x='140.664' xlink:href='#g3-78' y='186.027'/>
+</g>
+<g transform='matrix(1 0 0 1 277.158 34.954)'>
+<use x='114.487' xlink:href='#g3-114' y='186.027'/>
+<use x='117.379' xlink:href='#g3-112' y='186.027'/>
+<use x='121.751' xlink:href='#g3-116' y='186.027'/>
+<use x='124.809' xlink:href='#g3-101' y='186.027'/>
+<use x='128.573' xlink:href='#g3-115' y='186.027'/>
+<use x='131.819' xlink:href='#g3-116' y='186.027'/>
+<use x='134.877' xlink:href='#g3-78' y='186.027'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 22.192)'>
+<use x='114.487' xlink:href='#g2-48' y='186.027'/>
+<use x='117.133' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -9.12)'>
+<use x='114.487' xlink:href='#g2-48' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-53' y='186.027'/>
+<use x='121.25' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -40.431)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -71.743)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-53' y='186.027'/>
+<use x='121.25' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -103.054)'>
+<use x='114.487' xlink:href='#g2-50' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-120' y='186.027'/>
+</g>
+<path clip-path='url(#clip1)' d='M82.148 143.961H434.164' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M88.945 206.586H92.18V143.961H88.945ZM147.613 206.586H150.852V143.961H147.613ZM206.281 206.586H209.52V143.961H206.281ZM264.949 206.586H268.188V143.961H264.949ZM323.621 206.586H326.859V143.961H323.621ZM382.289 206.586H385.527V143.961H382.289Z' fill='#993333'/>
+<path clip-path='url(#clip1)' d='M88.945 206.586H92.18V143.961H88.945ZM147.613 206.586H150.852V143.961H147.613ZM206.281 206.586H209.52V143.961H206.281ZM264.949 206.586H268.188V143.961H264.949ZM323.621 206.586H326.859V143.961H323.621ZM382.289 206.586H385.527V143.961H382.289Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M90.563 143.961V143.898' fill='#993333'/>
+<path clip-path='url(#clip1)' d='M90.563 143.961V143.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M88.57 143.898H92.555' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M90.563 143.961V144.023' fill='#993333'/>
+<path clip-path='url(#clip1)' d='M90.563 143.961V144.023' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M92.555 144.024H88.57' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M149.231 143.961V143.523' fill='#993333'/>
+<path clip-path='url(#clip1)' d='M149.231 143.961V143.523' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M147.238 143.524H151.226' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M149.231 143.961V144.398' fill='#993333'/>
+<path clip-path='url(#clip1)' d='M149.231 143.961V144.398' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M151.223 144.398H147.238' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M207.902 143.961V142.207' fill='#993333'/>
+<path clip-path='url(#clip1)' d='M207.902 143.961V142.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M205.91 142.207H209.894' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M207.902 143.961V145.715' fill='#993333'/>
+<path clip-path='url(#clip1)' d='M207.902 143.961V145.715' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M209.891 145.715H205.906' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M266.57 143.961V143.336' fill='#993333'/>
+<path clip-path='url(#clip1)' d='M266.57 143.961V143.336' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M264.578 143.336H268.562' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M266.57 143.961V144.586' fill='#993333'/>
+<path clip-path='url(#clip1)' d='M266.57 143.961V144.586' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M268.563 144.586H264.578' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M325.238 143.961V143.586' fill='#993333'/>
+<path clip-path='url(#clip1)' d='M325.238 143.961V143.586' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M323.246 143.586H327.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M325.238 143.961V144.336' fill='#993333'/>
+<path clip-path='url(#clip1)' d='M325.238 143.961V144.336' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M327.231 144.336H323.246' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M383.906 143.961V143.523' fill='#993333'/>
+<path clip-path='url(#clip1)' d='M383.906 143.961V143.523' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M381.914 143.524H385.902' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M383.906 143.961V144.398' fill='#993333'/>
+<path clip-path='url(#clip1)' d='M383.906 143.961V144.398' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M385.898 144.398H381.914' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M94.176 206.586H97.41V142.519H94.176ZM152.844 206.586H156.082V140.203H152.844ZM211.512 206.586H214.75V148.344H211.512ZM270.18 206.586H273.418V121.418H270.18ZM328.852 206.586H332.09V135.07H328.852ZM387.52 206.586H390.758V81.34H387.52Z' fill='#8080bf'/>
+<path clip-path='url(#clip1)' d='M94.176 206.586H97.41V142.519H94.176ZM152.844 206.586H156.082V140.203H152.844ZM211.512 206.586H214.75V148.344H211.512ZM270.18 206.586H273.418V121.418H270.18ZM328.852 206.586H332.09V135.07H328.852ZM387.52 206.586H390.758V81.34H387.52Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M95.793 142.519V142.519' fill='#8080bf'/>
+<path clip-path='url(#clip1)' d='M93.801 142.52H97.785' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M95.793 142.519V142.519' fill='#8080bf'/>
+<path clip-path='url(#clip1)' d='M93.801 142.52H97.785' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M154.461 140.203V139.203' fill='#8080bf'/>
+<path clip-path='url(#clip1)' d='M154.461 140.203V139.203' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M152.469 139.203H156.457' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M154.461 140.203V141.207' fill='#8080bf'/>
+<path clip-path='url(#clip1)' d='M154.461 140.203V141.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M156.453 141.207H152.468' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M213.133 148.344V148.031' fill='#8080bf'/>
+<path clip-path='url(#clip1)' d='M213.133 148.344V148.031' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M211.141 148.032H215.125' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M213.133 148.344V148.656' fill='#8080bf'/>
+<path clip-path='url(#clip1)' d='M213.133 148.344V148.656' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M215.121 148.656H211.136' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M271.801 121.418V121.23' fill='#8080bf'/>
+<path clip-path='url(#clip1)' d='M271.801 121.418V121.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M269.809 121.23H273.793' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M271.801 121.418V121.605' fill='#8080bf'/>
+<path clip-path='url(#clip1)' d='M271.801 121.418V121.605' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M273.793 121.606H269.808' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M330.469 135.07V134.254' fill='#8080bf'/>
+<path clip-path='url(#clip1)' d='M330.469 135.07V134.254' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M328.477 134.254H332.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M330.469 135.07V135.883' fill='#8080bf'/>
+<path clip-path='url(#clip1)' d='M330.469 135.07V135.883' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M332.461 135.883H328.476' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M389.137 81.34V81.34' fill='#8080bf'/>
+<path clip-path='url(#clip1)' d='M387.145 81.34H391.133' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M389.137 81.34V81.34' fill='#8080bf'/>
+<path clip-path='url(#clip1)' d='M387.145 81.34H391.133' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M99.406 206.586H102.641V140.266H99.406ZM158.074 206.586H161.313V142.898H158.074ZM216.742 206.586H219.981V134.754H216.742ZM275.41 206.586H278.649V99.25H275.41ZM334.082 206.586H337.32V81.34H334.082ZM392.75 206.586H395.988V81.34H392.75Z' fill='#ffb733'/>
+<path clip-path='url(#clip1)' d='M99.406 206.586H102.641V140.266H99.406ZM158.074 206.586H161.313V142.898H158.074ZM216.742 206.586H219.981V134.754H216.742ZM275.41 206.586H278.649V99.25H275.41ZM334.082 206.586H337.32V81.34H334.082ZM392.75 206.586H395.988V81.34H392.75Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M101.024 140.266V140.203' fill='#ffb733'/>
+<path clip-path='url(#clip1)' d='M101.024 140.266V140.203' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M99.031 140.203H103.016' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M101.024 140.266V140.328' fill='#ffb733'/>
+<path clip-path='url(#clip1)' d='M101.024 140.266V140.328' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M103.016 140.328H99.031' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M159.691 142.898V142.269' fill='#ffb733'/>
+<path clip-path='url(#clip1)' d='M159.691 142.898V142.269' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M157.699 142.269H161.687' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M159.691 142.898V143.523' fill='#ffb733'/>
+<path clip-path='url(#clip1)' d='M159.691 142.898V143.523' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M161.684 143.524H157.699' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M218.363 134.754V134.066' fill='#ffb733'/>
+<path clip-path='url(#clip1)' d='M218.363 134.754V134.066' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M216.371 134.067H220.355' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M218.363 134.754V135.445' fill='#ffb733'/>
+<path clip-path='url(#clip1)' d='M218.363 134.754V135.445' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M220.352 135.445H216.367' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M277.031 99.25V91.168' fill='#ffb733'/>
+<path clip-path='url(#clip1)' d='M277.031 99.25V91.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M275.039 91.172H279.023' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M277.031 99.25V107.328' fill='#ffb733'/>
+<path clip-path='url(#clip1)' d='M277.031 99.25V107.328' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M279.024 107.328H275.039' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M335.699 81.34V81.34' fill='#ffb733'/>
+<path clip-path='url(#clip1)' d='M333.707 81.34H337.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M335.699 81.34V81.34' fill='#ffb733'/>
+<path clip-path='url(#clip1)' d='M333.707 81.34H337.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M394.367 81.34V81.34' fill='#ffb733'/>
+<path clip-path='url(#clip1)' d='M392.375 81.34H396.363' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M394.367 81.34V81.34' fill='#ffb733'/>
+<path clip-path='url(#clip1)' d='M392.375 81.34H396.363' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M104.637 206.586H107.871V124.859H104.637ZM163.305 206.586H166.543V141.207H163.305ZM221.973 206.586H225.211V118.035H221.973ZM280.641 206.586H283.879V121.043H280.641ZM339.313 206.586H342.551V133.066H339.313ZM397.981 206.586H401.219V89.039H397.981Z' fill='#bf80bf'/>
+<path clip-path='url(#clip1)' d='M104.637 206.586H107.871V124.859H104.637ZM163.305 206.586H166.543V141.207H163.305ZM221.973 206.586H225.211V118.035H221.973ZM280.641 206.586H283.879V121.043H280.641ZM339.313 206.586H342.551V133.066H339.313ZM397.981 206.586H401.219V89.039H397.981Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M106.254 124.859V124.797' fill='#bf80bf'/>
+<path clip-path='url(#clip1)' d='M106.254 124.859V124.797' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M104.261 124.796H108.246' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M106.254 124.859V124.922' fill='#bf80bf'/>
+<path clip-path='url(#clip1)' d='M106.254 124.859V124.922' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M108.246 124.922H104.261' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M164.922 141.207V140.894' fill='#bf80bf'/>
+<path clip-path='url(#clip1)' d='M164.922 141.207V140.894' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M162.929 140.895H166.917' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M164.922 141.207V141.519' fill='#bf80bf'/>
+<path clip-path='url(#clip1)' d='M164.922 141.207V141.519' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M166.914 141.52H162.929' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M223.594 118.035V117.973' fill='#bf80bf'/>
+<path clip-path='url(#clip1)' d='M223.594 118.035V117.973' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M221.601 117.973H225.585' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M223.594 118.035V118.098' fill='#bf80bf'/>
+<path clip-path='url(#clip1)' d='M223.594 118.035V118.098' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M225.582 118.098H221.597' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M282.262 121.043V120.539' fill='#bf80bf'/>
+<path clip-path='url(#clip1)' d='M282.262 121.043V120.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M280.269 120.539H284.253' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M282.262 121.043V121.543' fill='#bf80bf'/>
+<path clip-path='url(#clip1)' d='M282.262 121.043V121.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M284.254 121.543H280.269' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M340.93 133.066V132.812' fill='#bf80bf'/>
+<path clip-path='url(#clip1)' d='M340.93 133.066V132.812' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M338.937 132.812H342.921' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M340.93 133.066V133.316' fill='#bf80bf'/>
+<path clip-path='url(#clip1)' d='M340.93 133.066V133.316' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M342.922 133.316H338.937' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M399.598 89.039V88.539' fill='#bf80bf'/>
+<path clip-path='url(#clip1)' d='M399.598 89.039V88.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M397.605 88.539H401.593' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M399.598 89.039V89.543' fill='#bf80bf'/>
+<path clip-path='url(#clip1)' d='M399.598 89.039V89.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M401.59 89.543H397.605' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M109.867 206.586H113.102V142.332H109.867ZM168.535 206.586H171.774V143.086H168.535ZM227.203 206.586H230.442V132.125H227.203ZM285.871 206.586H289.109V139.641H285.871ZM344.543 206.586H347.781V81.34H344.543ZM403.211 206.586H406.449V100.187H403.211Z' fill='#dfbf9f'/>
+<path clip-path='url(#clip1)' d='M109.867 206.586H113.102V142.332H109.867ZM168.535 206.586H171.774V143.086H168.535ZM227.203 206.586H230.442V132.125H227.203ZM285.871 206.586H289.109V139.641H285.871ZM344.543 206.586H347.781V81.34H344.543ZM403.211 206.586H406.449V100.187H403.211Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M111.484 142.332V142.269' fill='#dfbf9f'/>
+<path clip-path='url(#clip1)' d='M111.484 142.332V142.269' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M109.492 142.269H113.477' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M111.484 142.332V142.394' fill='#dfbf9f'/>
+<path clip-path='url(#clip1)' d='M111.484 142.332V142.394' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M113.477 142.395H109.492' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M170.152 143.086V142.644' fill='#dfbf9f'/>
+<path clip-path='url(#clip1)' d='M170.152 143.086V142.644' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M168.16 142.644H172.145' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M170.152 143.086V143.523' fill='#dfbf9f'/>
+<path clip-path='url(#clip1)' d='M170.152 143.086V143.523' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M172.145 143.524H168.16' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M228.824 132.125V131.312' fill='#dfbf9f'/>
+<path clip-path='url(#clip1)' d='M228.824 132.125V131.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M226.832 131.312H230.816' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M228.824 132.125V132.941' fill='#dfbf9f'/>
+<path clip-path='url(#clip1)' d='M228.824 132.125V132.941' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M230.813 132.941H226.828' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M287.492 139.641V138.387' fill='#dfbf9f'/>
+<path clip-path='url(#clip1)' d='M287.492 139.641V138.387' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M285.5 138.387H289.484' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M287.492 139.641V140.894' fill='#dfbf9f'/>
+<path clip-path='url(#clip1)' d='M287.492 139.641V140.894' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M289.485 140.895H285.5' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M346.16 81.34V81.34' fill='#dfbf9f'/>
+<path clip-path='url(#clip1)' d='M344.168 81.34H348.152' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M346.16 81.34V81.34' fill='#dfbf9f'/>
+<path clip-path='url(#clip1)' d='M344.168 81.34H348.152' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M404.828 100.187V99.562' fill='#dfbf9f'/>
+<path clip-path='url(#clip1)' d='M404.828 100.187V99.562' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M402.836 99.562H406.824' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M404.828 100.187V100.812' fill='#dfbf9f'/>
+<path clip-path='url(#clip1)' d='M404.828 100.187V100.812' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M406.821 100.813H402.836' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M115.098 206.586H118.332V135.57H115.098ZM173.766 206.586H177.004V136.07H173.766ZM232.434 206.586H235.672V114.965H232.434ZM291.102 206.586H294.34V113.84H291.102ZM349.774 206.586H353.012V81.34H349.774ZM408.442 206.586H411.68V81.34H408.442Z' fill='#80bf80'/>
+<path clip-path='url(#clip1)' d='M115.098 206.586H118.332V135.57H115.098ZM173.766 206.586H177.004V136.07H173.766ZM232.434 206.586H235.672V114.965H232.434ZM291.102 206.586H294.34V113.84H291.102ZM349.774 206.586H353.012V81.34H349.774ZM408.442 206.586H411.68V81.34H408.442Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M116.715 135.57V135.508' fill='#80bf80'/>
+<path clip-path='url(#clip1)' d='M116.715 135.57V135.508' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M114.722 135.508H118.707' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M116.715 135.57V135.633' fill='#80bf80'/>
+<path clip-path='url(#clip1)' d='M116.715 135.57V135.633' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M118.707 135.633H114.722' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M175.383 136.07V135.258' fill='#80bf80'/>
+<path clip-path='url(#clip1)' d='M175.383 136.07V135.258' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M173.39 135.258H177.375' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M175.383 136.07V136.883' fill='#80bf80'/>
+<path clip-path='url(#clip1)' d='M175.383 136.07V136.883' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M177.375 136.883H173.39' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M234.055 114.965V114.09' fill='#80bf80'/>
+<path clip-path='url(#clip1)' d='M234.055 114.965V114.09' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M232.062 114.09H236.046' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M234.055 114.965V115.844' fill='#80bf80'/>
+<path clip-path='url(#clip1)' d='M234.055 114.965V115.844' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M236.043 115.843H232.058' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M292.723 113.84V113.215' fill='#80bf80'/>
+<path clip-path='url(#clip1)' d='M292.723 113.84V113.215' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M290.73 113.215H294.714' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M292.723 113.84V114.465' fill='#80bf80'/>
+<path clip-path='url(#clip1)' d='M292.723 113.84V114.465' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M294.715 114.465H290.73' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M351.391 81.34V81.34' fill='#80bf80'/>
+<path clip-path='url(#clip1)' d='M349.398 81.34H353.382' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M351.391 81.34V81.34' fill='#80bf80'/>
+<path clip-path='url(#clip1)' d='M349.398 81.34H353.382' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M410.059 81.34V81.34' fill='#80bf80'/>
+<path clip-path='url(#clip1)' d='M408.066 81.34H412.054' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M410.059 81.34V81.34' fill='#80bf80'/>
+<path clip-path='url(#clip1)' d='M408.066 81.34H412.054' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M120.328 206.586H123.563V135.633H120.328ZM178.996 206.586H182.234V81.34H178.996ZM237.664 206.586H240.902V138.387H237.664ZM296.332 206.586H299.57V81.34H296.332ZM355.004 206.586H358.238V81.34H355.004ZM413.672 206.586H416.91V81.34H413.672Z' fill='#bfbf80'/>
+<path clip-path='url(#clip1)' d='M120.328 206.586H123.563V135.633H120.328ZM178.996 206.586H182.234V81.34H178.996ZM237.664 206.586H240.902V138.387H237.664ZM296.332 206.586H299.57V81.34H296.332ZM355.004 206.586H358.238V81.34H355.004ZM413.672 206.586H416.91V81.34H413.672Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M121.945 135.633V135.57' fill='#bfbf80'/>
+<path clip-path='url(#clip1)' d='M121.945 135.633V135.57' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M119.953 135.571H123.938' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M121.945 135.633V135.695' fill='#bfbf80'/>
+<path clip-path='url(#clip1)' d='M121.945 135.633V135.695' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M123.938 135.695H119.953' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M180.613 81.34V81.34' fill='#bfbf80'/>
+<path clip-path='url(#clip1)' d='M178.621 81.34H182.606' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M180.613 81.34V81.34' fill='#bfbf80'/>
+<path clip-path='url(#clip1)' d='M178.621 81.34H182.606' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M239.285 138.387V136.195' fill='#bfbf80'/>
+<path clip-path='url(#clip1)' d='M239.285 138.387V136.195' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M237.293 136.195H241.278' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M239.285 138.387V140.578' fill='#bfbf80'/>
+<path clip-path='url(#clip1)' d='M239.285 138.387V140.578' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M241.274 140.578H237.289' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M297.953 81.34V81.34' fill='#bfbf80'/>
+<path clip-path='url(#clip1)' d='M295.961 81.34H299.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M297.953 81.34V81.34' fill='#bfbf80'/>
+<path clip-path='url(#clip1)' d='M295.961 81.34H299.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M356.621 81.34V81.34' fill='#bfbf80'/>
+<path clip-path='url(#clip1)' d='M354.629 81.34H358.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M356.621 81.34V81.34' fill='#bfbf80'/>
+<path clip-path='url(#clip1)' d='M354.629 81.34H358.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M415.289 81.34V81.34' fill='#bfbf80'/>
+<path clip-path='url(#clip1)' d='M413.297 81.34H417.285' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M415.289 81.34V81.34' fill='#bfbf80'/>
+<path clip-path='url(#clip1)' d='M413.297 81.34H417.285' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M125.559 206.586H128.793V139.266H125.559ZM184.227 206.586H187.465V135.695H184.227ZM242.895 206.586H246.133V137.824H242.895ZM301.563 206.586H304.801V97.433H301.563ZM360.234 206.586H363.469V81.34H360.234ZM418.902 206.586H422.141V81.34H418.902Z' fill='#339999'/>
+<path clip-path='url(#clip1)' d='M125.559 206.586H128.793V139.266H125.559ZM184.227 206.586H187.465V135.695H184.227ZM242.895 206.586H246.133V137.824H242.895ZM301.563 206.586H304.801V97.433H301.563ZM360.234 206.586H363.469V81.34H360.234ZM418.902 206.586H422.141V81.34H418.902Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M127.176 139.266V139.203' fill='#339999'/>
+<path clip-path='url(#clip1)' d='M127.176 139.266V139.203' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M125.183 139.203H129.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M127.176 139.266V139.328' fill='#339999'/>
+<path clip-path='url(#clip1)' d='M127.176 139.266V139.328' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M129.168 139.328H125.183' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M185.844 135.695V135.258' fill='#339999'/>
+<path clip-path='url(#clip1)' d='M185.844 135.695V135.258' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M183.851 135.258H187.836' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M185.844 135.695V136.133' fill='#339999'/>
+<path clip-path='url(#clip1)' d='M185.844 135.695V136.133' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M187.836 136.133H183.851' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M244.516 137.824V137.012' fill='#339999'/>
+<path clip-path='url(#clip1)' d='M244.516 137.824V137.012' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M242.523 137.012H246.508' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M244.516 137.824V138.637' fill='#339999'/>
+<path clip-path='url(#clip1)' d='M244.516 137.824V138.637' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M246.504 138.637H242.519' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M303.184 97.433V95.617' fill='#339999'/>
+<path clip-path='url(#clip1)' d='M303.184 97.433V95.617' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M301.191 95.617H305.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M303.184 97.433V99.25' fill='#339999'/>
+<path clip-path='url(#clip1)' d='M303.184 97.433V99.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M305.176 99.25H301.191' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M361.852 81.34V81.34' fill='#339999'/>
+<path clip-path='url(#clip1)' d='M359.859 81.34H363.844' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M361.852 81.34V81.34' fill='#339999'/>
+<path clip-path='url(#clip1)' d='M359.859 81.34H363.844' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M420.52 81.34V81.34' fill='#339999'/>
+<path clip-path='url(#clip1)' d='M418.527 81.34H422.515' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M420.52 81.34V81.34' fill='#339999'/>
+<path clip-path='url(#clip1)' d='M418.527 81.34H422.515' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M130.789 206.586H134.024V136.508H130.789ZM189.457 206.586H192.695V143.023H189.457ZM248.125 206.586H251.363V139.391H248.125ZM306.793 206.586H310.031V134.191H306.793ZM365.465 206.586H368.699V81.34H365.465ZM424.133 206.586H427.371V85.348H424.133Z' fill='#bf8080'/>
+<path clip-path='url(#clip1)' d='M130.789 206.586H134.024V136.508H130.789ZM189.457 206.586H192.695V143.023H189.457ZM248.125 206.586H251.363V139.391H248.125ZM306.793 206.586H310.031V134.191H306.793ZM365.465 206.586H368.699V81.34H365.465ZM424.133 206.586H427.371V85.348H424.133Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M132.406 136.508V136.445' fill='#bf8080'/>
+<path clip-path='url(#clip1)' d='M132.406 136.508V136.445' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M130.414 136.445H134.399' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M132.406 136.508V136.57' fill='#bf8080'/>
+<path clip-path='url(#clip1)' d='M132.406 136.508V136.57' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M134.399 136.571H130.414' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M191.074 143.023V142.582' fill='#bf8080'/>
+<path clip-path='url(#clip1)' d='M191.074 143.023V142.582' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M189.082 142.582H193.067' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M191.074 143.023V143.461' fill='#bf8080'/>
+<path clip-path='url(#clip1)' d='M191.074 143.023V143.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M193.067 143.461H189.082' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M249.746 139.391V136.383' fill='#bf8080'/>
+<path clip-path='url(#clip1)' d='M249.746 139.391V136.383' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M247.754 136.383H251.739' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M249.746 139.391V142.394' fill='#bf8080'/>
+<path clip-path='url(#clip1)' d='M249.746 139.391V142.394' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M251.735 142.395H247.75' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M308.414 134.191V131.875' fill='#bf8080'/>
+<path clip-path='url(#clip1)' d='M308.414 134.191V131.875' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M306.422 131.875H310.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M308.414 134.191V136.508' fill='#bf8080'/>
+<path clip-path='url(#clip1)' d='M308.414 134.191V136.508' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M310.407 136.508H306.422' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M367.082 81.34V81.34' fill='#bf8080'/>
+<path clip-path='url(#clip1)' d='M365.09 81.34H369.075' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M367.082 81.34V81.34' fill='#bf8080'/>
+<path clip-path='url(#clip1)' d='M365.09 81.34H369.075' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M425.75 85.348V72.633' fill='#bf8080'/>
+<path clip-path='url(#clip1)' d='M425.75 85.348V72.633' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M423.758 72.633H427.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M425.75 85.348V98.058' fill='#bf8080'/>
+<path clip-path='url(#clip1)' d='M425.75 85.348V98.058' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M427.743 98.059H423.758' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(0 -1 1 0 -93.83 252.423)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-48' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -35.161 252.423)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-48' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 23.508 252.423)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-48' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 82.177 252.423)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-48' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 140.846 252.423)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-48' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 199.515 252.423)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-48' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -88.6 250.983)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-50' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -29.93 248.666)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-54' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 28.739 256.807)'>
+<use x='114.487' xlink:href='#g2-48' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-57' y='186.027'/>
+<use x='121.25' xlink:href='#g2-51' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 87.408 229.879)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-51' y='186.027'/>
+<use x='121.25' xlink:href='#g2-54' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 146.077 243.531)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-49' y='186.027'/>
+<use x='121.25' xlink:href='#g2-52' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 204.746 189.8)'>
+<use x='109.598' xlink:href='#g4-1' y='186.027'/>
+<use x='113.103' xlink:href='#g4-1' y='186.027'/>
+<use x='116.608' xlink:href='#g4-1' y='186.027'/>
+<use x='120.114' xlink:href='#g2-53' y='186.027'/>
+<use x='122.76' xlink:href='#g2-46' y='186.027'/>
+<use x='124.23' xlink:href='#g2-56' y='186.027'/>
+<use x='126.877' xlink:href='#g2-50' y='186.027'/>
+<use x='129.523' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -83.369 248.729)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-54' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -24.7 251.359)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-50' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 33.969 243.218)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-49' y='186.027'/>
+<use x='121.25' xlink:href='#g2-53' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 92.638 207.711)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-55' y='186.027'/>
+<use x='121.25' xlink:href='#g2-49' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 151.307 189.8)'>
+<use x='109.598' xlink:href='#g4-1' y='186.027'/>
+<use x='113.103' xlink:href='#g4-1' y='186.027'/>
+<use x='116.608' xlink:href='#g4-1' y='186.027'/>
+<use x='120.114' xlink:href='#g2-50' y='186.027'/>
+<use x='122.76' xlink:href='#g2-46' y='186.027'/>
+<use x='124.23' xlink:href='#g2-51' y='186.027'/>
+<use x='126.877' xlink:href='#g2-54' y='186.027'/>
+<use x='129.523' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 209.976 189.8)'>
+<use x='109.598' xlink:href='#g4-1' y='186.027'/>
+<use x='113.103' xlink:href='#g4-1' y='186.027'/>
+<use x='116.608' xlink:href='#g4-1' y='186.027'/>
+<use x='120.114' xlink:href='#g2-50' y='186.027'/>
+<use x='122.76' xlink:href='#g2-46' y='186.027'/>
+<use x='124.23' xlink:href='#g2-57' y='186.027'/>
+<use x='126.877' xlink:href='#g2-49' y='186.027'/>
+<use x='129.523' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -78.139 233.323)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-51' y='186.027'/>
+<use x='121.25' xlink:href='#g2-49' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -19.47 249.668)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-52' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 39.199 226.497)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-52' y='186.027'/>
+<use x='121.25' xlink:href='#g2-49' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 97.868 229.503)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-51' y='186.027'/>
+<use x='121.25' xlink:href='#g2-55' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 156.537 241.527)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-49' y='186.027'/>
+<use x='121.25' xlink:href='#g2-55' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 215.206 197.503)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-56' y='186.027'/>
+<use x='121.25' xlink:href='#g2-56' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -72.908 250.795)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-51' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -14.239 251.547)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-49' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 44.43 240.588)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-49' y='186.027'/>
+<use x='121.25' xlink:href='#g2-57' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 103.099 248.102)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-55' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 161.768 189.8)'>
+<use x='109.598' xlink:href='#g4-1' y='186.027'/>
+<use x='113.103' xlink:href='#g4-1' y='186.027'/>
+<use x='116.608' xlink:href='#g4-1' y='186.027'/>
+<use x='120.114' xlink:href='#g2-50' y='186.027'/>
+<use x='122.76' xlink:href='#g2-46' y='186.027'/>
+<use x='124.23' xlink:href='#g2-49' y='186.027'/>
+<use x='126.877' xlink:href='#g2-54' y='186.027'/>
+<use x='129.523' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 220.437 208.65)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-55' y='186.027'/>
+<use x='121.25' xlink:href='#g2-48' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -67.678 244.032)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-49' y='186.027'/>
+<use x='121.25' xlink:href='#g2-51' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -9.009 244.533)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-49' y='186.027'/>
+<use x='121.25' xlink:href='#g2-51' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 49.66 223.429)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-52' y='186.027'/>
+<use x='121.25' xlink:href='#g2-54' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 108.329 222.302)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-52' y='186.027'/>
+<use x='121.25' xlink:href='#g2-56' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 166.998 189.8)'>
+<use x='109.598' xlink:href='#g4-1' y='186.027'/>
+<use x='113.103' xlink:href='#g4-1' y='186.027'/>
+<use x='116.608' xlink:href='#g4-1' y='186.027'/>
+<use x='120.114' xlink:href='#g2-56' y='186.027'/>
+<use x='122.76' xlink:href='#g2-46' y='186.027'/>
+<use x='124.23' xlink:href='#g2-52' y='186.027'/>
+<use x='126.877' xlink:href='#g2-50' y='186.027'/>
+<use x='129.523' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 225.667 189.8)'>
+<use x='109.598' xlink:href='#g4-1' y='186.027'/>
+<use x='113.103' xlink:href='#g4-1' y='186.027'/>
+<use x='116.608' xlink:href='#g4-1' y='186.027'/>
+<use x='120.114' xlink:href='#g2-57' y='186.027'/>
+<use x='122.76' xlink:href='#g2-46' y='186.027'/>
+<use x='124.23' xlink:href='#g2-57' y='186.027'/>
+<use x='126.877' xlink:href='#g2-51' y='186.027'/>
+<use x='129.523' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -62.447 244.095)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-49' y='186.027'/>
+<use x='121.25' xlink:href='#g2-51' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -3.778 189.8)'>
+<use x='109.598' xlink:href='#g4-1' y='186.027'/>
+<use x='113.103' xlink:href='#g4-1' y='186.027'/>
+<use x='116.608' xlink:href='#g4-1' y='186.027'/>
+<use x='120.114' xlink:href='#g2-51' y='186.027'/>
+<use x='122.76' xlink:href='#g2-46' y='186.027'/>
+<use x='124.23' xlink:href='#g2-54' y='186.027'/>
+<use x='126.877' xlink:href='#g2-51' y='186.027'/>
+<use x='129.523' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 54.891 246.85)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-57' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 113.56 189.8)'>
+<use x='109.598' xlink:href='#g4-1' y='186.027'/>
+<use x='113.103' xlink:href='#g4-1' y='186.027'/>
+<use x='116.608' xlink:href='#g4-1' y='186.027'/>
+<use x='120.114' xlink:href='#g2-50' y='186.027'/>
+<use x='122.76' xlink:href='#g2-51' y='186.027'/>
+<use x='125.406' xlink:href='#g2-46' y='186.027'/>
+<use x='126.877' xlink:href='#g2-57' y='186.027'/>
+<use x='129.523' xlink:href='#g2-57' y='186.027'/>
+<use x='132.169' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 172.229 189.8)'>
+<use x='109.598' xlink:href='#g4-1' y='186.027'/>
+<use x='113.103' xlink:href='#g4-1' y='186.027'/>
+<use x='116.608' xlink:href='#g4-1' y='186.027'/>
+<use x='120.114' xlink:href='#g2-49' y='186.027'/>
+<use x='122.76' xlink:href='#g2-56' y='186.027'/>
+<use x='125.406' xlink:href='#g2-46' y='186.027'/>
+<use x='126.877' xlink:href='#g2-53' y='186.027'/>
+<use x='129.523' xlink:href='#g2-52' y='186.027'/>
+<use x='132.169' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 230.898 189.8)'>
+<use x='109.598' xlink:href='#g4-1' y='186.027'/>
+<use x='113.103' xlink:href='#g4-1' y='186.027'/>
+<use x='116.608' xlink:href='#g4-1' y='186.027'/>
+<use x='120.114' xlink:href='#g2-51' y='186.027'/>
+<use x='122.76' xlink:href='#g2-52' y='186.027'/>
+<use x='125.406' xlink:href='#g2-46' y='186.027'/>
+<use x='126.877' xlink:href='#g2-52' y='186.027'/>
+<use x='129.523' xlink:href='#g2-53' y='186.027'/>
+<use x='132.169' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -57.217 247.727)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-56' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 1.452 244.157)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-49' y='186.027'/>
+<use x='121.25' xlink:href='#g2-51' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 60.121 246.286)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-49' y='186.027'/>
+<use x='121.25' xlink:href='#g2-48' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 118.79 205.895)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-55' y='186.027'/>
+<use x='121.25' xlink:href='#g2-52' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 177.459 189.8)'>
+<use x='109.598' xlink:href='#g4-1' y='186.027'/>
+<use x='113.103' xlink:href='#g4-1' y='186.027'/>
+<use x='116.608' xlink:href='#g4-1' y='186.027'/>
+<use x='120.114' xlink:href='#g2-52' y='186.027'/>
+<use x='122.76' xlink:href='#g2-46' y='186.027'/>
+<use x='124.23' xlink:href='#g2-51' y='186.027'/>
+<use x='126.877' xlink:href='#g2-51' y='186.027'/>
+<use x='129.523' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 236.128 189.8)'>
+<use x='109.598' xlink:href='#g4-1' y='186.027'/>
+<use x='113.103' xlink:href='#g4-1' y='186.027'/>
+<use x='116.608' xlink:href='#g4-1' y='186.027'/>
+<use x='120.114' xlink:href='#g2-50' y='186.027'/>
+<use x='122.76' xlink:href='#g2-46' y='186.027'/>
+<use x='124.23' xlink:href='#g2-49' y='186.027'/>
+<use x='126.877' xlink:href='#g2-50' y='186.027'/>
+<use x='129.523' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -51.986 244.971)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-49' y='186.027'/>
+<use x='121.25' xlink:href='#g2-50' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 6.683 251.484)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-50' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 65.352 247.852)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-48' y='186.027'/>
+<use x='121.25' xlink:href='#g2-55' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 124.021 242.654)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-49' y='186.027'/>
+<use x='121.25' xlink:href='#g2-54' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 182.69 189.8)'>
+<use x='109.598' xlink:href='#g4-1' y='186.027'/>
+<use x='113.103' xlink:href='#g4-1' y='186.027'/>
+<use x='116.608' xlink:href='#g4-1' y='186.027'/>
+<use x='120.114' xlink:href='#g2-50' y='186.027'/>
+<use x='122.76' xlink:href='#g2-46' y='186.027'/>
+<use x='124.23' xlink:href='#g2-50' y='186.027'/>
+<use x='126.877' xlink:href='#g2-55' y='186.027'/>
+<use x='129.523' xlink:href='#g2-120' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 241.359 193.808)'>
+<use x='114.487' xlink:href='#g2-49' y='186.027'/>
+<use x='117.133' xlink:href='#g2-46' y='186.027'/>
+<use x='118.603' xlink:href='#g2-57' y='186.027'/>
+<use x='121.25' xlink:href='#g2-52' y='186.027'/>
+</g>
+<g transform='matrix(0 -1 1 0 -126.667 313.708)'>
+<use x='114.487' xlink:href='#g1-82' y='186.027'/>
+<use x='120.457' xlink:href='#g1-101' y='186.027'/>
+<use x='124.553' xlink:href='#g1-108' y='186.027'/>
+<use x='126.753' xlink:href='#g1-97' y='186.027'/>
+<use x='131.181' xlink:href='#g1-116' y='186.027'/>
+<use x='134.509' xlink:href='#g1-105' y='186.027'/>
+<use x='136.709' xlink:href='#g1-118' y='186.027'/>
+<use x='140.957' xlink:href='#g1-101' y='186.027'/>
+<use x='148.124' xlink:href='#g1-116' y='186.027'/>
+<use x='151.452' xlink:href='#g1-105' y='186.027'/>
+<use x='153.652' xlink:href='#g1-109' y='186.027'/>
+<use x='160.972' xlink:href='#g1-101' y='186.027'/>
+<use x='168.139' xlink:href='#g3-40' y='186.027'/>
+<use x='171.432' xlink:href='#g3-108' y='186.027'/>
+<use x='173.453' xlink:href='#g3-111' y='186.027'/>
+<use x='177.452' xlink:href='#g3-119' y='186.027'/>
+<use x='183' xlink:href='#g3-101' y='186.027'/>
+<use x='186.764' xlink:href='#g3-114' y='186.027'/>
+<use x='192.479' xlink:href='#g3-105' y='186.027'/>
+<use x='194.499' xlink:href='#g3-115' y='186.027'/>
+<use x='200.568' xlink:href='#g3-98' y='186.027'/>
+<use x='205.176' xlink:href='#g3-101' y='186.027'/>
+<use x='208.94' xlink:href='#g3-116' y='186.027'/>
+<use x='211.998' xlink:href='#g3-116' y='186.027'/>
+<use x='215.056' xlink:href='#g3-101' y='186.027'/>
+<use x='218.819' xlink:href='#g3-114' y='186.027'/>
+<use x='221.711' xlink:href='#g3-41' y='186.027'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='258.383pt' version='1.1' viewBox='106.736 54.996 381.623 258.383' width='381.623pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip2'>
+<path d='M135.949 251.93H487.961V84.164H135.949Z'/>
+</clipPath>
+<use id='g3-40' transform='scale(1.143)' xlink:href='#g0-40'/>
+<use id='g3-41' transform='scale(1.143)' xlink:href='#g0-41'/>
+<use id='g3-45' transform='scale(1.143)' xlink:href='#g0-45'/>
+<use id='g3-49' transform='scale(1.143)' xlink:href='#g0-49'/>
+<use id='g3-54' transform='scale(1.143)' xlink:href='#g0-54'/>
+<use id='g3-56' transform='scale(1.143)' xlink:href='#g0-56'/>
+<use id='g3-58' transform='scale(1.143)' xlink:href='#g0-58'/>
+<use id='g3-78' transform='scale(1.143)' xlink:href='#g0-78'/>
+<use id='g3-97' transform='scale(1.143)' xlink:href='#g0-97'/>
+<use id='g3-98' transform='scale(1.143)' xlink:href='#g0-98'/>
+<use id='g3-99' transform='scale(1.143)' xlink:href='#g0-99'/>
+<use id='g3-100' transform='scale(1.143)' xlink:href='#g0-100'/>
+<use id='g3-101' transform='scale(1.143)' xlink:href='#g0-101'/>
+<use id='g3-103' transform='scale(1.143)' xlink:href='#g0-103'/>
+<use id='g3-104' transform='scale(1.143)' xlink:href='#g0-104'/>
+<use id='g3-105' transform='scale(1.143)' xlink:href='#g0-105'/>
+<use id='g3-106' transform='scale(1.143)' xlink:href='#g0-106'/>
+<use id='g3-108' transform='scale(1.143)' xlink:href='#g0-108'/>
+<use id='g3-109' transform='scale(1.143)' xlink:href='#g0-109'/>
+<use id='g3-110' transform='scale(1.143)' xlink:href='#g0-110'/>
+<use id='g3-111' transform='scale(1.143)' xlink:href='#g0-111'/>
+<use id='g3-112' transform='scale(1.143)' xlink:href='#g0-112'/>
+<use id='g3-114' transform='scale(1.143)' xlink:href='#g0-114'/>
+<use id='g3-115' transform='scale(1.143)' xlink:href='#g0-115'/>
+<use id='g3-116' transform='scale(1.143)' xlink:href='#g0-116'/>
+<use id='g3-119' transform='scale(1.143)' xlink:href='#g0-119'/>
+<use id='g3-120' transform='scale(1.143)' xlink:href='#g0-120'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g1-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g1-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g1-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g1-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g1-108'/>
+<path d='M6.581 -2.663C6.581 -3.327 6.402 -4.08 5.317 -4.08C4.564 -4.08 4.142 -3.622 3.927 -3.344C3.865 -3.524 3.676 -4.08 2.762 -4.08C2.053 -4.08 1.623 -3.667 1.417 -3.398V-4.035H0.726V0H1.47V-2.188C1.47 -2.78 1.704 -3.497 2.385 -3.497C3.282 -3.497 3.282 -2.86 3.282 -2.6V0H4.026V-2.188C4.026 -2.78 4.259 -3.497 4.94 -3.497C5.837 -3.497 5.837 -2.86 5.837 -2.6V0H6.581V-2.663Z' id='g1-109'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g1-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g1-118'/>
+<use id='g2-44' transform='scale(0.714)' xlink:href='#g0-44'/>
+<use id='g2-45' transform='scale(0.714)' xlink:href='#g0-45'/>
+<use id='g2-46' transform='scale(0.714)' xlink:href='#g0-46'/>
+<use id='g2-48' transform='scale(0.714)' xlink:href='#g0-48'/>
+<use id='g2-49' transform='scale(0.714)' xlink:href='#g0-49'/>
+<use id='g2-50' transform='scale(0.714)' xlink:href='#g0-50'/>
+<use id='g2-51' transform='scale(0.714)' xlink:href='#g0-51'/>
+<use id='g2-52' transform='scale(0.714)' xlink:href='#g0-52'/>
+<use id='g2-53' transform='scale(0.714)' xlink:href='#g0-53'/>
+<use id='g2-54' transform='scale(0.714)' xlink:href='#g0-54'/>
+<use id='g2-55' transform='scale(0.714)' xlink:href='#g0-55'/>
+<use id='g2-56' transform='scale(0.714)' xlink:href='#g0-56'/>
+<use id='g2-57' transform='scale(0.714)' xlink:href='#g0-57'/>
+<use id='g2-64' transform='scale(0.714)' xlink:href='#g0-64'/>
+<use id='g2-67' transform='scale(0.714)' xlink:href='#g0-67'/>
+<use id='g2-71' transform='scale(0.714)' xlink:href='#g0-71'/>
+<use id='g2-73' transform='scale(0.714)' xlink:href='#g0-73'/>
+<use id='g2-85' transform='scale(0.714)' xlink:href='#g0-85'/>
+<use id='g2-88' transform='scale(0.714)' xlink:href='#g0-88'/>
+<use id='g2-97' transform='scale(0.714)' xlink:href='#g0-97'/>
+<use id='g2-98' transform='scale(0.714)' xlink:href='#g0-98'/>
+<use id='g2-99' transform='scale(0.714)' xlink:href='#g0-99'/>
+<use id='g2-100' transform='scale(0.714)' xlink:href='#g0-100'/>
+<use id='g2-101' transform='scale(0.714)' xlink:href='#g0-101'/>
+<use id='g2-103' transform='scale(0.714)' xlink:href='#g0-103'/>
+<use id='g2-104' transform='scale(0.714)' xlink:href='#g0-104'/>
+<use id='g2-108' transform='scale(0.714)' xlink:href='#g0-108'/>
+<use id='g2-109' transform='scale(0.714)' xlink:href='#g0-109'/>
+<use id='g2-110' transform='scale(0.714)' xlink:href='#g0-110'/>
+<use id='g2-111' transform='scale(0.714)' xlink:href='#g0-111'/>
+<use id='g2-112' transform='scale(0.714)' xlink:href='#g0-112'/>
+<use id='g2-114' transform='scale(0.714)' xlink:href='#g0-114'/>
+<use id='g2-115' transform='scale(0.714)' xlink:href='#g0-115'/>
+<use id='g2-116' transform='scale(0.714)' xlink:href='#g0-116'/>
+<use id='g2-117' transform='scale(0.714)' xlink:href='#g0-117'/>
+<use id='g2-120' transform='scale(0.714)' xlink:href='#g0-120'/>
+<use id='g2-122' transform='scale(0.714)' xlink:href='#g0-122'/>
+<path d='M1.445 -1.245C1.445 -1.41 1.305 -1.549 1.141 -1.549S0.837 -1.41 0.837 -1.245S0.976 -0.941 1.141 -0.941S1.445 -1.081 1.445 -1.245Z' id='g4-1'/>
+<path d='M2.127 -5.23C2.008 -5.23 1.995 -5.23 1.911 -5.154C1.032 -4.387 0.586 -3.145 0.586 -1.743C0.586 -0.425 0.983 0.844 1.904 1.653C1.995 1.743 2.008 1.743 2.127 1.743H2.462C2.441 1.73 1.764 1.151 1.444 0.063C1.276 -0.481 1.193 -1.053 1.193 -1.743C1.193 -4.156 2.322 -5.112 2.462 -5.23H2.127Z' id='g0-40'/>
+<path d='M0.746 1.743C0.865 1.743 0.879 1.743 0.962 1.667C1.841 0.9 2.287 -0.342 2.287 -1.743C2.287 -3.062 1.89 -4.331 0.969 -5.14C0.879 -5.23 0.865 -5.23 0.746 -5.23H0.411C0.432 -5.216 1.109 -4.638 1.43 -3.55C1.597 -3.006 1.681 -2.434 1.681 -1.743C1.681 0.669 0.551 1.625 0.411 1.743H0.746Z' id='g0-41'/>
+<path d='M1.339 -0.007V-0.628H0.711V0H0.907L0.704 0.893H1.018L1.339 -0.007Z' id='g0-44'/>
+<path d='M2.05 -1.332V-1.771H0.084V-1.332H2.05Z' id='g0-45'/>
+<path d='M1.339 -0.628H0.711V0H1.339V-0.628Z' id='g0-46'/>
+<path d='M3.403 -2.267C3.403 -2.601 3.403 -3.417 3.075 -3.989C2.72 -4.617 2.183 -4.721 1.848 -4.721C1.534 -4.721 0.99 -4.624 0.642 -4.024C0.307 -3.466 0.293 -2.706 0.293 -2.267C0.293 -1.75 0.321 -1.116 0.614 -0.586C0.921 -0.021 1.437 0.146 1.848 0.146C2.545 0.146 2.929 -0.258 3.138 -0.697C3.382 -1.193 3.403 -1.834 3.403 -2.267ZM1.848 -0.314C1.555 -0.314 1.22 -0.481 1.046 -0.983C0.907 -1.409 0.9 -1.848 0.9 -2.357C0.9 -2.999 0.9 -4.261 1.848 -4.261S2.797 -2.999 2.797 -2.357C2.797 -1.897 2.797 -1.374 2.629 -0.928C2.434 -0.425 2.078 -0.314 1.848 -0.314Z' id='g0-48'/>
+<path d='M2.239 -4.721H2.085C1.632 -4.303 1.06 -4.275 0.642 -4.261V-3.822C0.914 -3.829 1.262 -3.843 1.611 -3.982V-0.439H0.683V0H3.166V-0.439H2.239V-4.721Z' id='g0-49'/>
+<path d='M1.974 -0.537C1.89 -0.537 1.806 -0.53 1.723 -0.53H0.928L2.008 -1.485C2.134 -1.597 2.476 -1.855 2.608 -1.967C2.915 -2.246 3.327 -2.608 3.327 -3.215C3.327 -4.003 2.741 -4.721 1.743 -4.721C1.004 -4.721 0.544 -4.324 0.307 -3.612L0.635 -3.201C0.795 -3.787 1.039 -4.24 1.646 -4.24C2.232 -4.24 2.678 -3.829 2.678 -3.201C2.678 -2.622 2.336 -2.294 1.918 -1.897C1.778 -1.757 1.402 -1.444 1.255 -1.304C1.053 -1.123 0.572 -0.656 0.37 -0.481V0H3.327V-0.537H1.974Z' id='g0-50'/>
+<path d='M0.697 -3.578C0.983 -4.135 1.485 -4.289 1.82 -4.289C2.232 -4.289 2.538 -4.052 2.538 -3.654C2.538 -3.285 2.287 -2.831 1.757 -2.741C1.723 -2.734 1.695 -2.734 1.234 -2.699V-2.239H1.778C2.441 -2.239 2.685 -1.716 2.685 -1.276C2.685 -0.732 2.35 -0.314 1.806 -0.314C1.311 -0.314 0.746 -0.551 0.398 -0.997L0.307 -0.544C0.711 -0.091 1.276 0.146 1.82 0.146C2.734 0.146 3.389 -0.537 3.389 -1.269C3.389 -1.841 2.929 -2.301 2.378 -2.462C2.908 -2.734 3.18 -3.201 3.18 -3.654C3.18 -4.247 2.573 -4.721 1.827 -4.721C1.213 -4.721 0.704 -4.4 0.411 -3.982L0.697 -3.578Z' id='g0-51'/>
+<path d='M2.762 -1.165H3.487V-1.625H2.762V-4.575H2.071L0.209 -1.625V-1.165H2.162V0H2.762V-1.165ZM0.802 -1.625C1.011 -1.953 2.211 -3.815 2.211 -4.233V-1.625H0.802Z' id='g0-52'/>
+<path d='M1.144 -4.094H3.075V-4.575H0.586V-1.967H1.095C1.262 -2.343 1.59 -2.511 1.904 -2.511C2.19 -2.511 2.622 -2.315 2.622 -1.43C2.622 -0.516 2.043 -0.314 1.688 -0.314C1.227 -0.314 0.781 -0.558 0.544 -0.955L0.279 -0.537C0.621 -0.112 1.137 0.146 1.688 0.146C2.608 0.146 3.327 -0.565 3.327 -1.416C3.327 -2.28 2.685 -2.971 1.918 -2.971C1.618 -2.971 1.353 -2.866 1.144 -2.692V-4.094Z' id='g0-53'/>
+<path d='M3.062 -4.582C2.685 -4.721 2.42 -4.721 2.287 -4.721C1.227 -4.721 0.307 -3.724 0.307 -2.253C0.307 -0.363 1.158 0.146 1.862 0.146C2.427 0.146 2.72 -0.119 2.936 -0.342C3.382 -0.816 3.389 -1.311 3.389 -1.555C3.389 -2.469 2.894 -3.229 2.218 -3.229C1.534 -3.229 1.165 -2.873 0.962 -2.671C1.053 -3.626 1.541 -4.289 2.294 -4.289C2.434 -4.289 2.713 -4.275 3.062 -4.142V-4.582ZM0.969 -1.534C0.969 -1.576 0.969 -1.681 0.976 -1.716C0.976 -2.19 1.276 -2.769 1.897 -2.769C2.748 -2.769 2.748 -1.792 2.748 -1.555C2.748 -1.29 2.748 -0.997 2.559 -0.704C2.392 -0.453 2.183 -0.314 1.862 -0.314C1.123 -0.314 1.004 -1.227 0.969 -1.534Z' id='g0-54'/>
+<path d='M1.723 -4.038C1.806 -4.038 1.89 -4.045 1.974 -4.045H2.852C1.792 -3.006 1.116 -1.548 1.116 0.07H1.771C1.771 -1.967 2.762 -3.431 3.389 -4.087V-4.575H0.307V-4.038H1.723Z' id='g0-55'/>
+<path d='M2.385 -2.469C2.845 -2.615 3.285 -2.985 3.285 -3.501C3.285 -4.135 2.678 -4.721 1.848 -4.721S0.411 -4.135 0.411 -3.501C0.411 -2.978 0.865 -2.608 1.311 -2.469C0.697 -2.28 0.307 -1.806 0.307 -1.269C0.307 -0.523 0.969 0.146 1.848 0.146S3.389 -0.523 3.389 -1.269C3.389 -1.806 2.992 -2.28 2.385 -2.469ZM1.848 -2.699C1.353 -2.699 0.948 -2.985 0.948 -3.494C0.948 -3.94 1.262 -4.289 1.848 -4.289C2.427 -4.289 2.748 -3.94 2.748 -3.494C2.748 -2.999 2.357 -2.699 1.848 -2.699ZM1.848 -0.314C1.367 -0.314 0.941 -0.621 0.941 -1.276C0.941 -1.904 1.346 -2.239 1.848 -2.239S2.755 -1.897 2.755 -1.276C2.755 -0.621 2.322 -0.314 1.848 -0.314Z' id='g0-56'/>
+<path d='M0.537 -0.174C0.879 0.077 1.193 0.146 1.52 0.146C2.497 0.146 3.389 -0.837 3.389 -2.336C3.389 -4.24 2.545 -4.721 1.876 -4.721C1.255 -4.721 0.969 -4.428 0.767 -4.226C0.321 -3.773 0.307 -3.292 0.307 -3.02C0.307 -2.12 0.795 -1.346 1.478 -1.346C2.267 -1.346 2.699 -1.869 2.734 -1.911C2.636 -0.802 2.092 -0.314 1.52 -0.314C1.158 -0.314 0.934 -0.446 0.774 -0.579L0.537 -0.174ZM2.713 -3.027C2.72 -2.985 2.72 -2.915 2.72 -2.873C2.72 -2.357 2.406 -1.806 1.799 -1.806C1.534 -1.806 1.325 -1.883 1.144 -2.169C0.962 -2.441 0.948 -2.706 0.948 -3.02C0.948 -3.292 0.948 -3.605 1.165 -3.912C1.311 -4.122 1.52 -4.289 1.869 -4.289C2.545 -4.289 2.692 -3.473 2.713 -3.027Z' id='g0-57'/>
+<path d='M1.339 -3.096H0.711V-2.469H1.339V-3.096ZM0.711 -0.628V0H1.339V-0.628H0.711Z' id='g0-58'/>
+<path d='M4.142 -0.614C4.038 -0.614 4.024 -0.614 3.968 -0.586C3.626 -0.467 3.271 -0.391 2.901 -0.391C1.778 -0.391 0.976 -1.339 0.976 -2.42C0.976 -3.592 1.883 -4.449 2.859 -4.449C3.055 -4.449 3.515 -4.4 3.745 -3.843C3.55 -3.954 3.333 -4.003 3.152 -4.003C2.406 -4.003 1.778 -3.306 1.778 -2.42C1.778 -1.513 2.427 -0.837 3.145 -0.837C3.689 -0.837 4.519 -1.276 4.519 -2.518C4.519 -3.222 4.47 -4.91 2.866 -4.91C1.541 -4.91 0.411 -3.815 0.411 -2.42C0.411 -1.039 1.527 0.07 2.873 0.07C3.515 0.07 4.115 -0.195 4.519 -0.614H4.142ZM3.152 -1.297C2.72 -1.297 2.343 -1.778 2.343 -2.42C2.343 -3.082 2.734 -3.543 3.145 -3.543C3.578 -3.543 3.954 -3.062 3.954 -2.42C3.954 -1.757 3.564 -1.297 3.152 -1.297Z' id='g0-64'/>
+<path d='M4.317 -0.851C3.829 -0.551 3.605 -0.418 2.908 -0.418C1.827 -0.418 1.172 -1.43 1.172 -2.434C1.172 -3.466 1.89 -4.435 2.908 -4.435C3.368 -4.435 3.843 -4.289 4.163 -4.045L4.275 -4.679C3.787 -4.861 3.396 -4.917 2.887 -4.917C1.506 -4.917 0.474 -3.773 0.474 -2.427C0.474 -0.99 1.569 0.07 2.929 0.07C3.612 0.07 3.898 -0.07 4.359 -0.321L4.317 -0.851Z' id='g0-67'/>
+<path d='M4.442 -2.085H2.88V-1.625H3.829V-0.558C3.522 -0.481 3.222 -0.418 2.908 -0.418C1.834 -0.418 1.172 -1.43 1.172 -2.427C1.172 -3.382 1.82 -4.435 2.873 -4.435C3.515 -4.435 3.919 -4.24 4.268 -3.947L4.38 -4.582C3.898 -4.812 3.473 -4.924 2.943 -4.924C1.534 -4.924 0.474 -3.822 0.474 -2.427C0.474 -1.067 1.527 0.07 2.901 0.07C3.403 0.07 3.996 -0.042 4.442 -0.272V-2.085Z' id='g0-71'/>
+<path d='M1.381 -4.84H0.676V0H1.381V-4.84Z' id='g0-73'/>
+<path d='M1.646 -4.84H0.697V0H1.283V-4.289H1.29L3.578 0H4.526V-4.84H3.94V-0.551H3.933L1.646 -4.84Z' id='g0-78'/>
+<path d='M4.4 -4.84H3.794V-1.625C3.794 -0.69 3.166 -0.265 2.566 -0.265S1.381 -0.697 1.381 -1.618V-4.84H0.676V-1.632C0.676 -0.607 1.555 0.146 2.559 0.146C3.557 0.146 4.4 -0.614 4.4 -1.632V-4.84Z' id='g0-85'/>
+<path d='M2.755 -2.552L4.519 -4.84H3.759L2.413 -3.055L1.039 -4.84H0.209L2.071 -2.552L0.105 0H0.865L2.413 -2.099L3.996 0H4.826L2.755 -2.552Z' id='g0-88'/>
+<path d='M2.971 -2.008C2.971 -2.72 2.427 -3.201 1.736 -3.201C1.297 -3.201 0.962 -3.11 0.572 -2.901L0.614 -2.392C0.844 -2.545 1.186 -2.755 1.736 -2.755C2.043 -2.755 2.364 -2.525 2.364 -2.001V-1.723C1.332 -1.688 0.314 -1.471 0.314 -0.823C0.314 -0.474 0.551 0.07 1.165 0.07C1.465 0.07 2.015 0.007 2.385 -0.265V0H2.971V-2.008ZM2.364 -0.99C2.364 -0.851 2.364 -0.669 2.12 -0.523C1.897 -0.398 1.625 -0.391 1.548 -0.391C1.165 -0.391 0.872 -0.565 0.872 -0.83C0.872 -1.276 2.05 -1.318 2.364 -1.332V-0.99Z' id='g0-97'/>
+<path d='M1.179 -4.84H0.593V0H1.2V-0.328C1.353 -0.195 1.688 0.07 2.197 0.07C2.957 0.07 3.571 -0.642 3.571 -1.555C3.571 -2.399 3.089 -3.166 2.392 -3.166C1.953 -3.166 1.527 -3.027 1.179 -2.769V-4.84ZM1.2 -2.197C1.2 -2.308 1.2 -2.392 1.444 -2.552C1.548 -2.615 1.736 -2.706 1.974 -2.706C2.441 -2.706 2.964 -2.392 2.964 -1.555C2.964 -0.704 2.385 -0.391 1.897 -0.391C1.639 -0.391 1.395 -0.509 1.2 -0.823V-2.197Z' id='g0-98'/>
+<path d='M3.034 -0.76C2.685 -0.537 2.308 -0.411 1.876 -0.411C1.234 -0.411 0.858 -0.928 0.858 -1.555C0.858 -2.092 1.137 -2.72 1.897 -2.72C2.371 -2.72 2.594 -2.622 2.95 -2.399L3.041 -2.901C2.622 -3.11 2.441 -3.201 1.897 -3.201C0.851 -3.201 0.251 -2.357 0.251 -1.548C0.251 -0.697 0.921 0.07 1.869 0.07C2.357 0.07 2.776 -0.077 3.075 -0.251L3.034 -0.76Z' id='g0-99'/>
+<path d='M3.229 -4.84H2.643V-2.797C2.197 -3.124 1.743 -3.166 1.541 -3.166C0.809 -3.166 0.251 -2.434 0.251 -1.548S0.802 0.07 1.52 0.07C1.953 0.07 2.357 -0.126 2.622 -0.363V0H3.229V-4.84ZM2.622 -0.865C2.448 -0.579 2.183 -0.391 1.848 -0.391C1.36 -0.391 0.858 -0.732 0.858 -1.541C0.858 -2.413 1.451 -2.706 1.925 -2.706C2.204 -2.706 2.441 -2.587 2.622 -2.35V-0.865Z' id='g0-100'/>
+<path d='M2.999 -0.76C2.608 -0.481 2.169 -0.391 1.869 -0.391C1.262 -0.391 0.802 -0.886 0.781 -1.527H3.068C3.068 -1.848 3.034 -2.315 2.762 -2.713C2.511 -3.068 2.092 -3.201 1.75 -3.201C0.9 -3.201 0.244 -2.455 0.244 -1.569C0.244 -0.676 0.941 0.07 1.862 0.07C2.267 0.07 2.685 -0.049 3.041 -0.265L2.999 -0.76ZM0.83 -1.946C0.99 -2.504 1.402 -2.741 1.75 -2.741C2.057 -2.741 2.511 -2.594 2.643 -1.946H0.83Z' id='g0-101'/>
+<path d='M3.508 -3.166C3.354 -3.166 2.887 -3.159 2.357 -2.957L2.343 -2.95C2.092 -3.117 1.848 -3.166 1.646 -3.166C0.962 -3.166 0.453 -2.629 0.453 -2.029C0.453 -1.785 0.537 -1.534 0.697 -1.339C0.6 -1.22 0.495 -1.025 0.495 -0.76C0.495 -0.488 0.607 -0.314 0.669 -0.23C0.286 -0.007 0.209 0.314 0.209 0.481C0.209 1.011 0.941 1.43 1.848 1.43C2.762 1.43 3.487 1.011 3.487 0.481C3.487 -0.502 2.267 -0.502 1.967 -0.502H1.318C1.206 -0.502 0.907 -0.502 0.907 -0.865C0.907 -1.004 0.955 -1.074 0.962 -1.088C1.206 -0.934 1.451 -0.886 1.639 -0.886C2.322 -0.886 2.831 -1.423 2.831 -2.022C2.831 -2.246 2.769 -2.448 2.643 -2.636C2.615 -2.678 2.615 -2.685 2.615 -2.692C2.615 -2.72 3.034 -2.72 3.068 -2.72C3.075 -2.72 3.34 -2.72 3.592 -2.692L3.508 -3.166ZM1.646 -1.318C1.269 -1.318 0.99 -1.555 0.99 -2.022C0.99 -2.566 1.339 -2.734 1.639 -2.734C2.015 -2.734 2.294 -2.497 2.294 -2.029C2.294 -1.485 1.946 -1.318 1.646 -1.318ZM1.974 0.042C2.134 0.042 2.957 0.042 2.957 0.488C2.957 0.788 2.434 0.997 1.848 0.997S0.739 0.788 0.739 0.488C0.739 0.453 0.739 0.042 1.304 0.042H1.974Z' id='g0-103'/>
+<path d='M3.243 -2.064C3.243 -2.608 3.082 -3.166 2.225 -3.166C1.625 -3.166 1.304 -2.817 1.165 -2.671V-4.84H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-2.064Z' id='g0-104'/>
+<path d='M1.227 -4.784H0.523V-4.08H1.227V-4.784ZM1.172 -3.096H0.586V0H1.172V-3.096Z' id='g0-105'/>
+<path d='M1.381 -4.784H0.676V-4.08H1.381V-4.784ZM-0.453 1.186C-0.133 1.36 0.181 1.423 0.446 1.423C0.928 1.423 1.381 1.053 1.381 0.411V-3.096H0.795V0.46C0.795 0.586 0.795 0.697 0.649 0.816C0.488 0.934 0.293 0.934 0.23 0.934C-0.063 0.934 -0.244 0.802 -0.328 0.725L-0.453 1.186Z' id='g0-106'/>
+<path d='M1.172 -4.84H0.586V0H1.172V-4.84Z' id='g0-108'/>
+<path d='M5.3 -2.064C5.3 -2.608 5.14 -3.166 4.282 -3.166C3.696 -3.166 3.333 -2.824 3.166 -2.601C3.096 -2.79 2.922 -3.166 2.225 -3.166C1.827 -3.166 1.444 -3.006 1.137 -2.636V-3.145H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-1.695C3.243 -2.155 3.438 -2.706 3.975 -2.706C4.693 -2.706 4.693 -2.218 4.693 -2.015V0H5.3V-2.064Z' id='g0-109'/>
+<path d='M3.243 -2.064C3.243 -2.608 3.082 -3.166 2.225 -3.166C1.827 -3.166 1.444 -3.006 1.137 -2.636V-3.145H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-2.064Z' id='g0-110'/>
+<path d='M3.487 -1.527C3.487 -2.448 2.755 -3.201 1.848 -3.201S0.209 -2.441 0.209 -1.527C0.209 -0.642 0.948 0.07 1.848 0.07C2.755 0.07 3.487 -0.642 3.487 -1.527ZM1.848 -0.411C1.297 -0.411 0.816 -0.816 0.816 -1.604S1.332 -2.741 1.848 -2.741C2.371 -2.741 2.88 -2.378 2.88 -1.604C2.88 -0.809 2.385 -0.411 1.848 -0.411Z' id='g0-111'/>
+<path d='M1.2 -0.328C1.569 0.007 1.967 0.07 2.204 0.07C2.943 0.07 3.571 -0.635 3.571 -1.555C3.571 -2.392 3.11 -3.166 2.42 -3.166C2.106 -3.166 1.583 -3.075 1.179 -2.762V-3.096H0.593V1.353H1.2V-0.328ZM1.2 -2.315C1.36 -2.511 1.632 -2.685 1.967 -2.685C2.525 -2.685 2.964 -2.169 2.964 -1.555C2.964 -0.865 2.441 -0.391 1.897 -0.391C1.792 -0.391 1.618 -0.404 1.437 -0.551C1.227 -0.711 1.2 -0.816 1.2 -0.948V-2.315Z' id='g0-112'/>
+<path d='M1.179 -1.485C1.179 -2.239 1.806 -2.643 2.42 -2.65V-3.166C1.834 -3.159 1.409 -2.873 1.13 -2.504V-3.145H0.593V0H1.179V-1.485Z' id='g0-114'/>
+<path d='M2.545 -2.985C2.071 -3.18 1.723 -3.201 1.471 -3.201C1.297 -3.201 0.244 -3.201 0.244 -2.273C0.244 -1.946 0.425 -1.764 0.516 -1.681C0.76 -1.437 1.053 -1.381 1.423 -1.311C1.75 -1.248 2.127 -1.179 2.127 -0.844C2.127 -0.404 1.548 -0.404 1.451 -0.404C1.004 -0.404 0.586 -0.565 0.307 -0.76L0.209 -0.237C0.446 -0.119 0.872 0.07 1.451 0.07C1.764 0.07 2.071 0.021 2.329 -0.167C2.587 -0.363 2.671 -0.669 2.671 -0.907C2.671 -1.032 2.657 -1.304 2.364 -1.569C2.106 -1.799 1.855 -1.848 1.52 -1.911C1.109 -1.988 0.788 -2.05 0.788 -2.357C0.788 -2.755 1.297 -2.755 1.402 -2.755C1.799 -2.755 2.106 -2.671 2.455 -2.49L2.545 -2.985Z' id='g0-115'/>
+<path d='M1.311 -2.657H2.343V-3.096H1.311V-3.982H0.774V-3.096H0.139V-2.657H0.753V-0.893C0.753 -0.425 0.872 0.07 1.374 0.07S2.26 -0.091 2.469 -0.188L2.35 -0.635C2.12 -0.467 1.876 -0.411 1.681 -0.411C1.388 -0.411 1.311 -0.697 1.311 -1.018V-2.657Z' id='g0-116'/>
+<path d='M3.243 -3.096H2.636V-1.074C2.636 -0.516 2.162 -0.342 1.757 -0.342C1.241 -0.342 1.186 -0.481 1.186 -0.802V-3.096H0.579V-0.76C0.579 -0.139 0.851 0.07 1.339 0.07C1.625 0.07 2.239 0.014 2.657 -0.321V0H3.243V-3.096Z' id='g0-117'/>
+<path d='M4.951 -3.096H4.407C4.345 -2.901 3.954 -1.723 3.738 -0.997C3.682 -0.795 3.612 -0.572 3.592 -0.411H3.585C3.543 -0.697 3.299 -1.451 3.285 -1.499L2.769 -3.096H2.239C2.036 -2.497 1.513 -0.934 1.458 -0.425H1.451C1.395 -0.921 0.879 -2.462 0.767 -2.797C0.711 -2.964 0.711 -2.978 0.676 -3.096H0.105L1.123 0H1.709C1.716 -0.028 1.904 -0.579 2.148 -1.353C2.253 -1.695 2.462 -2.364 2.497 -2.671L2.504 -2.678C2.518 -2.532 2.559 -2.378 2.608 -2.204S2.706 -1.841 2.755 -1.681L3.292 0H3.933L4.951 -3.096Z' id='g0-119'/>
+<path d='M1.932 -1.597L3.285 -3.096H2.671L1.681 -1.953L0.669 -3.096H0.042L1.437 -1.597L0 0H0.621L1.681 -1.311L2.783 0H3.41L1.932 -1.597Z' id='g0-120'/>
+<path d='M2.957 -2.803V-3.096H0.307V-2.65H1.332C1.416 -2.65 1.499 -2.657 1.583 -2.657H2.127L0.209 -0.307V0H2.978V-0.467H1.897C1.813 -0.467 1.73 -0.46 1.646 -0.46H1.039L2.957 -2.803Z' id='g0-122'/>
+</defs>
+<g id='page2'>
+<path d='M194.617 260.785V251.93M253.285 260.785V251.93M311.953 260.785V251.93M370.625 260.785V251.93M429.293 260.785V251.93M194.617 75.308V84.164M253.285 75.308V84.164M311.953 75.308V84.164M370.625 75.308V84.164M429.293 75.308V84.164' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M165.281 256.18V251.93M223.953 256.18V251.93M282.621 256.18V251.93M341.289 256.18V251.93M399.957 256.18V251.93M458.629 256.18V251.93M165.281 79.91V84.164M223.953 79.91V84.164M282.621 79.91V84.164M341.289 79.91V84.164M399.957 79.91V84.164M458.629 79.91V84.164' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 251.93H140.199M135.949 218.375H140.199M135.949 184.824H140.199M135.949 151.269H140.199M135.949 117.719H140.199M135.949 84.164H140.199M487.961 251.93H483.711M487.961 218.375H483.711M487.961 184.824H483.711M487.961 151.269H483.711M487.961 117.719H483.711M487.961 84.164H483.711' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 251.93V84.164H487.961V251.93H135.949Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -21.265 74.992)'>
+<use x='168.285' xlink:href='#g3-97' y='191.334'/>
+<use x='172.353' xlink:href='#g3-108' y='191.334'/>
+<use x='174.373' xlink:href='#g3-108' y='191.334'/>
+<use x='176.393' xlink:href='#g3-111' y='191.334'/>
+<use x='180.863' xlink:href='#g3-99' y='191.334'/>
+<use x='184.627' xlink:href='#g3-45' y='191.334'/>
+<use x='187.449' xlink:href='#g3-116' y='191.334'/>
+<use x='190.507' xlink:href='#g3-101' y='191.334'/>
+<use x='194.271' xlink:href='#g3-115' y='191.334'/>
+<use x='197.517' xlink:href='#g3-116' y='191.334'/>
+<use x='200.575' xlink:href='#g3-49' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 36.532 74.992)'>
+<use x='168.285' xlink:href='#g3-97' y='191.334'/>
+<use x='172.353' xlink:href='#g3-108' y='191.334'/>
+<use x='174.373' xlink:href='#g3-108' y='191.334'/>
+<use x='176.393' xlink:href='#g3-111' y='191.334'/>
+<use x='180.863' xlink:href='#g3-99' y='191.334'/>
+<use x='184.627' xlink:href='#g3-45' y='191.334'/>
+<use x='187.449' xlink:href='#g3-116' y='191.334'/>
+<use x='190.507' xlink:href='#g3-101' y='191.334'/>
+<use x='194.271' xlink:href='#g3-115' y='191.334'/>
+<use x='197.517' xlink:href='#g3-116' y='191.334'/>
+<use x='200.575' xlink:href='#g3-78' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 94.98 74.992)'>
+<use x='168.285' xlink:href='#g3-115' y='191.334'/>
+<use x='171.531' xlink:href='#g3-104' y='191.334'/>
+<use x='175.904' xlink:href='#g3-54' y='191.334'/>
+<use x='180.138' xlink:href='#g3-98' y='191.334'/>
+<use x='184.746' xlink:href='#g3-101' y='191.334'/>
+<use x='188.509' xlink:href='#g3-110' y='191.334'/>
+<use x='192.882' xlink:href='#g3-99' y='191.334'/>
+<use x='196.646' xlink:href='#g3-104' y='191.334'/>
+<use x='201.018' xlink:href='#g3-78' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 153.649 74.992)'>
+<use x='168.285' xlink:href='#g3-115' y='191.334'/>
+<use x='171.531' xlink:href='#g3-104' y='191.334'/>
+<use x='175.904' xlink:href='#g3-56' y='191.334'/>
+<use x='180.138' xlink:href='#g3-98' y='191.334'/>
+<use x='184.746' xlink:href='#g3-101' y='191.334'/>
+<use x='188.509' xlink:href='#g3-110' y='191.334'/>
+<use x='192.882' xlink:href='#g3-99' y='191.334'/>
+<use x='196.646' xlink:href='#g3-104' y='191.334'/>
+<use x='201.018' xlink:href='#g3-78' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 207.225 74.992)'>
+<use x='168.285' xlink:href='#g3-120' y='191.334'/>
+<use x='172.187' xlink:href='#g3-109' y='191.334'/>
+<use x='178.912' xlink:href='#g3-97' y='191.334'/>
+<use x='182.98' xlink:href='#g3-108' y='191.334'/>
+<use x='185' xlink:href='#g3-108' y='191.334'/>
+<use x='187.02' xlink:href='#g3-111' y='191.334'/>
+<use x='191.49' xlink:href='#g3-99' y='191.334'/>
+<use x='195.254' xlink:href='#g3-45' y='191.334'/>
+<use x='198.076' xlink:href='#g3-116' y='191.334'/>
+<use x='201.134' xlink:href='#g3-101' y='191.334'/>
+<use x='204.898' xlink:href='#g3-115' y='191.334'/>
+<use x='208.144' xlink:href='#g3-116' y='191.334'/>
+<use x='211.202' xlink:href='#g3-78' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 263.494 74.992)'>
+<use x='168.285' xlink:href='#g3-99' y='191.334'/>
+<use x='172.049' xlink:href='#g3-97' y='191.334'/>
+<use x='176.117' xlink:href='#g3-99' y='191.334'/>
+<use x='179.88' xlink:href='#g3-104' y='191.334'/>
+<use x='184.253' xlink:href='#g3-101' y='191.334'/>
+<use x='188.017' xlink:href='#g3-45' y='191.334'/>
+<use x='190.839' xlink:href='#g3-115' y='191.334'/>
+<use x='194.086' xlink:href='#g3-99' y='191.334'/>
+<use x='197.849' xlink:href='#g3-114' y='191.334'/>
+<use x='200.741' xlink:href='#g3-97' y='191.334'/>
+<use x='204.81' xlink:href='#g3-116' y='191.334'/>
+<use x='207.868' xlink:href='#g3-99' y='191.334'/>
+<use x='211.631' xlink:href='#g3-104' y='191.334'/>
+<use x='216.004' xlink:href='#g3-78' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 62.23)'>
+<use x='168.285' xlink:href='#g2-48' y='191.334'/>
+<use x='170.931' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 28.677)'>
+<use x='168.285' xlink:href='#g2-48' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-53' y='191.334'/>
+<use x='175.048' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -4.876)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -38.43)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-53' y='191.334'/>
+<use x='175.048' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -71.983)'>
+<use x='168.285' xlink:href='#g2-50' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -105.536)'>
+<use x='168.285' xlink:href='#g2-50' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-53' y='191.334'/>
+<use x='175.048' xlink:href='#g2-120' y='191.334'/>
+</g>
+<path clip-path='url(#clip2)' d='M135.949 184.824H487.961' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M142.742 251.93H145.981V184.824H142.742ZM201.41 251.93H204.649V184.824H201.41ZM260.078 251.93H263.317V184.824H260.078ZM318.75 251.93H321.988V184.824H318.75ZM377.418 251.93H380.656V184.824H377.418ZM436.086 251.93H439.324V184.824H436.086Z' fill='#993333'/>
+<path clip-path='url(#clip2)' d='M142.742 251.93H145.981V184.824H142.742ZM201.41 251.93H204.649V184.824H201.41ZM260.078 251.93H263.317V184.824H260.078ZM318.75 251.93H321.988V184.824H318.75ZM377.418 251.93H380.656V184.824H377.418ZM436.086 251.93H439.324V184.824H436.086Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M144.359 184.824V184.758' fill='#993333'/>
+<path clip-path='url(#clip2)' d='M144.359 184.824V184.758' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M142.367 184.758H146.355' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M144.359 184.824V184.891' fill='#993333'/>
+<path clip-path='url(#clip2)' d='M144.359 184.824V184.891' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M146.352 184.891H142.367' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M203.031 184.824V184.621' fill='#993333'/>
+<path clip-path='url(#clip2)' d='M203.031 184.824V184.621' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M201.039 184.621H205.023' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M203.031 184.824V185.023' fill='#993333'/>
+<path clip-path='url(#clip2)' d='M203.031 184.824V185.023' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M205.023 185.023H201.035' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M261.699 184.824V179.855' fill='#993333'/>
+<path clip-path='url(#clip2)' d='M261.699 184.824V179.855' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M259.707 179.855H263.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M261.699 184.824V189.789' fill='#993333'/>
+<path clip-path='url(#clip2)' d='M261.699 184.824V189.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M263.691 189.789H259.707' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M320.367 184.824V184.824' fill='#993333'/>
+<path clip-path='url(#clip2)' d='M318.375 184.824H322.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M320.367 184.824V184.824' fill='#993333'/>
+<path clip-path='url(#clip2)' d='M318.375 184.824H322.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M379.035 184.824V184.152' fill='#993333'/>
+<path clip-path='url(#clip2)' d='M379.035 184.824V184.152' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M377.043 184.152H381.031' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M379.035 184.824V185.492' fill='#993333'/>
+<path clip-path='url(#clip2)' d='M379.035 184.824V185.492' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M381.027 185.492H377.043' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M437.707 184.824V179.254' fill='#993333'/>
+<path clip-path='url(#clip2)' d='M437.707 184.824V179.254' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M435.715 179.254H439.699' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M437.707 184.824V190.394' fill='#993333'/>
+<path clip-path='url(#clip2)' d='M437.707 184.824V190.394' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M439.699 190.395H435.711' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M147.973 251.93H151.211V184.621H147.973ZM206.641 251.93H209.879V163.75H206.641ZM265.309 251.93H268.547V84.164H265.309ZM323.981 251.93H327.219V84.164H323.981ZM382.649 251.93H385.887V84.164H382.649ZM441.317 251.93H444.555V84.164H441.317Z' fill='#8080bf'/>
+<path clip-path='url(#clip2)' d='M147.973 251.93H151.211V184.621H147.973ZM206.641 251.93H209.879V163.75H206.641ZM265.309 251.93H268.547V84.164H265.309ZM323.981 251.93H327.219V84.164H323.981ZM382.649 251.93H385.887V84.164H382.649ZM441.317 251.93H444.555V84.164H441.317Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M149.59 184.621V184.555' fill='#8080bf'/>
+<path clip-path='url(#clip2)' d='M149.59 184.621V184.555' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M147.598 184.555H151.586' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M149.59 184.621V184.687' fill='#8080bf'/>
+<path clip-path='url(#clip2)' d='M149.59 184.621V184.687' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M151.582 184.688H147.597' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M208.262 163.75V162.41' fill='#8080bf'/>
+<path clip-path='url(#clip2)' d='M208.262 163.75V162.41' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M206.27 162.411H210.254' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M208.262 163.75V165.094' fill='#8080bf'/>
+<path clip-path='url(#clip2)' d='M208.262 163.75V165.094' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M210.253 165.094H206.265' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M266.93 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip2)' d='M264.938 84.164H268.922' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M266.93 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip2)' d='M264.938 84.164H268.922' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M325.598 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip2)' d='M323.606 84.164H327.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M325.598 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip2)' d='M323.606 84.164H327.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M384.266 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip2)' d='M382.274 84.164H386.262' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M384.266 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip2)' d='M382.274 84.164H386.262' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M442.938 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip2)' d='M440.946 84.164H444.93' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M442.938 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip2)' d='M440.946 84.164H444.93' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M153.203 251.93H156.442V178.312H153.203ZM211.871 251.93H215.11V178.984H211.871ZM270.539 251.93H273.777V131.875H270.539ZM329.211 251.93H332.449V124.832H329.211ZM387.879 251.93H391.117V84.164H387.879ZM446.547 251.93H449.785V84.164H446.547Z' fill='#ffb733'/>
+<path clip-path='url(#clip2)' d='M153.203 251.93H156.442V178.312H153.203ZM211.871 251.93H215.11V178.984H211.871ZM270.539 251.93H273.777V131.875H270.539ZM329.211 251.93H332.449V124.832H329.211ZM387.879 251.93H391.117V84.164H387.879ZM446.547 251.93H449.785V84.164H446.547Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M154.82 178.312V178.312' fill='#ffb733'/>
+<path clip-path='url(#clip2)' d='M152.828 178.313H156.816' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M154.82 178.312V178.312' fill='#ffb733'/>
+<path clip-path='url(#clip2)' d='M152.828 178.313H156.816' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M213.492 178.984V178.918' fill='#ffb733'/>
+<path clip-path='url(#clip2)' d='M213.492 178.984V178.918' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M211.5 178.918H215.484' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M213.492 178.984V179.051' fill='#ffb733'/>
+<path clip-path='url(#clip2)' d='M213.492 178.984V179.051' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M215.484 179.051H211.496' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M272.16 131.875V126.91' fill='#ffb733'/>
+<path clip-path='url(#clip2)' d='M272.16 131.875V126.91' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M270.168 126.91H274.152' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M272.16 131.875V136.844' fill='#ffb733'/>
+<path clip-path='url(#clip2)' d='M272.16 131.875V136.844' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M274.152 136.844H270.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M330.828 124.832V121.945' fill='#ffb733'/>
+<path clip-path='url(#clip2)' d='M330.828 124.832V121.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M328.836 121.946H332.82' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M330.828 124.832V127.715' fill='#ffb733'/>
+<path clip-path='url(#clip2)' d='M330.828 124.832V127.715' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M332.82 127.715H328.836' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M389.496 84.164V84.164' fill='#ffb733'/>
+<path clip-path='url(#clip2)' d='M387.504 84.164H391.492' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M389.496 84.164V84.164' fill='#ffb733'/>
+<path clip-path='url(#clip2)' d='M387.504 84.164H391.492' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M448.168 84.164V84.164' fill='#ffb733'/>
+<path clip-path='url(#clip2)' d='M446.176 84.164H450.16' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M448.168 84.164V84.164' fill='#ffb733'/>
+<path clip-path='url(#clip2)' d='M446.176 84.164H450.16' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M158.434 251.93H161.672V164.422H158.434ZM217.102 251.93H220.34V166.57H217.102ZM275.77 251.93H279.008V84.164H275.77ZM334.442 251.93H337.68V97.383H334.442ZM393.109 251.93H396.348V130.535H393.109ZM451.777 251.93H455.016V184.824H451.777Z' fill='#bf80bf'/>
+<path clip-path='url(#clip2)' d='M158.434 251.93H161.672V164.422H158.434ZM217.102 251.93H220.34V166.57H217.102ZM275.77 251.93H279.008V84.164H275.77ZM334.442 251.93H337.68V97.383H334.442ZM393.109 251.93H396.348V130.535H393.109ZM451.777 251.93H455.016V184.824H451.777Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M160.051 164.422V164.355' fill='#bf80bf'/>
+<path clip-path='url(#clip2)' d='M160.051 164.422V164.355' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M158.058 164.356H162.046' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M160.051 164.422V164.488' fill='#bf80bf'/>
+<path clip-path='url(#clip2)' d='M160.051 164.422V164.488' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M162.043 164.488H158.058' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M218.723 166.57V166.57' fill='#bf80bf'/>
+<path clip-path='url(#clip2)' d='M216.73 166.57H220.714' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M218.723 166.57V166.57' fill='#bf80bf'/>
+<path clip-path='url(#clip2)' d='M216.73 166.57H220.714' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M277.391 84.164V84.164' fill='#bf80bf'/>
+<path clip-path='url(#clip2)' d='M275.398 84.164H279.382' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M277.391 84.164V84.164' fill='#bf80bf'/>
+<path clip-path='url(#clip2)' d='M275.398 84.164H279.382' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M336.059 97.383V93.558' fill='#bf80bf'/>
+<path clip-path='url(#clip2)' d='M336.059 97.383V93.558' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M334.066 93.559H338.05' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M336.059 97.383V101.207' fill='#bf80bf'/>
+<path clip-path='url(#clip2)' d='M336.059 97.383V101.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M338.05 101.207H334.066' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M394.727 130.535V129.996' fill='#bf80bf'/>
+<path clip-path='url(#clip2)' d='M394.727 130.535V129.996' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M392.734 129.996H396.722' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M394.727 130.535V131.07' fill='#bf80bf'/>
+<path clip-path='url(#clip2)' d='M394.727 130.535V131.07' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M396.718 131.07H392.734' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M453.399 184.824V179.254' fill='#bf80bf'/>
+<path clip-path='url(#clip2)' d='M453.399 184.824V179.254' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M451.406 179.254H455.39' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M453.399 184.824V190.394' fill='#bf80bf'/>
+<path clip-path='url(#clip2)' d='M453.399 184.824V190.394' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M455.39 190.395H451.402' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M163.664 251.93H166.903V178.449H163.664ZM222.332 251.93H225.57V182.34H222.332ZM281 251.93H284.238V160.129H281ZM339.672 251.93H342.91V179.723H339.672ZM398.34 251.93H401.578V183.211H398.34ZM457.008 251.93H460.246V180.863H457.008Z' fill='#dfbf9f'/>
+<path clip-path='url(#clip2)' d='M163.664 251.93H166.903V178.449H163.664ZM222.332 251.93H225.57V182.34H222.332ZM281 251.93H284.238V160.129H281ZM339.672 251.93H342.91V179.723H339.672ZM398.34 251.93H401.578V183.211H398.34ZM457.008 251.93H460.246V180.863H457.008Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M165.281 178.449V178.379' fill='#dfbf9f'/>
+<path clip-path='url(#clip2)' d='M165.281 178.449V178.379' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M163.289 178.379H167.277' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M165.281 178.449V178.516' fill='#dfbf9f'/>
+<path clip-path='url(#clip2)' d='M165.281 178.449V178.516' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M167.274 178.516H163.289' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M223.953 182.34V182.004' fill='#dfbf9f'/>
+<path clip-path='url(#clip2)' d='M223.953 182.34V182.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M221.961 182.004H225.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M223.953 182.34V182.676' fill='#dfbf9f'/>
+<path clip-path='url(#clip2)' d='M223.953 182.34V182.676' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M225.945 182.676H221.957' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M282.621 160.129V155.164' fill='#dfbf9f'/>
+<path clip-path='url(#clip2)' d='M282.621 160.129V155.164' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M280.629 155.164H284.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M282.621 160.129V165.094' fill='#dfbf9f'/>
+<path clip-path='url(#clip2)' d='M282.621 160.129V165.094' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M284.613 165.094H280.629' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M341.289 179.723V178.312' fill='#dfbf9f'/>
+<path clip-path='url(#clip2)' d='M341.289 179.723V178.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M339.297 178.313H343.281' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M341.289 179.723V181.133' fill='#dfbf9f'/>
+<path clip-path='url(#clip2)' d='M341.289 179.723V181.133' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M343.281 181.133H339.297' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M399.957 183.211V182.207' fill='#dfbf9f'/>
+<path clip-path='url(#clip2)' d='M399.957 183.211V182.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M397.965 182.207H401.953' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M399.957 183.211V184.219' fill='#dfbf9f'/>
+<path clip-path='url(#clip2)' d='M399.957 183.211V184.219' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M401.949 184.219H397.965' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M458.629 180.863V171.199' fill='#dfbf9f'/>
+<path clip-path='url(#clip2)' d='M458.629 180.863V171.199' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M456.637 171.2H460.621' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M458.629 180.863V190.527' fill='#dfbf9f'/>
+<path clip-path='url(#clip2)' d='M458.629 180.863V190.527' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M460.621 190.527H456.633' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M168.895 251.93H172.133V175.496H168.895ZM227.563 251.93H230.801V84.164H227.563ZM286.231 251.93H289.469V84.164H286.231ZM344.902 251.93H348.141V84.164H344.902ZM403.57 251.93H406.809V84.164H403.57ZM462.238 251.93H465.477V84.164H462.238Z' fill='#80bf80'/>
+<path clip-path='url(#clip2)' d='M168.895 251.93H172.133V175.496H168.895ZM227.563 251.93H230.801V84.164H227.563ZM286.231 251.93H289.469V84.164H286.231ZM344.902 251.93H348.141V84.164H344.902ZM403.57 251.93H406.809V84.164H403.57ZM462.238 251.93H465.477V84.164H462.238Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M170.512 175.496V175.43' fill='#80bf80'/>
+<path clip-path='url(#clip2)' d='M170.512 175.496V175.43' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M168.519 175.429H172.507' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M170.512 175.496V175.562' fill='#80bf80'/>
+<path clip-path='url(#clip2)' d='M170.512 175.496V175.562' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M172.504 175.562H168.519' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M229.184 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip2)' d='M227.191 84.164H231.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M229.184 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip2)' d='M227.191 84.164H231.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M287.852 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip2)' d='M285.859 84.164H289.843' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M287.852 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip2)' d='M285.859 84.164H289.843' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M346.52 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip2)' d='M344.527 84.164H348.511' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M346.52 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip2)' d='M344.527 84.164H348.511' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M405.188 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip2)' d='M403.195 84.164H407.183' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M405.188 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip2)' d='M403.195 84.164H407.183' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M463.859 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip2)' d='M461.867 84.164H465.851' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M463.859 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip2)' d='M461.867 84.164H465.851' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M174.125 251.93H177.363V120.402H174.125ZM232.793 251.93H236.031V84.164H232.793ZM291.461 251.93H294.699V84.164H291.461ZM350.133 251.93H353.371V84.164H350.133ZM408.801 251.93H412.039V84.164H408.801ZM467.469 251.93H470.707V184.824H467.469Z' fill='#bfbf80'/>
+<path clip-path='url(#clip2)' d='M174.125 251.93H177.363V120.402H174.125ZM232.793 251.93H236.031V84.164H232.793ZM291.461 251.93H294.699V84.164H291.461ZM350.133 251.93H353.371V84.164H350.133ZM408.801 251.93H412.039V84.164H408.801ZM467.469 251.93H470.707V184.824H467.469Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M175.742 120.402V120.266' fill='#bfbf80'/>
+<path clip-path='url(#clip2)' d='M175.742 120.402V120.266' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M173.75 120.266H177.738' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M175.742 120.402V120.535' fill='#bfbf80'/>
+<path clip-path='url(#clip2)' d='M175.742 120.402V120.535' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M177.735 120.535H173.75' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M234.414 84.164V84.164' fill='#bfbf80'/>
+<path clip-path='url(#clip2)' d='M232.422 84.164H236.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M234.414 84.164V84.164' fill='#bfbf80'/>
+<path clip-path='url(#clip2)' d='M232.422 84.164H236.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M293.082 84.164V84.164' fill='#bfbf80'/>
+<path clip-path='url(#clip2)' d='M291.09 84.164H295.074' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M293.082 84.164V84.164' fill='#bfbf80'/>
+<path clip-path='url(#clip2)' d='M291.09 84.164H295.074' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M351.75 84.164V84.164' fill='#bfbf80'/>
+<path clip-path='url(#clip2)' d='M349.758 84.164H353.742' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M351.75 84.164V84.164' fill='#bfbf80'/>
+<path clip-path='url(#clip2)' d='M349.758 84.164H353.742' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M410.418 84.164V84.164' fill='#bfbf80'/>
+<path clip-path='url(#clip2)' d='M408.426 84.164H412.414' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M410.418 84.164V84.164' fill='#bfbf80'/>
+<path clip-path='url(#clip2)' d='M408.426 84.164H412.414' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M469.09 184.824V179.254' fill='#bfbf80'/>
+<path clip-path='url(#clip2)' d='M469.09 184.824V179.254' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M467.098 179.254H471.082' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M469.09 184.824V190.394' fill='#bfbf80'/>
+<path clip-path='url(#clip2)' d='M469.09 184.824V190.394' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M471.082 190.395H467.094' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M179.356 251.93H182.594V168.652H179.356ZM238.024 251.93H241.262V169.457H238.024ZM296.692 251.93H299.93V84.164H296.692ZM355.363 251.93H358.602V84.164H355.363ZM414.031 251.93H417.27V84.164H414.031ZM472.699 251.93H475.938V84.164H472.699Z' fill='#339999'/>
+<path clip-path='url(#clip2)' d='M179.356 251.93H182.594V168.652H179.356ZM238.024 251.93H241.262V169.457H238.024ZM296.692 251.93H299.93V84.164H296.692ZM355.363 251.93H358.602V84.164H355.363ZM414.031 251.93H417.27V84.164H414.031ZM472.699 251.93H475.938V84.164H472.699Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M180.973 168.652V168.582' fill='#339999'/>
+<path clip-path='url(#clip2)' d='M180.973 168.652V168.582' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M178.98 168.582H182.968' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M180.973 168.652V168.719' fill='#339999'/>
+<path clip-path='url(#clip2)' d='M180.973 168.652V168.719' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M182.965 168.719H178.98' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M239.645 169.457V169.387' fill='#339999'/>
+<path clip-path='url(#clip2)' d='M239.645 169.457V169.387' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M237.652 169.387H241.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M239.645 169.457V169.523' fill='#339999'/>
+<path clip-path='url(#clip2)' d='M239.645 169.457V169.523' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M241.633 169.524H237.648' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M298.313 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip2)' d='M296.32 84.164H300.304' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M298.313 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip2)' d='M296.32 84.164H300.304' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M356.981 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip2)' d='M354.988 84.164H358.972' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M356.981 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip2)' d='M354.988 84.164H358.972' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M415.649 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip2)' d='M413.656 84.164H417.644' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M415.649 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip2)' d='M413.656 84.164H417.644' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M474.32 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip2)' d='M472.328 84.164H476.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M474.32 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip2)' d='M472.328 84.164H476.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M184.586 251.93H187.824V176.301H184.586ZM243.254 251.93H246.492V177.309H243.254ZM301.922 251.93H305.16V84.164H301.922ZM360.594 251.93H363.832V84.164H360.594ZM419.262 251.93H422.5V143.621H419.262ZM477.93 251.93H481.168V176.906H477.93Z' fill='#bf8080'/>
+<path clip-path='url(#clip2)' d='M184.586 251.93H187.824V176.301H184.586ZM243.254 251.93H246.492V177.309H243.254ZM301.922 251.93H305.16V84.164H301.922ZM360.594 251.93H363.832V84.164H360.594ZM419.262 251.93H422.5V143.621H419.262ZM477.93 251.93H481.168V176.906H477.93Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M186.203 176.301V175.965' fill='#bf8080'/>
+<path clip-path='url(#clip2)' d='M186.203 176.301V175.965' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M184.211 175.965H188.199' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M186.203 176.301V176.637' fill='#bf8080'/>
+<path clip-path='url(#clip2)' d='M186.203 176.301V176.637' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M188.196 176.637H184.211' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M244.875 177.309V177.172' fill='#bf8080'/>
+<path clip-path='url(#clip2)' d='M244.875 177.309V177.172' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M242.883 177.172H246.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M244.875 177.309V177.441' fill='#bf8080'/>
+<path clip-path='url(#clip2)' d='M244.875 177.309V177.441' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M246.864 177.441H242.879' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M303.543 84.164V84.164' fill='#bf8080'/>
+<path clip-path='url(#clip2)' d='M301.551 84.164H305.535' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M303.543 84.164V84.164' fill='#bf8080'/>
+<path clip-path='url(#clip2)' d='M301.551 84.164H305.535' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M362.211 84.164V84.164' fill='#bf8080'/>
+<path clip-path='url(#clip2)' d='M360.219 84.164H364.203' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M362.211 84.164V84.164' fill='#bf8080'/>
+<path clip-path='url(#clip2)' d='M360.219 84.164H364.203' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M420.879 143.621V142.746' fill='#bf8080'/>
+<path clip-path='url(#clip2)' d='M420.879 143.621V142.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M418.887 142.746H422.875' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M420.879 143.621V144.492' fill='#bf8080'/>
+<path clip-path='url(#clip2)' d='M420.879 143.621V144.492' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M422.871 144.492H418.887' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M479.551 176.906V171.336' fill='#bf8080'/>
+<path clip-path='url(#clip2)' d='M479.551 176.906V171.336' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M477.559 171.336H481.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M479.551 176.906V182.473' fill='#bf8080'/>
+<path clip-path='url(#clip2)' d='M479.551 176.906V182.473' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M481.543 182.472H477.555' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M420.539 312.98H487.762V278.973H420.539Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 255.575 118.327)'>
+<use x='168.285' xlink:href='#g2-99' y='167.424'/>
+<use x='170.637' xlink:href='#g2-53' y='167.424'/>
+<use x='173.284' xlink:href='#g2-45' y='167.424'/>
+<use x='175.048' xlink:href='#g2-49' y='167.424'/>
+<use x='177.694' xlink:href='#g2-56' y='167.424'/>
+<use x='180.341' xlink:href='#g2-120' y='167.424'/>
+<use x='182.779' xlink:href='#g2-108' y='167.424'/>
+<use x='184.042' xlink:href='#g2-97' y='167.424'/>
+<use x='186.438' xlink:href='#g2-114' y='167.424'/>
+<use x='188.245' xlink:href='#g2-103' y='167.424'/>
+<use x='190.891' xlink:href='#g2-101' y='167.424'/>
+<use x='193.244' xlink:href='#g2-44' y='167.424'/>
+<use x='196.478' xlink:href='#g2-49' y='167.424'/>
+<use x='199.124' xlink:href='#g2-52' y='167.424'/>
+<use x='201.771' xlink:href='#g2-52' y='167.424'/>
+<use x='204.417' xlink:href='#g2-71' y='167.424'/>
+<use x='207.946' xlink:href='#g2-98' y='167.424'/>
+<use x='168.285' xlink:href='#g2-55' y='173.401'/>
+<use x='170.931' xlink:href='#g2-50' y='173.401'/>
+<use x='175.342' xlink:href='#g2-112' y='173.401'/>
+<use x='177.928' xlink:href='#g2-114' y='173.401'/>
+<use x='179.735' xlink:href='#g2-111' y='173.401'/>
+<use x='182.529' xlink:href='#g2-99' y='173.401'/>
+<use x='186.645' xlink:href='#g2-73' y='173.401'/>
+<use x='188.115' xlink:href='#g2-110' y='173.401'/>
+<use x='190.848' xlink:href='#g2-116' y='173.401'/>
+<use x='192.759' xlink:href='#g2-101' y='173.401'/>
+<use x='195.112' xlink:href='#g2-108' y='173.401'/>
+<use x='198.139' xlink:href='#g2-88' y='173.401'/>
+<use x='201.667' xlink:href='#g2-101' y='173.401'/>
+<use x='204.019' xlink:href='#g2-111' y='173.401'/>
+<use x='206.666' xlink:href='#g2-110' y='173.401'/>
+<use x='211.163' xlink:href='#g2-64' y='173.401'/>
+<use x='214.691' xlink:href='#g2-51' y='173.401'/>
+<use x='217.338' xlink:href='#g2-71' y='173.401'/>
+<use x='220.866' xlink:href='#g2-104' y='173.401'/>
+<use x='223.599' xlink:href='#g2-122' y='173.401'/>
+<use x='168.285' xlink:href='#g2-85' y='179.379'/>
+<use x='171.917' xlink:href='#g2-98' y='179.379'/>
+<use x='174.65' xlink:href='#g2-117' y='179.379'/>
+<use x='177.383' xlink:href='#g2-110' y='179.379'/>
+<use x='180.116' xlink:href='#g2-116' y='179.379'/>
+<use x='182.027' xlink:href='#g2-117' y='179.379'/>
+<use x='186.524' xlink:href='#g2-49' y='179.379'/>
+<use x='189.17' xlink:href='#g2-56' y='179.379'/>
+<use x='191.817' xlink:href='#g2-46' y='179.379'/>
+<use x='193.287' xlink:href='#g2-48' y='179.379'/>
+<use x='195.933' xlink:href='#g2-52' y='179.379'/>
+<use x='198.58' xlink:href='#g2-46' y='179.379'/>
+<use x='200.05' xlink:href='#g2-49' y='179.379'/>
+<use x='202.696' xlink:href='#g2-44' y='179.379'/>
+<use x='205.931' xlink:href='#g2-71' y='179.379'/>
+<use x='209.459' xlink:href='#g2-67' y='179.379'/>
+<use x='212.841' xlink:href='#g2-67' y='179.379'/>
+<use x='217.986' xlink:href='#g2-55' y='179.379'/>
+<use x='220.633' xlink:href='#g2-46' y='179.379'/>
+<use x='222.103' xlink:href='#g2-52' y='179.379'/>
+<use x='224.749' xlink:href='#g2-46' y='179.379'/>
+<use x='226.219' xlink:href='#g2-48' y='179.379'/>
+<use x='168.285' xlink:href='#g2-51' y='185.357'/>
+<use x='170.931' xlink:href='#g2-54' y='185.357'/>
+<use x='175.342' xlink:href='#g2-99' y='185.357'/>
+<use x='177.694' xlink:href='#g2-111' y='185.357'/>
+<use x='180.193' xlink:href='#g2-114' y='185.357'/>
+<use x='182.001' xlink:href='#g2-101' y='185.357'/>
+<use x='184.353' xlink:href='#g2-115' y='185.357'/>
+<use x='188.146' xlink:href='#g2-111' y='185.357'/>
+<use x='190.793' xlink:href='#g2-110' y='185.357'/>
+<use x='195.29' xlink:href='#g2-50' y='185.357'/>
+<use x='199.7' xlink:href='#g2-110' y='185.357'/>
+<use x='202.433' xlink:href='#g2-117' y='185.357'/>
+<use x='205.166' xlink:href='#g2-109' y='185.357'/>
+<use x='209.369' xlink:href='#g2-97' y='185.357'/>
+<use x='213.676' xlink:href='#g2-110' y='185.357'/>
+<use x='216.409' xlink:href='#g2-111' y='185.357'/>
+<use x='219.202' xlink:href='#g2-100' y='185.357'/>
+<use x='221.935' xlink:href='#g2-101' y='185.357'/>
+<use x='224.287' xlink:href='#g2-115' y='185.357'/>
+<use x='168.285' xlink:href='#g2-50' y='191.334'/>
+<use x='170.931' xlink:href='#g2-48' y='191.334'/>
+<use x='173.578' xlink:href='#g2-50' y='191.334'/>
+<use x='176.224' xlink:href='#g2-48' y='191.334'/>
+<use x='178.87' xlink:href='#g2-45' y='191.334'/>
+<use x='180.635' xlink:href='#g2-48' y='191.334'/>
+<use x='183.281' xlink:href='#g2-49' y='191.334'/>
+<use x='185.927' xlink:href='#g2-45' y='191.334'/>
+<use x='187.692' xlink:href='#g2-49' y='191.334'/>
+<use x='190.338' xlink:href='#g2-56' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -45.339 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 13.33 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 71.999 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 130.668 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 189.337 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 248.006 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -40.108 346.703)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 18.561 325.833)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-51' y='191.334'/>
+<use x='175.048' xlink:href='#g2-49' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 77.23 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-50' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-56' y='191.334'/>
+<use x='180.675' xlink:href='#g2-52' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 135.899 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-51' y='191.334'/>
+<use x='176.558' xlink:href='#g2-49' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-51' y='191.334'/>
+<use x='183.321' xlink:href='#g2-50' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 194.568 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-51' y='191.334'/>
+<use x='176.558' xlink:href='#g2-49' y='191.334'/>
+<use x='179.205' xlink:href='#g2-51' y='191.334'/>
+<use x='181.851' xlink:href='#g2-46' y='191.334'/>
+<use x='183.321' xlink:href='#g2-55' y='191.334'/>
+<use x='185.968' xlink:href='#g2-49' y='191.334'/>
+<use x='188.614' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 253.236 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-50' y='191.334'/>
+<use x='176.558' xlink:href='#g2-56' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-55' y='191.334'/>
+<use x='183.321' xlink:href='#g2-54' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -34.878 340.395)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-49' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 23.791 341.066)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-57' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 82.46 293.957)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-55' y='191.334'/>
+<use x='175.048' xlink:href='#g2-57' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 141.129 286.911)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-56' y='191.334'/>
+<use x='175.048' xlink:href='#g2-57' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 199.798 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-49' y='191.334'/>
+<use x='176.558' xlink:href='#g2-48' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-52' y='191.334'/>
+<use x='183.321' xlink:href='#g2-52' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 258.467 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-50' y='191.334'/>
+<use x='176.558' xlink:href='#g2-56' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-53' y='191.334'/>
+<use x='183.321' xlink:href='#g2-51' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -29.648 326.504)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-51' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 29.021 328.651)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-50' y='191.334'/>
+<use x='175.048' xlink:href='#g2-55' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 87.69 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-50' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-54' y='191.334'/>
+<use x='180.675' xlink:href='#g2-56' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 146.359 259.465)'>
+<use x='168.285' xlink:href='#g2-50' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-51' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 205.028 292.615)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-56' y='191.334'/>
+<use x='175.048' xlink:href='#g2-49' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 263.697 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -24.417 340.529)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-49' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 34.252 344.421)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-52' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 92.921 322.209)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-51' y='191.334'/>
+<use x='175.048' xlink:href='#g2-55' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 151.59 341.804)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-56' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 210.259 345.293)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-50' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 268.928 342.945)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-54' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -19.187 337.576)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-49' y='191.334'/>
+<use x='175.048' xlink:href='#g2-52' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 39.482 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-54' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-50' y='191.334'/>
+<use x='180.675' xlink:href='#g2-56' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 98.151 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-54' y='191.334'/>
+<use x='176.558' xlink:href='#g2-48' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-48' y='191.334'/>
+<use x='183.321' xlink:href='#g2-48' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 156.82 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-50' y='191.334'/>
+<use x='176.558' xlink:href='#g2-49' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-55' y='191.334'/>
+<use x='183.321' xlink:href='#g2-57' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 215.489 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-51' y='191.334'/>
+<use x='176.558' xlink:href='#g2-48' y='191.334'/>
+<use x='179.205' xlink:href='#g2-55' y='191.334'/>
+<use x='181.851' xlink:href='#g2-46' y='191.334'/>
+<use x='183.321' xlink:href='#g2-48' y='191.334'/>
+<use x='185.968' xlink:href='#g2-50' y='191.334'/>
+<use x='188.614' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 274.158 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-49' y='191.334'/>
+<use x='176.558' xlink:href='#g2-51' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-51' y='191.334'/>
+<use x='183.321' xlink:href='#g2-53' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -13.956 282.482)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-57' y='191.334'/>
+<use x='175.048' xlink:href='#g2-54' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 44.713 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-52' y='191.334'/>
+<use x='176.558' xlink:href='#g2-50' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-53' y='191.334'/>
+<use x='183.321' xlink:href='#g2-56' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 103.382 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-50' y='191.334'/>
+<use x='176.558' xlink:href='#g2-50' y='191.334'/>
+<use x='179.205' xlink:href='#g2-57' y='191.334'/>
+<use x='181.851' xlink:href='#g2-46' y='191.334'/>
+<use x='183.321' xlink:href='#g2-53' y='191.334'/>
+<use x='185.968' xlink:href='#g2-56' y='191.334'/>
+<use x='188.614' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 162.051 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-55' y='191.334'/>
+<use x='176.558' xlink:href='#g2-51' y='191.334'/>
+<use x='179.205' xlink:href='#g2-53' y='191.334'/>
+<use x='181.851' xlink:href='#g2-46' y='191.334'/>
+<use x='183.321' xlink:href='#g2-50' y='191.334'/>
+<use x='185.968' xlink:href='#g2-55' y='191.334'/>
+<use x='188.614' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 220.72 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-51' y='191.334'/>
+<use x='176.558' xlink:href='#g2-50' y='191.334'/>
+<use x='179.205' xlink:href='#g2-52' y='191.334'/>
+<use x='181.851' xlink:href='#g2-46' y='191.334'/>
+<use x='183.321' xlink:href='#g2-54' y='191.334'/>
+<use x='185.968' xlink:href='#g2-55' y='191.334'/>
+<use x='188.614' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 279.389 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -8.726 330.731)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-50' y='191.334'/>
+<use x='175.048' xlink:href='#g2-52' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 49.943 331.537)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-50' y='191.334'/>
+<use x='175.048' xlink:href='#g2-51' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 108.612 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-51' y='191.334'/>
+<use x='176.558' xlink:href='#g2-52' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-54' y='191.334'/>
+<use x='183.321' xlink:href='#g2-56' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 167.281 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-55' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-50' y='191.334'/>
+<use x='180.675' xlink:href='#g2-57' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 225.95 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-52' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-49' y='191.334'/>
+<use x='180.675' xlink:href='#g2-51' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 284.619 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-49' y='191.334'/>
+<use x='176.558' xlink:href='#g2-49' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-52' y='191.334'/>
+<use x='183.321' xlink:href='#g2-55' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -3.495 338.381)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-49' y='191.334'/>
+<use x='175.048' xlink:href='#g2-51' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 55.174 339.388)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-49' y='191.334'/>
+<use x='175.048' xlink:href='#g2-49' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 113.843 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-51' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-52' y='191.334'/>
+<use x='180.675' xlink:href='#g2-50' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 172.512 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-53' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-50' y='191.334'/>
+<use x='180.675' xlink:href='#g2-55' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 231.181 305.701)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-54' y='191.334'/>
+<use x='175.048' xlink:href='#g2-49' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 289.85 338.985)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-49' y='191.334'/>
+<use x='175.048' xlink:href='#g2-50' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -78.176 391.591)'>
+<use x='168.285' xlink:href='#g1-82' y='191.334'/>
+<use x='174.255' xlink:href='#g1-101' y='191.334'/>
+<use x='178.351' xlink:href='#g1-108' y='191.334'/>
+<use x='180.551' xlink:href='#g1-97' y='191.334'/>
+<use x='184.979' xlink:href='#g1-116' y='191.334'/>
+<use x='188.307' xlink:href='#g1-105' y='191.334'/>
+<use x='190.507' xlink:href='#g1-118' y='191.334'/>
+<use x='194.755' xlink:href='#g1-101' y='191.334'/>
+<use x='201.922' xlink:href='#g1-116' y='191.334'/>
+<use x='205.25' xlink:href='#g1-105' y='191.334'/>
+<use x='207.45' xlink:href='#g1-109' y='191.334'/>
+<use x='214.77' xlink:href='#g1-101' y='191.334'/>
+<use x='221.937' xlink:href='#g3-40' y='191.334'/>
+<use x='225.231' xlink:href='#g3-108' y='191.334'/>
+<use x='227.251' xlink:href='#g3-111' y='191.334'/>
+<use x='231.25' xlink:href='#g3-119' y='191.334'/>
+<use x='236.799' xlink:href='#g3-101' y='191.334'/>
+<use x='240.562' xlink:href='#g3-114' y='191.334'/>
+<use x='246.277' xlink:href='#g3-105' y='191.334'/>
+<use x='248.297' xlink:href='#g3-115' y='191.334'/>
+<use x='254.366' xlink:href='#g3-98' y='191.334'/>
+<use x='258.974' xlink:href='#g3-101' y='191.334'/>
+<use x='262.738' xlink:href='#g3-116' y='191.334'/>
+<use x='265.796' xlink:href='#g3-116' y='191.334'/>
+<use x='268.854' xlink:href='#g3-101' y='191.334'/>
+<use x='272.618' xlink:href='#g3-114' y='191.334'/>
+<use x='275.51' xlink:href='#g3-41' y='191.334'/>
+</g>
+<path d='M136.149 312.324H341.363V282.324H136.149Z' fill='#ffffff'/>
+<path d='M136.149 312.324H341.363V282.324H136.149Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path d='M139.336 292.816H142.324V284.844H139.336ZM145.313 292.816H148.301V286.836H145.313Z' fill='#993333'/>
+<path d='M139.336 292.816H142.324V284.844H139.336ZM145.313 292.816H148.301V286.836H145.313Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -40.354 114.479)'>
+<use x='195.372' xlink:href='#g3-120' y='178.327'/>
+<use x='199.274' xlink:href='#g3-109' y='178.327'/>
+<use x='205.999' xlink:href='#g3-105' y='178.327'/>
+<use x='208.019' xlink:href='#g3-58' y='178.327'/>
+<use x='210.371' xlink:href='#g0-57' y='178.327'/>
+<use x='214.076' xlink:href='#g0-57' y='178.327'/>
+</g>
+<path d='M184.145 292.816H187.133V284.844H184.145ZM190.121 292.816H193.109V286.836H190.121Z' fill='#8080bf'/>
+<path d='M184.145 292.816H187.133V284.844H184.145ZM190.121 292.816H193.109V286.836H190.121Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.441 114.036)'>
+<use x='239.382' xlink:href='#g3-116' y='178.327'/>
+<use x='242.44' xlink:href='#g3-99' y='178.327'/>
+<use x='246.204' xlink:href='#g3-58' y='178.327'/>
+<use x='248.556' xlink:href='#g0-53' y='178.327'/>
+<use x='252.261' xlink:href='#g0-55' y='178.327'/>
+</g>
+<path d='M227.356 292.816H230.344V284.844H227.356ZM233.332 292.816H236.32V286.836H233.332Z' fill='#ffb733'/>
+<path d='M227.356 292.816H230.344V284.844H227.356ZM233.332 292.816H236.32V286.836H233.332Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.04 114.479)'>
+<use x='281.863' xlink:href='#g3-106' y='178.327'/>
+<use x='284.119' xlink:href='#g3-101' y='178.327'/>
+<use x='287.882' xlink:href='#g3-58' y='178.327'/>
+<use x='290.235' xlink:href='#g0-53' y='178.327'/>
+<use x='293.94' xlink:href='#g0-56' y='178.327'/>
+</g>
+<path d='M269.106 292.816H272.094V284.844H269.106ZM275.086 292.816H278.074V286.836H275.086Z' fill='#bf80bf'/>
+<path d='M269.106 292.816H272.094V284.844H269.106ZM275.086 292.816H278.074V286.836H275.086Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -39.932 114.515)'>
+<use x='321.287' xlink:href='#g3-116' y='178.327'/>
+<use x='324.345' xlink:href='#g3-98' y='178.327'/>
+<use x='328.717' xlink:href='#g3-98' y='178.327'/>
+<use x='333.09' xlink:href='#g3-58' y='178.327'/>
+<use x='335.442' xlink:href='#g0-53' y='178.327'/>
+<use x='339.147' xlink:href='#g0-56' y='178.327'/>
+</g>
+<path d='M306.203 292.816H309.191V284.844H306.203ZM312.18 292.816H315.168V286.836H312.18Z' fill='#dfbf9f'/>
+<path d='M306.203 292.816H309.191V284.844H306.203ZM312.18 292.816H315.168V286.836H312.18Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.662 114.036)'>
+<use x='356.019' xlink:href='#g3-114' y='178.327'/>
+<use x='358.911' xlink:href='#g3-112' y='178.327'/>
+<use x='363.284' xlink:href='#g3-58' y='178.327'/>
+<use x='365.636' xlink:href='#g0-56' y='178.327'/>
+<use x='369.341' xlink:href='#g0-49' y='178.327'/>
+</g>
+<path d='M139.336 305.82H142.324V297.852H139.336ZM145.313 305.82H148.301V299.844H145.313Z' fill='#80bf80'/>
+<path d='M139.336 305.82H142.324V297.852H139.336ZM145.313 305.82H148.301V299.844H145.313Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -43.882 114.515)'>
+<use x='195.372' xlink:href='#g3-104' y='191.334'/>
+<use x='199.744' xlink:href='#g3-111' y='191.334'/>
+<use x='203.978' xlink:href='#g3-97' y='191.334'/>
+<use x='207.811' xlink:href='#g3-114' y='191.334'/>
+<use x='210.703' xlink:href='#g3-100' y='191.334'/>
+<use x='215.076' xlink:href='#g3-58' y='191.334'/>
+<use x='217.428' xlink:href='#g0-51' y='191.334'/>
+<use x='221.133' xlink:href='#g0-51' y='191.334'/>
+</g>
+<path d='M184.145 305.82H187.133V297.852H184.145ZM190.121 305.82H193.109V299.844H190.121Z' fill='#bfbf80'/>
+<path d='M184.145 305.82H187.133V297.852H184.145ZM190.121 305.82H193.109V299.844H190.121Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -43.084 114.515)'>
+<use x='239.382' xlink:href='#g3-109' y='191.334'/>
+<use x='246.107' xlink:href='#g3-101' y='191.334'/>
+<use x='249.871' xlink:href='#g3-115' y='191.334'/>
+<use x='253.117' xlink:href='#g3-104' y='191.334'/>
+<use x='257.489' xlink:href='#g3-58' y='191.334'/>
+<use x='259.842' xlink:href='#g0-50' y='191.334'/>
+<use x='263.547' xlink:href='#g0-56' y='191.334'/>
+</g>
+<path d='M227.356 305.82H230.344V297.852H227.356ZM233.332 305.82H236.32V299.844H233.332Z' fill='#339999'/>
+<path d='M227.356 305.82H230.344V297.852H227.356ZM233.332 305.82H236.32V299.844H233.332Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -42.353 114.515)'>
+<use x='281.863' xlink:href='#g3-103' y='191.334'/>
+<use x='286.097' xlink:href='#g3-108' y='191.334'/>
+<use x='288.118' xlink:href='#g3-105' y='191.334'/>
+<use x='290.138' xlink:href='#g3-98' y='191.334'/>
+<use x='294.746' xlink:href='#g3-99' y='191.334'/>
+<use x='298.509' xlink:href='#g3-58' y='191.334'/>
+<use x='300.862' xlink:href='#g0-52' y='191.334'/>
+<use x='304.567' xlink:href='#g0-55' y='191.334'/>
+</g>
+<path d='M269.106 305.82H272.094V297.852H269.106ZM275.086 305.82H278.074V299.844H275.086Z' fill='#bf8080'/>
+<path d='M269.106 305.82H272.094V297.852H269.106ZM275.086 305.82H278.074V299.844H275.086Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -40.026 114.479)'>
+<use x='321.287' xlink:href='#g3-115' y='191.334'/>
+<use x='324.533' xlink:href='#g3-109' y='191.334'/>
+<use x='331.258' xlink:href='#g3-105' y='191.334'/>
+<use x='333.278' xlink:href='#g3-58' y='191.334'/>
+<use x='335.63' xlink:href='#g0-54' y='191.334'/>
+<use x='339.335' xlink:href='#g0-55' y='191.334'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='193.064pt' version='1.1' viewBox='52.938 51.67 381.624 193.064' width='381.624pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip7'>
+<path d='M82.148 228.594H434.164V60.828H82.148Z'/>
+</clipPath>
+<use id='g3-40' transform='scale(1.143)' xlink:href='#g0-40'/>
+<use id='g3-41' transform='scale(1.143)' xlink:href='#g0-41'/>
+<use id='g3-78' transform='scale(1.143)' xlink:href='#g0-78'/>
+<use id='g3-97' transform='scale(1.143)' xlink:href='#g0-97'/>
+<use id='g3-98' transform='scale(1.143)' xlink:href='#g0-98'/>
+<use id='g3-99' transform='scale(1.143)' xlink:href='#g0-99'/>
+<use id='g3-100' transform='scale(1.143)' xlink:href='#g0-100'/>
+<use id='g3-101' transform='scale(1.143)' xlink:href='#g0-101'/>
+<use id='g3-102' transform='scale(1.143)' xlink:href='#g0-102'/>
+<use id='g3-105' transform='scale(1.143)' xlink:href='#g0-105'/>
+<use id='g3-108' transform='scale(1.143)' xlink:href='#g0-108'/>
+<use id='g3-109' transform='scale(1.143)' xlink:href='#g0-109'/>
+<use id='g3-110' transform='scale(1.143)' xlink:href='#g0-110'/>
+<use id='g3-111' transform='scale(1.143)' xlink:href='#g0-111'/>
+<use id='g3-112' transform='scale(1.143)' xlink:href='#g0-112'/>
+<use id='g3-114' transform='scale(1.143)' xlink:href='#g0-114'/>
+<use id='g3-115' transform='scale(1.143)' xlink:href='#g0-115'/>
+<use id='g3-116' transform='scale(1.143)' xlink:href='#g0-116'/>
+<use id='g3-119' transform='scale(1.143)' xlink:href='#g0-119'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g1-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g1-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g1-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g1-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g1-108'/>
+<path d='M1.462 -1.91C1.462 -2.851 2.197 -3.425 3.013 -3.434V-4.08C2.367 -4.071 1.775 -3.748 1.408 -3.219V-4.035H0.744V0H1.462V-1.91Z' id='g1-114'/>
+<path d='M3.165 -3.847C2.609 -4.098 2.197 -4.133 1.829 -4.133C1.623 -4.133 0.305 -4.133 0.305 -2.95C0.305 -2.52 0.565 -2.251 0.664 -2.152C1.004 -1.856 1.237 -1.811 1.847 -1.695C2.134 -1.641 2.645 -1.542 2.645 -1.085C2.645 -0.502 1.919 -0.502 1.802 -0.502C1.273 -0.502 0.762 -0.681 0.377 -0.95L0.26 -0.296C0.798 -0.009 1.345 0.099 1.802 0.099C2.385 0.099 3.318 -0.09 3.318 -1.157C3.318 -1.47 3.192 -1.784 2.878 -2.053C2.573 -2.313 2.304 -2.367 1.748 -2.475C1.426 -2.537 0.977 -2.618 0.977 -3.04C0.977 -3.569 1.614 -3.569 1.748 -3.569C2.403 -3.569 2.789 -3.362 3.049 -3.219L3.165 -3.847Z' id='g1-115'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g1-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g1-118'/>
+<use id='g2-46' transform='scale(0.714)' xlink:href='#g0-46'/>
+<use id='g2-48' transform='scale(0.714)' xlink:href='#g0-48'/>
+<use id='g2-49' transform='scale(0.714)' xlink:href='#g0-49'/>
+<use id='g2-50' transform='scale(0.714)' xlink:href='#g0-50'/>
+<use id='g2-51' transform='scale(0.714)' xlink:href='#g0-51'/>
+<use id='g2-52' transform='scale(0.714)' xlink:href='#g0-52'/>
+<use id='g2-53' transform='scale(0.714)' xlink:href='#g0-53'/>
+<use id='g2-54' transform='scale(0.714)' xlink:href='#g0-54'/>
+<use id='g2-55' transform='scale(0.714)' xlink:href='#g0-55'/>
+<use id='g2-56' transform='scale(0.714)' xlink:href='#g0-56'/>
+<use id='g2-57' transform='scale(0.714)' xlink:href='#g0-57'/>
+<use id='g2-120' transform='scale(0.714)' xlink:href='#g0-120'/>
+<path d='M2.127 -5.23C2.008 -5.23 1.995 -5.23 1.911 -5.154C1.032 -4.387 0.586 -3.145 0.586 -1.743C0.586 -0.425 0.983 0.844 1.904 1.653C1.995 1.743 2.008 1.743 2.127 1.743H2.462C2.441 1.73 1.764 1.151 1.444 0.063C1.276 -0.481 1.193 -1.053 1.193 -1.743C1.193 -4.156 2.322 -5.112 2.462 -5.23H2.127Z' id='g0-40'/>
+<path d='M0.746 1.743C0.865 1.743 0.879 1.743 0.962 1.667C1.841 0.9 2.287 -0.342 2.287 -1.743C2.287 -3.062 1.89 -4.331 0.969 -5.14C0.879 -5.23 0.865 -5.23 0.746 -5.23H0.411C0.432 -5.216 1.109 -4.638 1.43 -3.55C1.597 -3.006 1.681 -2.434 1.681 -1.743C1.681 0.669 0.551 1.625 0.411 1.743H0.746Z' id='g0-41'/>
+<path d='M1.339 -0.628H0.711V0H1.339V-0.628Z' id='g0-46'/>
+<path d='M3.403 -2.267C3.403 -2.601 3.403 -3.417 3.075 -3.989C2.72 -4.617 2.183 -4.721 1.848 -4.721C1.534 -4.721 0.99 -4.624 0.642 -4.024C0.307 -3.466 0.293 -2.706 0.293 -2.267C0.293 -1.75 0.321 -1.116 0.614 -0.586C0.921 -0.021 1.437 0.146 1.848 0.146C2.545 0.146 2.929 -0.258 3.138 -0.697C3.382 -1.193 3.403 -1.834 3.403 -2.267ZM1.848 -0.314C1.555 -0.314 1.22 -0.481 1.046 -0.983C0.907 -1.409 0.9 -1.848 0.9 -2.357C0.9 -2.999 0.9 -4.261 1.848 -4.261S2.797 -2.999 2.797 -2.357C2.797 -1.897 2.797 -1.374 2.629 -0.928C2.434 -0.425 2.078 -0.314 1.848 -0.314Z' id='g0-48'/>
+<path d='M2.239 -4.721H2.085C1.632 -4.303 1.06 -4.275 0.642 -4.261V-3.822C0.914 -3.829 1.262 -3.843 1.611 -3.982V-0.439H0.683V0H3.166V-0.439H2.239V-4.721Z' id='g0-49'/>
+<path d='M1.974 -0.537C1.89 -0.537 1.806 -0.53 1.723 -0.53H0.928L2.008 -1.485C2.134 -1.597 2.476 -1.855 2.608 -1.967C2.915 -2.246 3.327 -2.608 3.327 -3.215C3.327 -4.003 2.741 -4.721 1.743 -4.721C1.004 -4.721 0.544 -4.324 0.307 -3.612L0.635 -3.201C0.795 -3.787 1.039 -4.24 1.646 -4.24C2.232 -4.24 2.678 -3.829 2.678 -3.201C2.678 -2.622 2.336 -2.294 1.918 -1.897C1.778 -1.757 1.402 -1.444 1.255 -1.304C1.053 -1.123 0.572 -0.656 0.37 -0.481V0H3.327V-0.537H1.974Z' id='g0-50'/>
+<path d='M0.697 -3.578C0.983 -4.135 1.485 -4.289 1.82 -4.289C2.232 -4.289 2.538 -4.052 2.538 -3.654C2.538 -3.285 2.287 -2.831 1.757 -2.741C1.723 -2.734 1.695 -2.734 1.234 -2.699V-2.239H1.778C2.441 -2.239 2.685 -1.716 2.685 -1.276C2.685 -0.732 2.35 -0.314 1.806 -0.314C1.311 -0.314 0.746 -0.551 0.398 -0.997L0.307 -0.544C0.711 -0.091 1.276 0.146 1.82 0.146C2.734 0.146 3.389 -0.537 3.389 -1.269C3.389 -1.841 2.929 -2.301 2.378 -2.462C2.908 -2.734 3.18 -3.201 3.18 -3.654C3.18 -4.247 2.573 -4.721 1.827 -4.721C1.213 -4.721 0.704 -4.4 0.411 -3.982L0.697 -3.578Z' id='g0-51'/>
+<path d='M2.762 -1.165H3.487V-1.625H2.762V-4.575H2.071L0.209 -1.625V-1.165H2.162V0H2.762V-1.165ZM0.802 -1.625C1.011 -1.953 2.211 -3.815 2.211 -4.233V-1.625H0.802Z' id='g0-52'/>
+<path d='M1.144 -4.094H3.075V-4.575H0.586V-1.967H1.095C1.262 -2.343 1.59 -2.511 1.904 -2.511C2.19 -2.511 2.622 -2.315 2.622 -1.43C2.622 -0.516 2.043 -0.314 1.688 -0.314C1.227 -0.314 0.781 -0.558 0.544 -0.955L0.279 -0.537C0.621 -0.112 1.137 0.146 1.688 0.146C2.608 0.146 3.327 -0.565 3.327 -1.416C3.327 -2.28 2.685 -2.971 1.918 -2.971C1.618 -2.971 1.353 -2.866 1.144 -2.692V-4.094Z' id='g0-53'/>
+<path d='M3.062 -4.582C2.685 -4.721 2.42 -4.721 2.287 -4.721C1.227 -4.721 0.307 -3.724 0.307 -2.253C0.307 -0.363 1.158 0.146 1.862 0.146C2.427 0.146 2.72 -0.119 2.936 -0.342C3.382 -0.816 3.389 -1.311 3.389 -1.555C3.389 -2.469 2.894 -3.229 2.218 -3.229C1.534 -3.229 1.165 -2.873 0.962 -2.671C1.053 -3.626 1.541 -4.289 2.294 -4.289C2.434 -4.289 2.713 -4.275 3.062 -4.142V-4.582ZM0.969 -1.534C0.969 -1.576 0.969 -1.681 0.976 -1.716C0.976 -2.19 1.276 -2.769 1.897 -2.769C2.748 -2.769 2.748 -1.792 2.748 -1.555C2.748 -1.29 2.748 -0.997 2.559 -0.704C2.392 -0.453 2.183 -0.314 1.862 -0.314C1.123 -0.314 1.004 -1.227 0.969 -1.534Z' id='g0-54'/>
+<path d='M1.723 -4.038C1.806 -4.038 1.89 -4.045 1.974 -4.045H2.852C1.792 -3.006 1.116 -1.548 1.116 0.07H1.771C1.771 -1.967 2.762 -3.431 3.389 -4.087V-4.575H0.307V-4.038H1.723Z' id='g0-55'/>
+<path d='M2.385 -2.469C2.845 -2.615 3.285 -2.985 3.285 -3.501C3.285 -4.135 2.678 -4.721 1.848 -4.721S0.411 -4.135 0.411 -3.501C0.411 -2.978 0.865 -2.608 1.311 -2.469C0.697 -2.28 0.307 -1.806 0.307 -1.269C0.307 -0.523 0.969 0.146 1.848 0.146S3.389 -0.523 3.389 -1.269C3.389 -1.806 2.992 -2.28 2.385 -2.469ZM1.848 -2.699C1.353 -2.699 0.948 -2.985 0.948 -3.494C0.948 -3.94 1.262 -4.289 1.848 -4.289C2.427 -4.289 2.748 -3.94 2.748 -3.494C2.748 -2.999 2.357 -2.699 1.848 -2.699ZM1.848 -0.314C1.367 -0.314 0.941 -0.621 0.941 -1.276C0.941 -1.904 1.346 -2.239 1.848 -2.239S2.755 -1.897 2.755 -1.276C2.755 -0.621 2.322 -0.314 1.848 -0.314Z' id='g0-56'/>
+<path d='M0.537 -0.174C0.879 0.077 1.193 0.146 1.52 0.146C2.497 0.146 3.389 -0.837 3.389 -2.336C3.389 -4.24 2.545 -4.721 1.876 -4.721C1.255 -4.721 0.969 -4.428 0.767 -4.226C0.321 -3.773 0.307 -3.292 0.307 -3.02C0.307 -2.12 0.795 -1.346 1.478 -1.346C2.267 -1.346 2.699 -1.869 2.734 -1.911C2.636 -0.802 2.092 -0.314 1.52 -0.314C1.158 -0.314 0.934 -0.446 0.774 -0.579L0.537 -0.174ZM2.713 -3.027C2.72 -2.985 2.72 -2.915 2.72 -2.873C2.72 -2.357 2.406 -1.806 1.799 -1.806C1.534 -1.806 1.325 -1.883 1.144 -2.169C0.962 -2.441 0.948 -2.706 0.948 -3.02C0.948 -3.292 0.948 -3.605 1.165 -3.912C1.311 -4.122 1.52 -4.289 1.869 -4.289C2.545 -4.289 2.692 -3.473 2.713 -3.027Z' id='g0-57'/>
+<path d='M1.646 -4.84H0.697V0H1.283V-4.289H1.29L3.578 0H4.526V-4.84H3.94V-0.551H3.933L1.646 -4.84Z' id='g0-78'/>
+<path d='M2.971 -2.008C2.971 -2.72 2.427 -3.201 1.736 -3.201C1.297 -3.201 0.962 -3.11 0.572 -2.901L0.614 -2.392C0.844 -2.545 1.186 -2.755 1.736 -2.755C2.043 -2.755 2.364 -2.525 2.364 -2.001V-1.723C1.332 -1.688 0.314 -1.471 0.314 -0.823C0.314 -0.474 0.551 0.07 1.165 0.07C1.465 0.07 2.015 0.007 2.385 -0.265V0H2.971V-2.008ZM2.364 -0.99C2.364 -0.851 2.364 -0.669 2.12 -0.523C1.897 -0.398 1.625 -0.391 1.548 -0.391C1.165 -0.391 0.872 -0.565 0.872 -0.83C0.872 -1.276 2.05 -1.318 2.364 -1.332V-0.99Z' id='g0-97'/>
+<path d='M1.179 -4.84H0.593V0H1.2V-0.328C1.353 -0.195 1.688 0.07 2.197 0.07C2.957 0.07 3.571 -0.642 3.571 -1.555C3.571 -2.399 3.089 -3.166 2.392 -3.166C1.953 -3.166 1.527 -3.027 1.179 -2.769V-4.84ZM1.2 -2.197C1.2 -2.308 1.2 -2.392 1.444 -2.552C1.548 -2.615 1.736 -2.706 1.974 -2.706C2.441 -2.706 2.964 -2.392 2.964 -1.555C2.964 -0.704 2.385 -0.391 1.897 -0.391C1.639 -0.391 1.395 -0.509 1.2 -0.823V-2.197Z' id='g0-98'/>
+<path d='M3.034 -0.76C2.685 -0.537 2.308 -0.411 1.876 -0.411C1.234 -0.411 0.858 -0.928 0.858 -1.555C0.858 -2.092 1.137 -2.72 1.897 -2.72C2.371 -2.72 2.594 -2.622 2.95 -2.399L3.041 -2.901C2.622 -3.11 2.441 -3.201 1.897 -3.201C0.851 -3.201 0.251 -2.357 0.251 -1.548C0.251 -0.697 0.921 0.07 1.869 0.07C2.357 0.07 2.776 -0.077 3.075 -0.251L3.034 -0.76Z' id='g0-99'/>
+<path d='M3.229 -4.84H2.643V-2.797C2.197 -3.124 1.743 -3.166 1.541 -3.166C0.809 -3.166 0.251 -2.434 0.251 -1.548S0.802 0.07 1.52 0.07C1.953 0.07 2.357 -0.126 2.622 -0.363V0H3.229V-4.84ZM2.622 -0.865C2.448 -0.579 2.183 -0.391 1.848 -0.391C1.36 -0.391 0.858 -0.732 0.858 -1.541C0.858 -2.413 1.451 -2.706 1.925 -2.706C2.204 -2.706 2.441 -2.587 2.622 -2.35V-0.865Z' id='g0-100'/>
+<path d='M2.999 -0.76C2.608 -0.481 2.169 -0.391 1.869 -0.391C1.262 -0.391 0.802 -0.886 0.781 -1.527H3.068C3.068 -1.848 3.034 -2.315 2.762 -2.713C2.511 -3.068 2.092 -3.201 1.75 -3.201C0.9 -3.201 0.244 -2.455 0.244 -1.569C0.244 -0.676 0.941 0.07 1.862 0.07C2.267 0.07 2.685 -0.049 3.041 -0.265L2.999 -0.76ZM0.83 -1.946C0.99 -2.504 1.402 -2.741 1.75 -2.741C2.057 -2.741 2.511 -2.594 2.643 -1.946H0.83Z' id='g0-101'/>
+<path d='M1.325 -2.657H2.12V-3.096H1.304V-3.898C1.304 -4.38 1.743 -4.449 1.974 -4.449C2.12 -4.449 2.308 -4.428 2.566 -4.331V-4.84C2.385 -4.882 2.169 -4.91 1.981 -4.91C1.262 -4.91 0.739 -4.394 0.739 -3.703V-3.096H0.202V-2.657H0.739V0H1.325V-2.657Z' id='g0-102'/>
+<path d='M1.227 -4.784H0.523V-4.08H1.227V-4.784ZM1.172 -3.096H0.586V0H1.172V-3.096Z' id='g0-105'/>
+<path d='M1.172 -4.84H0.586V0H1.172V-4.84Z' id='g0-108'/>
+<path d='M5.3 -2.064C5.3 -2.608 5.14 -3.166 4.282 -3.166C3.696 -3.166 3.333 -2.824 3.166 -2.601C3.096 -2.79 2.922 -3.166 2.225 -3.166C1.827 -3.166 1.444 -3.006 1.137 -2.636V-3.145H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-1.695C3.243 -2.155 3.438 -2.706 3.975 -2.706C4.693 -2.706 4.693 -2.218 4.693 -2.015V0H5.3V-2.064Z' id='g0-109'/>
+<path d='M3.243 -2.064C3.243 -2.608 3.082 -3.166 2.225 -3.166C1.827 -3.166 1.444 -3.006 1.137 -2.636V-3.145H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-2.064Z' id='g0-110'/>
+<path d='M3.487 -1.527C3.487 -2.448 2.755 -3.201 1.848 -3.201S0.209 -2.441 0.209 -1.527C0.209 -0.642 0.948 0.07 1.848 0.07C2.755 0.07 3.487 -0.642 3.487 -1.527ZM1.848 -0.411C1.297 -0.411 0.816 -0.816 0.816 -1.604S1.332 -2.741 1.848 -2.741C2.371 -2.741 2.88 -2.378 2.88 -1.604C2.88 -0.809 2.385 -0.411 1.848 -0.411Z' id='g0-111'/>
+<path d='M1.2 -0.328C1.569 0.007 1.967 0.07 2.204 0.07C2.943 0.07 3.571 -0.635 3.571 -1.555C3.571 -2.392 3.11 -3.166 2.42 -3.166C2.106 -3.166 1.583 -3.075 1.179 -2.762V-3.096H0.593V1.353H1.2V-0.328ZM1.2 -2.315C1.36 -2.511 1.632 -2.685 1.967 -2.685C2.525 -2.685 2.964 -2.169 2.964 -1.555C2.964 -0.865 2.441 -0.391 1.897 -0.391C1.792 -0.391 1.618 -0.404 1.437 -0.551C1.227 -0.711 1.2 -0.816 1.2 -0.948V-2.315Z' id='g0-112'/>
+<path d='M1.179 -1.485C1.179 -2.239 1.806 -2.643 2.42 -2.65V-3.166C1.834 -3.159 1.409 -2.873 1.13 -2.504V-3.145H0.593V0H1.179V-1.485Z' id='g0-114'/>
+<path d='M2.545 -2.985C2.071 -3.18 1.723 -3.201 1.471 -3.201C1.297 -3.201 0.244 -3.201 0.244 -2.273C0.244 -1.946 0.425 -1.764 0.516 -1.681C0.76 -1.437 1.053 -1.381 1.423 -1.311C1.75 -1.248 2.127 -1.179 2.127 -0.844C2.127 -0.404 1.548 -0.404 1.451 -0.404C1.004 -0.404 0.586 -0.565 0.307 -0.76L0.209 -0.237C0.446 -0.119 0.872 0.07 1.451 0.07C1.764 0.07 2.071 0.021 2.329 -0.167C2.587 -0.363 2.671 -0.669 2.671 -0.907C2.671 -1.032 2.657 -1.304 2.364 -1.569C2.106 -1.799 1.855 -1.848 1.52 -1.911C1.109 -1.988 0.788 -2.05 0.788 -2.357C0.788 -2.755 1.297 -2.755 1.402 -2.755C1.799 -2.755 2.106 -2.671 2.455 -2.49L2.545 -2.985Z' id='g0-115'/>
+<path d='M1.311 -2.657H2.343V-3.096H1.311V-3.982H0.774V-3.096H0.139V-2.657H0.753V-0.893C0.753 -0.425 0.872 0.07 1.374 0.07S2.26 -0.091 2.469 -0.188L2.35 -0.635C2.12 -0.467 1.876 -0.411 1.681 -0.411C1.388 -0.411 1.311 -0.697 1.311 -1.018V-2.657Z' id='g0-116'/>
+<path d='M4.951 -3.096H4.407C4.345 -2.901 3.954 -1.723 3.738 -0.997C3.682 -0.795 3.612 -0.572 3.592 -0.411H3.585C3.543 -0.697 3.299 -1.451 3.285 -1.499L2.769 -3.096H2.239C2.036 -2.497 1.513 -0.934 1.458 -0.425H1.451C1.395 -0.921 0.879 -2.462 0.767 -2.797C0.711 -2.964 0.711 -2.978 0.676 -3.096H0.105L1.123 0H1.709C1.716 -0.028 1.904 -0.579 2.148 -1.353C2.253 -1.695 2.462 -2.364 2.497 -2.671L2.504 -2.678C2.518 -2.532 2.559 -2.378 2.608 -2.204S2.706 -1.841 2.755 -1.681L3.292 0H3.933L4.951 -3.096Z' id='g0-119'/>
+<path d='M1.932 -1.597L3.285 -3.096H2.671L1.681 -1.953L0.669 -3.096H0.042L1.437 -1.597L0 0H0.621L1.681 -1.311L2.783 0H3.41L1.932 -1.597Z' id='g0-120'/>
+</defs>
+<g id='page7'>
+<path d='M140.82 237.449V228.594M199.488 237.449V228.594M258.156 237.449V228.594M316.824 237.449V228.594M375.496 237.449V228.594M140.82 51.969V60.828M199.488 51.969V60.828M258.156 51.969V60.828M316.824 51.969V60.828M375.496 51.969V60.828' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M111.484 232.844V228.594M170.152 232.844V228.594M228.824 232.844V228.594M287.492 232.844V228.594M346.16 232.844V228.594M404.828 232.844V228.594M111.484 56.574V60.828M170.152 56.574V60.828M228.824 56.574V60.828M287.492 56.574V60.828M346.16 56.574V60.828M404.828 56.574V60.828' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 228.594H86.402M82.148 195.039H86.402M82.148 161.484H86.402M82.148 127.934H86.402M82.148 94.379H86.402M82.148 60.828H86.402M434.164 228.594H429.91M434.164 195.039H429.91M434.164 161.484H429.91M434.164 127.934H429.91M434.164 94.379H429.91M434.164 60.828H429.91' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 228.594V60.828H434.164V228.594H82.148Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -11.54 34.954)'>
+<use x='114.487' xlink:href='#g3-99' y='208.035'/>
+<use x='118.25' xlink:href='#g3-102' y='208.035'/>
+<use x='120.838' xlink:href='#g3-114' y='208.035'/>
+<use x='123.73' xlink:href='#g3-97' y='208.035'/>
+<use x='127.798' xlink:href='#g3-99' y='208.035'/>
+</g>
+<g transform='matrix(1 0 0 1 45.565 34.954)'>
+<use x='114.487' xlink:href='#g3-108' y='208.035'/>
+<use x='116.507' xlink:href='#g3-101' y='208.035'/>
+<use x='120.271' xlink:href='#g3-97' y='208.035'/>
+<use x='124.339' xlink:href='#g3-110' y='208.035'/>
+<use x='128.711' xlink:href='#g3-78' y='208.035'/>
+</g>
+<g transform='matrix(1 0 0 1 106.188 34.954)'>
+<use x='114.487' xlink:href='#g3-114' y='208.035'/>
+<use x='117.379' xlink:href='#g3-101' y='208.035'/>
+<use x='121.142' xlink:href='#g3-100' y='208.035'/>
+<use x='125.515' xlink:href='#g3-105' y='208.035'/>
+<use x='127.535' xlink:href='#g3-115' y='208.035'/>
+</g>
+<g transform='matrix(1 0 0 1 159.716 34.954)'>
+<use x='114.487' xlink:href='#g3-108' y='208.035'/>
+<use x='116.507' xlink:href='#g3-97' y='208.035'/>
+<use x='120.34' xlink:href='#g3-114' y='208.035'/>
+<use x='123.232' xlink:href='#g3-115' y='208.035'/>
+<use x='126.478' xlink:href='#g3-111' y='208.035'/>
+<use x='130.712' xlink:href='#g3-110' y='208.035'/>
+<use x='135.085' xlink:href='#g3-78' y='208.035'/>
+</g>
+<g transform='matrix(1 0 0 1 215.596 34.954)'>
+<use x='114.487' xlink:href='#g3-109' y='208.035'/>
+<use x='121.211' xlink:href='#g3-115' y='208.035'/>
+<use x='124.458' xlink:href='#g3-116' y='208.035'/>
+<use x='127.516' xlink:href='#g3-114' y='208.035'/>
+<use x='130.408' xlink:href='#g3-101' y='208.035'/>
+<use x='134.171' xlink:href='#g3-115' y='208.035'/>
+<use x='137.418' xlink:href='#g3-115' y='208.035'/>
+<use x='140.664' xlink:href='#g3-78' y='208.035'/>
+</g>
+<g transform='matrix(1 0 0 1 277.158 34.954)'>
+<use x='114.487' xlink:href='#g3-114' y='208.035'/>
+<use x='117.379' xlink:href='#g3-112' y='208.035'/>
+<use x='121.751' xlink:href='#g3-116' y='208.035'/>
+<use x='124.809' xlink:href='#g3-101' y='208.035'/>
+<use x='128.573' xlink:href='#g3-115' y='208.035'/>
+<use x='131.819' xlink:href='#g3-116' y='208.035'/>
+<use x='134.877' xlink:href='#g3-78' y='208.035'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 22.192)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-120' y='208.035'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -11.361)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-53' y='208.035'/>
+<use x='121.25' xlink:href='#g2-120' y='208.035'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -44.915)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-120' y='208.035'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -78.468)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-53' y='208.035'/>
+<use x='121.25' xlink:href='#g2-120' y='208.035'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -112.021)'>
+<use x='114.487' xlink:href='#g2-50' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-120' y='208.035'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -145.574)'>
+<use x='114.487' xlink:href='#g2-50' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-53' y='208.035'/>
+<use x='121.25' xlink:href='#g2-120' y='208.035'/>
+</g>
+<path clip-path='url(#clip7)' d='M82.148 161.484H434.164' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M88.945 228.594H92.18V161.484H88.945ZM147.613 228.594H150.852V161.484H147.613ZM206.281 228.594H209.52V161.484H206.281ZM264.949 228.594H268.188V161.484H264.949ZM323.621 228.594H326.859V161.484H323.621ZM382.289 228.594H385.527V161.484H382.289Z' fill='#ffffff'/>
+<path clip-path='url(#clip7)' d='M88.945 228.594H92.18V161.484H88.945ZM147.613 228.594H150.852V161.484H147.613ZM206.281 228.594H209.52V161.484H206.281ZM264.949 228.594H268.188V161.484H264.949ZM323.621 228.594H326.859V161.484H323.621ZM382.289 228.594H385.527V161.484H382.289Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M90.563 161.484V161.484' fill='#ffffff'/>
+<path clip-path='url(#clip7)' d='M88.57 161.484H92.555' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M90.563 161.484V161.484' fill='#ffffff'/>
+<path clip-path='url(#clip7)' d='M88.57 161.484H92.555' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M149.231 161.484V161.484' fill='#ffffff'/>
+<path clip-path='url(#clip7)' d='M147.238 161.484H151.223' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M149.231 161.484V161.484' fill='#ffffff'/>
+<path clip-path='url(#clip7)' d='M147.238 161.484H151.223' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M207.902 161.484V161.484' fill='#ffffff'/>
+<path clip-path='url(#clip7)' d='M205.906 161.484H209.894' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M207.902 161.484V161.484' fill='#ffffff'/>
+<path clip-path='url(#clip7)' d='M205.906 161.484H209.894' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M266.57 161.484V161.484' fill='#ffffff'/>
+<path clip-path='url(#clip7)' d='M264.578 161.484H268.562' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M266.57 161.484V161.484' fill='#ffffff'/>
+<path clip-path='url(#clip7)' d='M264.578 161.484H268.562' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M325.238 161.484V161.484' fill='#ffffff'/>
+<path clip-path='url(#clip7)' d='M323.246 161.484H327.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M325.238 161.484V161.484' fill='#ffffff'/>
+<path clip-path='url(#clip7)' d='M323.246 161.484H327.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M383.906 161.484V161.484' fill='#ffffff'/>
+<path clip-path='url(#clip7)' d='M381.914 161.484H385.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M383.906 161.484V161.484' fill='#ffffff'/>
+<path clip-path='url(#clip7)' d='M381.914 161.484H385.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M94.176 228.594H97.41V74.719H94.176ZM152.844 228.594H156.082V172.559H152.844ZM211.512 228.594H214.75V145.852H211.512ZM270.18 228.594H273.418V154.773H270.18ZM328.852 228.594H332.09V193.43H328.852ZM387.52 228.594H390.758V191.148H387.52Z' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M94.176 228.594H97.41V74.719H94.176ZM152.844 228.594H156.082V172.559H152.844ZM211.512 228.594H214.75V145.852H211.512ZM270.18 228.594H273.418V154.773H270.18ZM328.852 228.594H332.09V193.43H328.852ZM387.52 228.594H390.758V191.148H387.52Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M95.793 74.719V74.719' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M93.801 74.719H97.785' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M95.793 74.719V74.719' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M93.801 74.719H97.785' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M154.461 172.559V172.559' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M152.469 172.558H156.454' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M154.461 172.559V172.559' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M152.469 172.558H156.454' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M213.133 145.852V145.852' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M211.137 145.851H215.125' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M213.133 145.852V145.852' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M211.137 145.851H215.125' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M271.801 154.773V154.773' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M269.809 154.773H273.793' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M271.801 154.773V154.773' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M269.809 154.773H273.793' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M330.469 193.43V193.43' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M328.477 193.429H332.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M330.469 193.43V193.43' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M328.477 193.429H332.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M389.137 191.148V191.148' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M387.145 191.149H391.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M389.137 191.148V191.148' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M387.145 191.149H391.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M99.406 228.594H102.641V138.133H99.406ZM158.074 228.594H161.313V162.961H158.074ZM216.742 228.594H219.981V157.797H216.742ZM275.41 228.594H278.649V141.02H275.41ZM334.082 228.594H337.32V191.816H334.082ZM392.75 228.594H395.988V179.871H392.75Z' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M99.406 228.594H102.641V138.133H99.406ZM158.074 228.594H161.313V162.961H158.074ZM216.742 228.594H219.981V157.797H216.742ZM275.41 228.594H278.649V141.02H275.41ZM334.082 228.594H337.32V191.816H334.082ZM392.75 228.594H395.988V179.871H392.75Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M101.024 138.133V138.133' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M99.031 138.133H103.016' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M101.024 138.133V138.133' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M99.031 138.133H103.016' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M159.691 162.961V162.961' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M157.699 162.961H161.684' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M159.691 162.961V162.961' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M157.699 162.961H161.684' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M218.363 157.797V157.797' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M216.367 157.797H220.355' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M218.363 157.797V157.797' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M216.367 157.797H220.355' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M277.031 141.02V141.02' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M275.039 141.02H279.023' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M277.031 141.02V141.02' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M275.039 141.02H279.023' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M335.699 191.816V191.816' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M333.707 191.816H337.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M335.699 191.816V191.816' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M333.707 191.816H337.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M394.367 179.871V179.871' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M392.375 179.871H396.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M394.367 179.871V179.871' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M392.375 179.871H396.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M104.637 228.594H107.871V140.883H104.637ZM163.305 228.594H166.543V152.695H163.305ZM221.973 228.594H225.211V115.184H221.973ZM280.641 228.594H283.879V167.793H280.641ZM339.313 228.594H342.551V189.738H339.313ZM397.981 228.594H401.219V194.367H397.981Z' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M104.637 228.594H107.871V140.883H104.637ZM163.305 228.594H166.543V152.695H163.305ZM221.973 228.594H225.211V115.184H221.973ZM280.641 228.594H283.879V167.793H280.641ZM339.313 228.594H342.551V189.738H339.313ZM397.981 228.594H401.219V194.367H397.981Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M106.254 140.883V140.883' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M104.261 140.882H108.246' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M106.254 140.883V140.883' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M104.261 140.882H108.246' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M164.922 152.695V152.695' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M162.929 152.695H166.914' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M164.922 152.695V152.695' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M162.929 152.695H166.914' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M223.594 115.184V115.184' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M221.597 115.184H225.585' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M223.594 115.184V115.184' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M221.597 115.184H225.585' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M282.262 167.793V167.793' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M280.269 167.793H284.253' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M282.262 167.793V167.793' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M280.269 167.793H284.253' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M340.93 189.738V189.738' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M338.937 189.739H342.921' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M340.93 189.738V189.738' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M338.937 189.739H342.921' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M399.598 194.367V194.367' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M397.605 194.367H401.589' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M399.598 194.367V194.367' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M397.605 194.367H401.589' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M109.867 228.594H113.102V163.031H109.867ZM168.535 228.594H171.774V157.191H168.535ZM227.203 228.594H230.442V142.094H227.203ZM285.871 228.594H289.109V150.414H285.871ZM344.543 228.594H347.781V150.949H344.543ZM403.211 228.594H406.449V103.105H403.211Z' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M109.867 228.594H113.102V163.031H109.867ZM168.535 228.594H171.774V157.191H168.535ZM227.203 228.594H230.442V142.094H227.203ZM285.871 228.594H289.109V150.414H285.871ZM344.543 228.594H347.781V150.949H344.543ZM403.211 228.594H406.449V103.105H403.211Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M111.484 163.031V163.031' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M109.492 163.031H113.477' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M111.484 163.031V163.031' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M109.492 163.031H113.477' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M170.152 157.191V157.191' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M168.16 157.191H172.145' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M170.152 157.191V157.191' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M168.16 157.191H172.145' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M228.824 142.094V142.094' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M226.828 142.093H230.816' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M228.824 142.094V142.094' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M226.828 142.093H230.816' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M287.492 150.414V150.414' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M285.5 150.414H289.484' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M287.492 150.414V150.414' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M285.5 150.414H289.484' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M346.16 150.949V150.949' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M344.168 150.949H348.152' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M346.16 150.949V150.949' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M344.168 150.949H348.152' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M404.828 103.105V103.105' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M402.836 103.106H406.82' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M404.828 103.105V103.105' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M402.836 103.106H406.82' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M115.098 228.594H118.332V133.906H115.098ZM173.766 228.594H177.004V148.133H173.766ZM232.434 228.594H235.672V144.91H232.434ZM291.102 228.594H294.34V160.48H291.102ZM349.774 228.594H353.012V168.801H349.774ZM408.442 228.594H411.68V181.484H408.442Z' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M115.098 228.594H118.332V133.906H115.098ZM173.766 228.594H177.004V148.133H173.766ZM232.434 228.594H235.672V144.91H232.434ZM291.102 228.594H294.34V160.48H291.102ZM349.774 228.594H353.012V168.801H349.774ZM408.442 228.594H411.68V181.484H408.442Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M116.715 133.906V133.906' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M114.722 133.906H118.707' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M116.715 133.906V133.906' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M114.722 133.906H118.707' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M175.383 148.133V148.133' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M173.39 148.133H177.375' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M175.383 148.133V148.133' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M173.39 148.133H177.375' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M234.055 144.91V144.91' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M232.058 144.91H236.046' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M234.055 144.91V144.91' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M232.058 144.91H236.046' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M292.723 160.48V160.48' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M290.73 160.48H294.714' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M292.723 160.48V160.48' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M290.73 160.48H294.714' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M351.391 168.801V168.801' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M349.398 168.801H353.382' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M351.391 168.801V168.801' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M349.398 168.801H353.382' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M410.059 181.484V181.484' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M408.066 181.485H412.05' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M410.059 181.484V181.484' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M408.066 181.485H412.05' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M120.328 228.594H123.563V140.48H120.328ZM178.996 228.594H182.234V159.473H178.996ZM237.664 228.594H240.902V159.406H237.664ZM296.332 228.594H299.57V157.797H296.332ZM355.004 228.594H358.238V172.355H355.004ZM413.672 228.594H416.91V184.637H413.672Z' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M120.328 228.594H123.563V140.48H120.328ZM178.996 228.594H182.234V159.473H178.996ZM237.664 228.594H240.902V159.406H237.664ZM296.332 228.594H299.57V157.797H296.332ZM355.004 228.594H358.238V172.355H355.004ZM413.672 228.594H416.91V184.637H413.672Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M121.945 140.48V140.48' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M119.953 140.481H123.938' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M121.945 140.48V140.48' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M119.953 140.481H123.938' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M180.613 159.473V159.473' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M178.621 159.473H182.606' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M180.613 159.473V159.473' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M178.621 159.473H182.606' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M239.285 159.406V159.406' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M237.289 159.407H241.274' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M239.285 159.406V159.406' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M237.289 159.407H241.274' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M297.953 157.797V157.797' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M295.961 157.797H299.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M297.953 157.797V157.797' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M295.961 157.797H299.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M356.621 172.355V172.355' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M354.629 172.355H358.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M356.621 172.355V172.355' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M354.629 172.355H358.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M415.289 184.637V184.637' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M413.297 184.637H417.281' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M415.289 184.637V184.637' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M413.297 184.637H417.281' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M125.559 228.594H128.793V163.098H125.559ZM184.227 228.594H187.465V151.824H184.227ZM242.895 228.594H246.133V161.082H242.895ZM301.563 228.594H304.801V167.457H301.563ZM360.234 228.594H363.469V168.195H360.234ZM418.902 228.594H422.141V199.332H418.902Z' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M125.559 228.594H128.793V163.098H125.559ZM184.227 228.594H187.465V151.824H184.227ZM242.895 228.594H246.133V161.082H242.895ZM301.563 228.594H304.801V167.457H301.563ZM360.234 228.594H363.469V168.195H360.234ZM418.902 228.594H422.141V199.332H418.902Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M127.176 163.098V163.098' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M125.183 163.097H129.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M127.176 163.098V163.098' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M125.183 163.097H129.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M185.844 151.824V151.824' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M183.851 151.825H187.836' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M185.844 151.824V151.824' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M183.851 151.825H187.836' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M244.516 161.082V161.082' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M242.519 161.082H246.504' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M244.516 161.082V161.082' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M242.519 161.082H246.504' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M303.184 167.457V167.457' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M301.191 167.457H305.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M303.184 167.457V167.457' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M301.191 167.457H305.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M361.852 168.195V168.195' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M359.859 168.196H363.843' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M361.852 168.195V168.195' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M359.859 168.196H363.843' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M420.52 199.332V199.332' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M418.527 199.332H422.511' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M420.52 199.332V199.332' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M418.527 199.332H422.511' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M130.789 228.594H134.024V157.996H130.789ZM189.457 228.594H192.695V153.969H189.457ZM248.125 228.594H251.363V154.305H248.125ZM306.793 228.594H310.031V122.633H306.793ZM365.465 228.594H368.699V165.98H365.465ZM424.133 228.594H427.371V150.211H424.133Z' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M130.789 228.594H134.024V157.996H130.789ZM189.457 228.594H192.695V153.969H189.457ZM248.125 228.594H251.363V154.305H248.125ZM306.793 228.594H310.031V122.633H306.793ZM365.465 228.594H368.699V165.98H365.465ZM424.133 228.594H427.371V150.211H424.133Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M132.406 157.996V157.996' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M130.414 157.996H134.399' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M132.406 157.996V157.996' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M130.414 157.996H134.399' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M191.074 153.969V153.969' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M189.082 153.969H193.067' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M191.074 153.969V153.969' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M189.082 153.969H193.067' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M249.746 154.305V154.305' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M247.75 154.305H251.735' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M249.746 154.305V154.305' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M247.75 154.305H251.735' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M308.414 122.633V122.633' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M306.422 122.633H310.407' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M308.414 122.633V122.633' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M306.422 122.633H310.407' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M367.082 165.98V165.98' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M365.09 165.98H369.074' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M367.082 165.98V165.98' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M365.09 165.98H369.074' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M425.75 150.211V150.211' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M423.758 150.211H427.742' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M425.75 150.211V150.211' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M423.758 150.211H427.742' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(0 -1 1 0 -115.838 269.769)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-48' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -57.169 269.769)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-48' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 1.5 269.769)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-48' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 60.169 269.769)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-48' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 118.838 269.769)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-48' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 177.507 269.769)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-48' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -110.608 183.001)'>
+<use x='114.487' xlink:href='#g2-50' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-50' y='208.035'/>
+<use x='121.25' xlink:href='#g2-57' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -51.938 280.841)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-56' y='208.035'/>
+<use x='121.25' xlink:href='#g2-52' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 6.731 254.133)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-50' y='208.035'/>
+<use x='121.25' xlink:href='#g2-51' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 65.4 263.058)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-49' y='208.035'/>
+<use x='121.25' xlink:href='#g2-48' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 124.069 301.711)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-53' y='208.035'/>
+<use x='121.25' xlink:href='#g2-50' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 182.738 299.43)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-53' y='208.035'/>
+<use x='121.25' xlink:href='#g2-54' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -105.377 246.416)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-51' y='208.035'/>
+<use x='121.25' xlink:href='#g2-53' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -46.708 271.245)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-57' y='208.035'/>
+<use x='121.25' xlink:href='#g2-56' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 11.961 266.078)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-53' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 70.63 249.302)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-51' y='208.035'/>
+<use x='121.25' xlink:href='#g2-49' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 129.299 300.101)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-53' y='208.035'/>
+<use x='121.25' xlink:href='#g2-53' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 187.968 288.156)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-55' y='208.035'/>
+<use x='121.25' xlink:href='#g2-51' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -100.147 249.167)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-51' y='208.035'/>
+<use x='121.25' xlink:href='#g2-49' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -41.478 260.978)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-49' y='208.035'/>
+<use x='121.25' xlink:href='#g2-51' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 17.191 223.466)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-54' y='208.035'/>
+<use x='121.25' xlink:href='#g2-57' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 75.86 276.077)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-57' y='208.035'/>
+<use x='121.25' xlink:href='#g2-49' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 134.529 298.021)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-53' y='208.035'/>
+<use x='121.25' xlink:href='#g2-56' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 193.198 302.651)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-53' y='208.035'/>
+<use x='121.25' xlink:href='#g2-49' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -94.916 271.312)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-57' y='208.035'/>
+<use x='121.25' xlink:href='#g2-56' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -36.247 265.474)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-54' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 22.422 250.375)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-50' y='208.035'/>
+<use x='121.25' xlink:href='#g2-57' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 81.091 258.696)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-49' y='208.035'/>
+<use x='121.25' xlink:href='#g2-54' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 139.76 259.233)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-49' y='208.035'/>
+<use x='121.25' xlink:href='#g2-54' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 198.429 211.387)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-56' y='208.035'/>
+<use x='121.25' xlink:href='#g2-55' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -89.686 242.188)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-52' y='208.035'/>
+<use x='121.25' xlink:href='#g2-49' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -31.017 256.415)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-50' y='208.035'/>
+<use x='121.25' xlink:href='#g2-48' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 27.652 253.194)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-50' y='208.035'/>
+<use x='121.25' xlink:href='#g2-53' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 86.321 268.762)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-50' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 144.99 277.083)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-56' y='208.035'/>
+<use x='121.25' xlink:href='#g2-57' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 203.659 289.767)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-55' y='208.035'/>
+<use x='121.25' xlink:href='#g2-48' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -84.455 248.765)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-51' y='208.035'/>
+<use x='121.25' xlink:href='#g2-49' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -25.786 267.756)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-51' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 32.883 267.689)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-51' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 91.552 266.078)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-53' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 150.221 280.64)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-56' y='208.035'/>
+<use x='121.25' xlink:href='#g2-52' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 208.89 292.921)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-54' y='208.035'/>
+<use x='121.25' xlink:href='#g2-54' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -79.225 271.379)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-57' y='208.035'/>
+<use x='121.25' xlink:href='#g2-56' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -20.556 260.106)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-49' y='208.035'/>
+<use x='121.25' xlink:href='#g2-52' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 38.113 269.366)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-49' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 96.782 275.741)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-57' y='208.035'/>
+<use x='121.25' xlink:href='#g2-49' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 155.451 276.48)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-57' y='208.035'/>
+<use x='121.25' xlink:href='#g2-48' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 214.12 307.617)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-52' y='208.035'/>
+<use x='121.25' xlink:href='#g2-52' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -73.994 266.279)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-48' y='208.035'/>
+<use x='121.25' xlink:href='#g2-53' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -15.325 262.253)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-49' y='208.035'/>
+<use x='121.25' xlink:href='#g2-49' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 43.344 262.589)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-49' y='208.035'/>
+<use x='121.25' xlink:href='#g2-49' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 102.013 230.914)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-53' y='208.035'/>
+<use x='121.25' xlink:href='#g2-56' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 160.682 274.265)'>
+<use x='114.487' xlink:href='#g2-48' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-57' y='208.035'/>
+<use x='121.25' xlink:href='#g2-51' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 219.351 258.495)'>
+<use x='114.487' xlink:href='#g2-49' y='208.035'/>
+<use x='117.133' xlink:href='#g2-46' y='208.035'/>
+<use x='118.603' xlink:href='#g2-49' y='208.035'/>
+<use x='121.25' xlink:href='#g2-55' y='208.035'/>
+</g>
+<g transform='matrix(0 -1 1 0 -148.675 311.091)'>
+<use x='114.487' xlink:href='#g1-82' y='208.035'/>
+<use x='120.457' xlink:href='#g1-101' y='208.035'/>
+<use x='124.553' xlink:href='#g1-108' y='208.035'/>
+<use x='126.753' xlink:href='#g1-97' y='208.035'/>
+<use x='131.181' xlink:href='#g1-116' y='208.035'/>
+<use x='134.509' xlink:href='#g1-105' y='208.035'/>
+<use x='136.709' xlink:href='#g1-118' y='208.035'/>
+<use x='140.957' xlink:href='#g1-101' y='208.035'/>
+<use x='148.124' xlink:href='#g1-114' y='208.035'/>
+<use x='151.272' xlink:href='#g1-115' y='208.035'/>
+<use x='154.805' xlink:href='#g1-115' y='208.035'/>
+<use x='161.409' xlink:href='#g3-40' y='208.035'/>
+<use x='164.702' xlink:href='#g3-108' y='208.035'/>
+<use x='166.722' xlink:href='#g3-111' y='208.035'/>
+<use x='170.721' xlink:href='#g3-119' y='208.035'/>
+<use x='176.27' xlink:href='#g3-101' y='208.035'/>
+<use x='180.034' xlink:href='#g3-114' y='208.035'/>
+<use x='185.749' xlink:href='#g3-105' y='208.035'/>
+<use x='187.769' xlink:href='#g3-115' y='208.035'/>
+<use x='193.838' xlink:href='#g3-98' y='208.035'/>
+<use x='198.446' xlink:href='#g3-101' y='208.035'/>
+<use x='202.209' xlink:href='#g3-116' y='208.035'/>
+<use x='205.267' xlink:href='#g3-116' y='208.035'/>
+<use x='208.325' xlink:href='#g3-101' y='208.035'/>
+<use x='212.089' xlink:href='#g3-114' y='208.035'/>
+<use x='214.981' xlink:href='#g3-41' y='208.035'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='255.738pt' version='1.1' viewBox='106.736 54.996 381.624 255.738' width='381.624pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip8'>
+<path d='M135.949 249.281H487.961V81.519H135.949Z'/>
+</clipPath>
+<use id='g3-40' transform='scale(1.143)' xlink:href='#g0-40'/>
+<use id='g3-41' transform='scale(1.143)' xlink:href='#g0-41'/>
+<use id='g3-45' transform='scale(1.143)' xlink:href='#g0-45'/>
+<use id='g3-49' transform='scale(1.143)' xlink:href='#g0-49'/>
+<use id='g3-54' transform='scale(1.143)' xlink:href='#g0-54'/>
+<use id='g3-56' transform='scale(1.143)' xlink:href='#g0-56'/>
+<use id='g3-58' transform='scale(1.143)' xlink:href='#g0-58'/>
+<use id='g3-78' transform='scale(1.143)' xlink:href='#g0-78'/>
+<use id='g3-97' transform='scale(1.143)' xlink:href='#g0-97'/>
+<use id='g3-98' transform='scale(1.143)' xlink:href='#g0-98'/>
+<use id='g3-99' transform='scale(1.143)' xlink:href='#g0-99'/>
+<use id='g3-100' transform='scale(1.143)' xlink:href='#g0-100'/>
+<use id='g3-101' transform='scale(1.143)' xlink:href='#g0-101'/>
+<use id='g3-103' transform='scale(1.143)' xlink:href='#g0-103'/>
+<use id='g3-104' transform='scale(1.143)' xlink:href='#g0-104'/>
+<use id='g3-105' transform='scale(1.143)' xlink:href='#g0-105'/>
+<use id='g3-106' transform='scale(1.143)' xlink:href='#g0-106'/>
+<use id='g3-108' transform='scale(1.143)' xlink:href='#g0-108'/>
+<use id='g3-109' transform='scale(1.143)' xlink:href='#g0-109'/>
+<use id='g3-110' transform='scale(1.143)' xlink:href='#g0-110'/>
+<use id='g3-111' transform='scale(1.143)' xlink:href='#g0-111'/>
+<use id='g3-112' transform='scale(1.143)' xlink:href='#g0-112'/>
+<use id='g3-114' transform='scale(1.143)' xlink:href='#g0-114'/>
+<use id='g3-115' transform='scale(1.143)' xlink:href='#g0-115'/>
+<use id='g3-116' transform='scale(1.143)' xlink:href='#g0-116'/>
+<use id='g3-119' transform='scale(1.143)' xlink:href='#g0-119'/>
+<use id='g3-120' transform='scale(1.143)' xlink:href='#g0-120'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g1-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g1-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g1-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g1-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g1-108'/>
+<path d='M1.462 -1.91C1.462 -2.851 2.197 -3.425 3.013 -3.434V-4.08C2.367 -4.071 1.775 -3.748 1.408 -3.219V-4.035H0.744V0H1.462V-1.91Z' id='g1-114'/>
+<path d='M3.165 -3.847C2.609 -4.098 2.197 -4.133 1.829 -4.133C1.623 -4.133 0.305 -4.133 0.305 -2.95C0.305 -2.52 0.565 -2.251 0.664 -2.152C1.004 -1.856 1.237 -1.811 1.847 -1.695C2.134 -1.641 2.645 -1.542 2.645 -1.085C2.645 -0.502 1.919 -0.502 1.802 -0.502C1.273 -0.502 0.762 -0.681 0.377 -0.95L0.26 -0.296C0.798 -0.009 1.345 0.099 1.802 0.099C2.385 0.099 3.318 -0.09 3.318 -1.157C3.318 -1.47 3.192 -1.784 2.878 -2.053C2.573 -2.313 2.304 -2.367 1.748 -2.475C1.426 -2.537 0.977 -2.618 0.977 -3.04C0.977 -3.569 1.614 -3.569 1.748 -3.569C2.403 -3.569 2.789 -3.362 3.049 -3.219L3.165 -3.847Z' id='g1-115'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g1-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g1-118'/>
+<use id='g2-44' transform='scale(0.714)' xlink:href='#g0-44'/>
+<use id='g2-45' transform='scale(0.714)' xlink:href='#g0-45'/>
+<use id='g2-46' transform='scale(0.714)' xlink:href='#g0-46'/>
+<use id='g2-48' transform='scale(0.714)' xlink:href='#g0-48'/>
+<use id='g2-49' transform='scale(0.714)' xlink:href='#g0-49'/>
+<use id='g2-50' transform='scale(0.714)' xlink:href='#g0-50'/>
+<use id='g2-51' transform='scale(0.714)' xlink:href='#g0-51'/>
+<use id='g2-52' transform='scale(0.714)' xlink:href='#g0-52'/>
+<use id='g2-53' transform='scale(0.714)' xlink:href='#g0-53'/>
+<use id='g2-54' transform='scale(0.714)' xlink:href='#g0-54'/>
+<use id='g2-55' transform='scale(0.714)' xlink:href='#g0-55'/>
+<use id='g2-56' transform='scale(0.714)' xlink:href='#g0-56'/>
+<use id='g2-57' transform='scale(0.714)' xlink:href='#g0-57'/>
+<use id='g2-64' transform='scale(0.714)' xlink:href='#g0-64'/>
+<use id='g2-67' transform='scale(0.714)' xlink:href='#g0-67'/>
+<use id='g2-71' transform='scale(0.714)' xlink:href='#g0-71'/>
+<use id='g2-73' transform='scale(0.714)' xlink:href='#g0-73'/>
+<use id='g2-85' transform='scale(0.714)' xlink:href='#g0-85'/>
+<use id='g2-88' transform='scale(0.714)' xlink:href='#g0-88'/>
+<use id='g2-97' transform='scale(0.714)' xlink:href='#g0-97'/>
+<use id='g2-98' transform='scale(0.714)' xlink:href='#g0-98'/>
+<use id='g2-99' transform='scale(0.714)' xlink:href='#g0-99'/>
+<use id='g2-100' transform='scale(0.714)' xlink:href='#g0-100'/>
+<use id='g2-101' transform='scale(0.714)' xlink:href='#g0-101'/>
+<use id='g2-103' transform='scale(0.714)' xlink:href='#g0-103'/>
+<use id='g2-104' transform='scale(0.714)' xlink:href='#g0-104'/>
+<use id='g2-108' transform='scale(0.714)' xlink:href='#g0-108'/>
+<use id='g2-109' transform='scale(0.714)' xlink:href='#g0-109'/>
+<use id='g2-110' transform='scale(0.714)' xlink:href='#g0-110'/>
+<use id='g2-111' transform='scale(0.714)' xlink:href='#g0-111'/>
+<use id='g2-112' transform='scale(0.714)' xlink:href='#g0-112'/>
+<use id='g2-114' transform='scale(0.714)' xlink:href='#g0-114'/>
+<use id='g2-115' transform='scale(0.714)' xlink:href='#g0-115'/>
+<use id='g2-116' transform='scale(0.714)' xlink:href='#g0-116'/>
+<use id='g2-117' transform='scale(0.714)' xlink:href='#g0-117'/>
+<use id='g2-120' transform='scale(0.714)' xlink:href='#g0-120'/>
+<use id='g2-122' transform='scale(0.714)' xlink:href='#g0-122'/>
+<path d='M1.445 -1.245C1.445 -1.41 1.305 -1.549 1.141 -1.549S0.837 -1.41 0.837 -1.245S0.976 -0.941 1.141 -0.941S1.445 -1.081 1.445 -1.245Z' id='g4-1'/>
+<path d='M2.127 -5.23C2.008 -5.23 1.995 -5.23 1.911 -5.154C1.032 -4.387 0.586 -3.145 0.586 -1.743C0.586 -0.425 0.983 0.844 1.904 1.653C1.995 1.743 2.008 1.743 2.127 1.743H2.462C2.441 1.73 1.764 1.151 1.444 0.063C1.276 -0.481 1.193 -1.053 1.193 -1.743C1.193 -4.156 2.322 -5.112 2.462 -5.23H2.127Z' id='g0-40'/>
+<path d='M0.746 1.743C0.865 1.743 0.879 1.743 0.962 1.667C1.841 0.9 2.287 -0.342 2.287 -1.743C2.287 -3.062 1.89 -4.331 0.969 -5.14C0.879 -5.23 0.865 -5.23 0.746 -5.23H0.411C0.432 -5.216 1.109 -4.638 1.43 -3.55C1.597 -3.006 1.681 -2.434 1.681 -1.743C1.681 0.669 0.551 1.625 0.411 1.743H0.746Z' id='g0-41'/>
+<path d='M1.339 -0.007V-0.628H0.711V0H0.907L0.704 0.893H1.018L1.339 -0.007Z' id='g0-44'/>
+<path d='M2.05 -1.332V-1.771H0.084V-1.332H2.05Z' id='g0-45'/>
+<path d='M1.339 -0.628H0.711V0H1.339V-0.628Z' id='g0-46'/>
+<path d='M3.403 -2.267C3.403 -2.601 3.403 -3.417 3.075 -3.989C2.72 -4.617 2.183 -4.721 1.848 -4.721C1.534 -4.721 0.99 -4.624 0.642 -4.024C0.307 -3.466 0.293 -2.706 0.293 -2.267C0.293 -1.75 0.321 -1.116 0.614 -0.586C0.921 -0.021 1.437 0.146 1.848 0.146C2.545 0.146 2.929 -0.258 3.138 -0.697C3.382 -1.193 3.403 -1.834 3.403 -2.267ZM1.848 -0.314C1.555 -0.314 1.22 -0.481 1.046 -0.983C0.907 -1.409 0.9 -1.848 0.9 -2.357C0.9 -2.999 0.9 -4.261 1.848 -4.261S2.797 -2.999 2.797 -2.357C2.797 -1.897 2.797 -1.374 2.629 -0.928C2.434 -0.425 2.078 -0.314 1.848 -0.314Z' id='g0-48'/>
+<path d='M2.239 -4.721H2.085C1.632 -4.303 1.06 -4.275 0.642 -4.261V-3.822C0.914 -3.829 1.262 -3.843 1.611 -3.982V-0.439H0.683V0H3.166V-0.439H2.239V-4.721Z' id='g0-49'/>
+<path d='M1.974 -0.537C1.89 -0.537 1.806 -0.53 1.723 -0.53H0.928L2.008 -1.485C2.134 -1.597 2.476 -1.855 2.608 -1.967C2.915 -2.246 3.327 -2.608 3.327 -3.215C3.327 -4.003 2.741 -4.721 1.743 -4.721C1.004 -4.721 0.544 -4.324 0.307 -3.612L0.635 -3.201C0.795 -3.787 1.039 -4.24 1.646 -4.24C2.232 -4.24 2.678 -3.829 2.678 -3.201C2.678 -2.622 2.336 -2.294 1.918 -1.897C1.778 -1.757 1.402 -1.444 1.255 -1.304C1.053 -1.123 0.572 -0.656 0.37 -0.481V0H3.327V-0.537H1.974Z' id='g0-50'/>
+<path d='M0.697 -3.578C0.983 -4.135 1.485 -4.289 1.82 -4.289C2.232 -4.289 2.538 -4.052 2.538 -3.654C2.538 -3.285 2.287 -2.831 1.757 -2.741C1.723 -2.734 1.695 -2.734 1.234 -2.699V-2.239H1.778C2.441 -2.239 2.685 -1.716 2.685 -1.276C2.685 -0.732 2.35 -0.314 1.806 -0.314C1.311 -0.314 0.746 -0.551 0.398 -0.997L0.307 -0.544C0.711 -0.091 1.276 0.146 1.82 0.146C2.734 0.146 3.389 -0.537 3.389 -1.269C3.389 -1.841 2.929 -2.301 2.378 -2.462C2.908 -2.734 3.18 -3.201 3.18 -3.654C3.18 -4.247 2.573 -4.721 1.827 -4.721C1.213 -4.721 0.704 -4.4 0.411 -3.982L0.697 -3.578Z' id='g0-51'/>
+<path d='M2.762 -1.165H3.487V-1.625H2.762V-4.575H2.071L0.209 -1.625V-1.165H2.162V0H2.762V-1.165ZM0.802 -1.625C1.011 -1.953 2.211 -3.815 2.211 -4.233V-1.625H0.802Z' id='g0-52'/>
+<path d='M1.144 -4.094H3.075V-4.575H0.586V-1.967H1.095C1.262 -2.343 1.59 -2.511 1.904 -2.511C2.19 -2.511 2.622 -2.315 2.622 -1.43C2.622 -0.516 2.043 -0.314 1.688 -0.314C1.227 -0.314 0.781 -0.558 0.544 -0.955L0.279 -0.537C0.621 -0.112 1.137 0.146 1.688 0.146C2.608 0.146 3.327 -0.565 3.327 -1.416C3.327 -2.28 2.685 -2.971 1.918 -2.971C1.618 -2.971 1.353 -2.866 1.144 -2.692V-4.094Z' id='g0-53'/>
+<path d='M3.062 -4.582C2.685 -4.721 2.42 -4.721 2.287 -4.721C1.227 -4.721 0.307 -3.724 0.307 -2.253C0.307 -0.363 1.158 0.146 1.862 0.146C2.427 0.146 2.72 -0.119 2.936 -0.342C3.382 -0.816 3.389 -1.311 3.389 -1.555C3.389 -2.469 2.894 -3.229 2.218 -3.229C1.534 -3.229 1.165 -2.873 0.962 -2.671C1.053 -3.626 1.541 -4.289 2.294 -4.289C2.434 -4.289 2.713 -4.275 3.062 -4.142V-4.582ZM0.969 -1.534C0.969 -1.576 0.969 -1.681 0.976 -1.716C0.976 -2.19 1.276 -2.769 1.897 -2.769C2.748 -2.769 2.748 -1.792 2.748 -1.555C2.748 -1.29 2.748 -0.997 2.559 -0.704C2.392 -0.453 2.183 -0.314 1.862 -0.314C1.123 -0.314 1.004 -1.227 0.969 -1.534Z' id='g0-54'/>
+<path d='M1.723 -4.038C1.806 -4.038 1.89 -4.045 1.974 -4.045H2.852C1.792 -3.006 1.116 -1.548 1.116 0.07H1.771C1.771 -1.967 2.762 -3.431 3.389 -4.087V-4.575H0.307V-4.038H1.723Z' id='g0-55'/>
+<path d='M2.385 -2.469C2.845 -2.615 3.285 -2.985 3.285 -3.501C3.285 -4.135 2.678 -4.721 1.848 -4.721S0.411 -4.135 0.411 -3.501C0.411 -2.978 0.865 -2.608 1.311 -2.469C0.697 -2.28 0.307 -1.806 0.307 -1.269C0.307 -0.523 0.969 0.146 1.848 0.146S3.389 -0.523 3.389 -1.269C3.389 -1.806 2.992 -2.28 2.385 -2.469ZM1.848 -2.699C1.353 -2.699 0.948 -2.985 0.948 -3.494C0.948 -3.94 1.262 -4.289 1.848 -4.289C2.427 -4.289 2.748 -3.94 2.748 -3.494C2.748 -2.999 2.357 -2.699 1.848 -2.699ZM1.848 -0.314C1.367 -0.314 0.941 -0.621 0.941 -1.276C0.941 -1.904 1.346 -2.239 1.848 -2.239S2.755 -1.897 2.755 -1.276C2.755 -0.621 2.322 -0.314 1.848 -0.314Z' id='g0-56'/>
+<path d='M0.537 -0.174C0.879 0.077 1.193 0.146 1.52 0.146C2.497 0.146 3.389 -0.837 3.389 -2.336C3.389 -4.24 2.545 -4.721 1.876 -4.721C1.255 -4.721 0.969 -4.428 0.767 -4.226C0.321 -3.773 0.307 -3.292 0.307 -3.02C0.307 -2.12 0.795 -1.346 1.478 -1.346C2.267 -1.346 2.699 -1.869 2.734 -1.911C2.636 -0.802 2.092 -0.314 1.52 -0.314C1.158 -0.314 0.934 -0.446 0.774 -0.579L0.537 -0.174ZM2.713 -3.027C2.72 -2.985 2.72 -2.915 2.72 -2.873C2.72 -2.357 2.406 -1.806 1.799 -1.806C1.534 -1.806 1.325 -1.883 1.144 -2.169C0.962 -2.441 0.948 -2.706 0.948 -3.02C0.948 -3.292 0.948 -3.605 1.165 -3.912C1.311 -4.122 1.52 -4.289 1.869 -4.289C2.545 -4.289 2.692 -3.473 2.713 -3.027Z' id='g0-57'/>
+<path d='M1.339 -3.096H0.711V-2.469H1.339V-3.096ZM0.711 -0.628V0H1.339V-0.628H0.711Z' id='g0-58'/>
+<path d='M4.142 -0.614C4.038 -0.614 4.024 -0.614 3.968 -0.586C3.626 -0.467 3.271 -0.391 2.901 -0.391C1.778 -0.391 0.976 -1.339 0.976 -2.42C0.976 -3.592 1.883 -4.449 2.859 -4.449C3.055 -4.449 3.515 -4.4 3.745 -3.843C3.55 -3.954 3.333 -4.003 3.152 -4.003C2.406 -4.003 1.778 -3.306 1.778 -2.42C1.778 -1.513 2.427 -0.837 3.145 -0.837C3.689 -0.837 4.519 -1.276 4.519 -2.518C4.519 -3.222 4.47 -4.91 2.866 -4.91C1.541 -4.91 0.411 -3.815 0.411 -2.42C0.411 -1.039 1.527 0.07 2.873 0.07C3.515 0.07 4.115 -0.195 4.519 -0.614H4.142ZM3.152 -1.297C2.72 -1.297 2.343 -1.778 2.343 -2.42C2.343 -3.082 2.734 -3.543 3.145 -3.543C3.578 -3.543 3.954 -3.062 3.954 -2.42C3.954 -1.757 3.564 -1.297 3.152 -1.297Z' id='g0-64'/>
+<path d='M4.317 -0.851C3.829 -0.551 3.605 -0.418 2.908 -0.418C1.827 -0.418 1.172 -1.43 1.172 -2.434C1.172 -3.466 1.89 -4.435 2.908 -4.435C3.368 -4.435 3.843 -4.289 4.163 -4.045L4.275 -4.679C3.787 -4.861 3.396 -4.917 2.887 -4.917C1.506 -4.917 0.474 -3.773 0.474 -2.427C0.474 -0.99 1.569 0.07 2.929 0.07C3.612 0.07 3.898 -0.07 4.359 -0.321L4.317 -0.851Z' id='g0-67'/>
+<path d='M4.442 -2.085H2.88V-1.625H3.829V-0.558C3.522 -0.481 3.222 -0.418 2.908 -0.418C1.834 -0.418 1.172 -1.43 1.172 -2.427C1.172 -3.382 1.82 -4.435 2.873 -4.435C3.515 -4.435 3.919 -4.24 4.268 -3.947L4.38 -4.582C3.898 -4.812 3.473 -4.924 2.943 -4.924C1.534 -4.924 0.474 -3.822 0.474 -2.427C0.474 -1.067 1.527 0.07 2.901 0.07C3.403 0.07 3.996 -0.042 4.442 -0.272V-2.085Z' id='g0-71'/>
+<path d='M1.381 -4.84H0.676V0H1.381V-4.84Z' id='g0-73'/>
+<path d='M1.646 -4.84H0.697V0H1.283V-4.289H1.29L3.578 0H4.526V-4.84H3.94V-0.551H3.933L1.646 -4.84Z' id='g0-78'/>
+<path d='M4.4 -4.84H3.794V-1.625C3.794 -0.69 3.166 -0.265 2.566 -0.265S1.381 -0.697 1.381 -1.618V-4.84H0.676V-1.632C0.676 -0.607 1.555 0.146 2.559 0.146C3.557 0.146 4.4 -0.614 4.4 -1.632V-4.84Z' id='g0-85'/>
+<path d='M2.755 -2.552L4.519 -4.84H3.759L2.413 -3.055L1.039 -4.84H0.209L2.071 -2.552L0.105 0H0.865L2.413 -2.099L3.996 0H4.826L2.755 -2.552Z' id='g0-88'/>
+<path d='M2.971 -2.008C2.971 -2.72 2.427 -3.201 1.736 -3.201C1.297 -3.201 0.962 -3.11 0.572 -2.901L0.614 -2.392C0.844 -2.545 1.186 -2.755 1.736 -2.755C2.043 -2.755 2.364 -2.525 2.364 -2.001V-1.723C1.332 -1.688 0.314 -1.471 0.314 -0.823C0.314 -0.474 0.551 0.07 1.165 0.07C1.465 0.07 2.015 0.007 2.385 -0.265V0H2.971V-2.008ZM2.364 -0.99C2.364 -0.851 2.364 -0.669 2.12 -0.523C1.897 -0.398 1.625 -0.391 1.548 -0.391C1.165 -0.391 0.872 -0.565 0.872 -0.83C0.872 -1.276 2.05 -1.318 2.364 -1.332V-0.99Z' id='g0-97'/>
+<path d='M1.179 -4.84H0.593V0H1.2V-0.328C1.353 -0.195 1.688 0.07 2.197 0.07C2.957 0.07 3.571 -0.642 3.571 -1.555C3.571 -2.399 3.089 -3.166 2.392 -3.166C1.953 -3.166 1.527 -3.027 1.179 -2.769V-4.84ZM1.2 -2.197C1.2 -2.308 1.2 -2.392 1.444 -2.552C1.548 -2.615 1.736 -2.706 1.974 -2.706C2.441 -2.706 2.964 -2.392 2.964 -1.555C2.964 -0.704 2.385 -0.391 1.897 -0.391C1.639 -0.391 1.395 -0.509 1.2 -0.823V-2.197Z' id='g0-98'/>
+<path d='M3.034 -0.76C2.685 -0.537 2.308 -0.411 1.876 -0.411C1.234 -0.411 0.858 -0.928 0.858 -1.555C0.858 -2.092 1.137 -2.72 1.897 -2.72C2.371 -2.72 2.594 -2.622 2.95 -2.399L3.041 -2.901C2.622 -3.11 2.441 -3.201 1.897 -3.201C0.851 -3.201 0.251 -2.357 0.251 -1.548C0.251 -0.697 0.921 0.07 1.869 0.07C2.357 0.07 2.776 -0.077 3.075 -0.251L3.034 -0.76Z' id='g0-99'/>
+<path d='M3.229 -4.84H2.643V-2.797C2.197 -3.124 1.743 -3.166 1.541 -3.166C0.809 -3.166 0.251 -2.434 0.251 -1.548S0.802 0.07 1.52 0.07C1.953 0.07 2.357 -0.126 2.622 -0.363V0H3.229V-4.84ZM2.622 -0.865C2.448 -0.579 2.183 -0.391 1.848 -0.391C1.36 -0.391 0.858 -0.732 0.858 -1.541C0.858 -2.413 1.451 -2.706 1.925 -2.706C2.204 -2.706 2.441 -2.587 2.622 -2.35V-0.865Z' id='g0-100'/>
+<path d='M2.999 -0.76C2.608 -0.481 2.169 -0.391 1.869 -0.391C1.262 -0.391 0.802 -0.886 0.781 -1.527H3.068C3.068 -1.848 3.034 -2.315 2.762 -2.713C2.511 -3.068 2.092 -3.201 1.75 -3.201C0.9 -3.201 0.244 -2.455 0.244 -1.569C0.244 -0.676 0.941 0.07 1.862 0.07C2.267 0.07 2.685 -0.049 3.041 -0.265L2.999 -0.76ZM0.83 -1.946C0.99 -2.504 1.402 -2.741 1.75 -2.741C2.057 -2.741 2.511 -2.594 2.643 -1.946H0.83Z' id='g0-101'/>
+<path d='M3.508 -3.166C3.354 -3.166 2.887 -3.159 2.357 -2.957L2.343 -2.95C2.092 -3.117 1.848 -3.166 1.646 -3.166C0.962 -3.166 0.453 -2.629 0.453 -2.029C0.453 -1.785 0.537 -1.534 0.697 -1.339C0.6 -1.22 0.495 -1.025 0.495 -0.76C0.495 -0.488 0.607 -0.314 0.669 -0.23C0.286 -0.007 0.209 0.314 0.209 0.481C0.209 1.011 0.941 1.43 1.848 1.43C2.762 1.43 3.487 1.011 3.487 0.481C3.487 -0.502 2.267 -0.502 1.967 -0.502H1.318C1.206 -0.502 0.907 -0.502 0.907 -0.865C0.907 -1.004 0.955 -1.074 0.962 -1.088C1.206 -0.934 1.451 -0.886 1.639 -0.886C2.322 -0.886 2.831 -1.423 2.831 -2.022C2.831 -2.246 2.769 -2.448 2.643 -2.636C2.615 -2.678 2.615 -2.685 2.615 -2.692C2.615 -2.72 3.034 -2.72 3.068 -2.72C3.075 -2.72 3.34 -2.72 3.592 -2.692L3.508 -3.166ZM1.646 -1.318C1.269 -1.318 0.99 -1.555 0.99 -2.022C0.99 -2.566 1.339 -2.734 1.639 -2.734C2.015 -2.734 2.294 -2.497 2.294 -2.029C2.294 -1.485 1.946 -1.318 1.646 -1.318ZM1.974 0.042C2.134 0.042 2.957 0.042 2.957 0.488C2.957 0.788 2.434 0.997 1.848 0.997S0.739 0.788 0.739 0.488C0.739 0.453 0.739 0.042 1.304 0.042H1.974Z' id='g0-103'/>
+<path d='M3.243 -2.064C3.243 -2.608 3.082 -3.166 2.225 -3.166C1.625 -3.166 1.304 -2.817 1.165 -2.671V-4.84H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-2.064Z' id='g0-104'/>
+<path d='M1.227 -4.784H0.523V-4.08H1.227V-4.784ZM1.172 -3.096H0.586V0H1.172V-3.096Z' id='g0-105'/>
+<path d='M1.381 -4.784H0.676V-4.08H1.381V-4.784ZM-0.453 1.186C-0.133 1.36 0.181 1.423 0.446 1.423C0.928 1.423 1.381 1.053 1.381 0.411V-3.096H0.795V0.46C0.795 0.586 0.795 0.697 0.649 0.816C0.488 0.934 0.293 0.934 0.23 0.934C-0.063 0.934 -0.244 0.802 -0.328 0.725L-0.453 1.186Z' id='g0-106'/>
+<path d='M1.172 -4.84H0.586V0H1.172V-4.84Z' id='g0-108'/>
+<path d='M5.3 -2.064C5.3 -2.608 5.14 -3.166 4.282 -3.166C3.696 -3.166 3.333 -2.824 3.166 -2.601C3.096 -2.79 2.922 -3.166 2.225 -3.166C1.827 -3.166 1.444 -3.006 1.137 -2.636V-3.145H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-1.695C3.243 -2.155 3.438 -2.706 3.975 -2.706C4.693 -2.706 4.693 -2.218 4.693 -2.015V0H5.3V-2.064Z' id='g0-109'/>
+<path d='M3.243 -2.064C3.243 -2.608 3.082 -3.166 2.225 -3.166C1.827 -3.166 1.444 -3.006 1.137 -2.636V-3.145H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-2.064Z' id='g0-110'/>
+<path d='M3.487 -1.527C3.487 -2.448 2.755 -3.201 1.848 -3.201S0.209 -2.441 0.209 -1.527C0.209 -0.642 0.948 0.07 1.848 0.07C2.755 0.07 3.487 -0.642 3.487 -1.527ZM1.848 -0.411C1.297 -0.411 0.816 -0.816 0.816 -1.604S1.332 -2.741 1.848 -2.741C2.371 -2.741 2.88 -2.378 2.88 -1.604C2.88 -0.809 2.385 -0.411 1.848 -0.411Z' id='g0-111'/>
+<path d='M1.2 -0.328C1.569 0.007 1.967 0.07 2.204 0.07C2.943 0.07 3.571 -0.635 3.571 -1.555C3.571 -2.392 3.11 -3.166 2.42 -3.166C2.106 -3.166 1.583 -3.075 1.179 -2.762V-3.096H0.593V1.353H1.2V-0.328ZM1.2 -2.315C1.36 -2.511 1.632 -2.685 1.967 -2.685C2.525 -2.685 2.964 -2.169 2.964 -1.555C2.964 -0.865 2.441 -0.391 1.897 -0.391C1.792 -0.391 1.618 -0.404 1.437 -0.551C1.227 -0.711 1.2 -0.816 1.2 -0.948V-2.315Z' id='g0-112'/>
+<path d='M1.179 -1.485C1.179 -2.239 1.806 -2.643 2.42 -2.65V-3.166C1.834 -3.159 1.409 -2.873 1.13 -2.504V-3.145H0.593V0H1.179V-1.485Z' id='g0-114'/>
+<path d='M2.545 -2.985C2.071 -3.18 1.723 -3.201 1.471 -3.201C1.297 -3.201 0.244 -3.201 0.244 -2.273C0.244 -1.946 0.425 -1.764 0.516 -1.681C0.76 -1.437 1.053 -1.381 1.423 -1.311C1.75 -1.248 2.127 -1.179 2.127 -0.844C2.127 -0.404 1.548 -0.404 1.451 -0.404C1.004 -0.404 0.586 -0.565 0.307 -0.76L0.209 -0.237C0.446 -0.119 0.872 0.07 1.451 0.07C1.764 0.07 2.071 0.021 2.329 -0.167C2.587 -0.363 2.671 -0.669 2.671 -0.907C2.671 -1.032 2.657 -1.304 2.364 -1.569C2.106 -1.799 1.855 -1.848 1.52 -1.911C1.109 -1.988 0.788 -2.05 0.788 -2.357C0.788 -2.755 1.297 -2.755 1.402 -2.755C1.799 -2.755 2.106 -2.671 2.455 -2.49L2.545 -2.985Z' id='g0-115'/>
+<path d='M1.311 -2.657H2.343V-3.096H1.311V-3.982H0.774V-3.096H0.139V-2.657H0.753V-0.893C0.753 -0.425 0.872 0.07 1.374 0.07S2.26 -0.091 2.469 -0.188L2.35 -0.635C2.12 -0.467 1.876 -0.411 1.681 -0.411C1.388 -0.411 1.311 -0.697 1.311 -1.018V-2.657Z' id='g0-116'/>
+<path d='M3.243 -3.096H2.636V-1.074C2.636 -0.516 2.162 -0.342 1.757 -0.342C1.241 -0.342 1.186 -0.481 1.186 -0.802V-3.096H0.579V-0.76C0.579 -0.139 0.851 0.07 1.339 0.07C1.625 0.07 2.239 0.014 2.657 -0.321V0H3.243V-3.096Z' id='g0-117'/>
+<path d='M4.951 -3.096H4.407C4.345 -2.901 3.954 -1.723 3.738 -0.997C3.682 -0.795 3.612 -0.572 3.592 -0.411H3.585C3.543 -0.697 3.299 -1.451 3.285 -1.499L2.769 -3.096H2.239C2.036 -2.497 1.513 -0.934 1.458 -0.425H1.451C1.395 -0.921 0.879 -2.462 0.767 -2.797C0.711 -2.964 0.711 -2.978 0.676 -3.096H0.105L1.123 0H1.709C1.716 -0.028 1.904 -0.579 2.148 -1.353C2.253 -1.695 2.462 -2.364 2.497 -2.671L2.504 -2.678C2.518 -2.532 2.559 -2.378 2.608 -2.204S2.706 -1.841 2.755 -1.681L3.292 0H3.933L4.951 -3.096Z' id='g0-119'/>
+<path d='M1.932 -1.597L3.285 -3.096H2.671L1.681 -1.953L0.669 -3.096H0.042L1.437 -1.597L0 0H0.621L1.681 -1.311L2.783 0H3.41L1.932 -1.597Z' id='g0-120'/>
+<path d='M2.957 -2.803V-3.096H0.307V-2.65H1.332C1.416 -2.65 1.499 -2.657 1.583 -2.657H2.127L0.209 -0.307V0H2.978V-0.467H1.897C1.813 -0.467 1.73 -0.46 1.646 -0.46H1.039L2.957 -2.803Z' id='g0-122'/>
+</defs>
+<g id='page8'>
+<path d='M194.617 258.141V249.281M253.285 258.141V249.281M311.953 258.141V249.281M370.625 258.141V249.281M429.293 258.141V249.281M194.617 72.66V81.519M253.285 72.66V81.519M311.953 72.66V81.519M370.625 72.66V81.519M429.293 72.66V81.519' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M165.281 253.535V249.281M223.953 253.535V249.281M282.621 253.535V249.281M341.289 253.535V249.281M399.957 253.535V249.281M458.629 253.535V249.281M165.281 77.266V81.519M223.953 77.266V81.519M282.621 77.266V81.519M341.289 77.266V81.519M399.957 77.266V81.519M458.629 77.266V81.519' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 249.281H140.199M135.949 215.73H140.199M135.949 182.176H140.199M135.949 148.625H140.199M135.949 115.07H140.199M135.949 81.519H140.199M487.961 249.281H483.711M487.961 215.73H483.711M487.961 182.176H483.711M487.961 148.625H483.711M487.961 115.07H483.711M487.961 81.519H483.711' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 249.281V81.519H487.961V249.281H135.949Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -21.265 74.992)'>
+<use x='168.285' xlink:href='#g3-97' y='188.688'/>
+<use x='172.353' xlink:href='#g3-108' y='188.688'/>
+<use x='174.373' xlink:href='#g3-108' y='188.688'/>
+<use x='176.393' xlink:href='#g3-111' y='188.688'/>
+<use x='180.863' xlink:href='#g3-99' y='188.688'/>
+<use x='184.627' xlink:href='#g3-45' y='188.688'/>
+<use x='187.449' xlink:href='#g3-116' y='188.688'/>
+<use x='190.507' xlink:href='#g3-101' y='188.688'/>
+<use x='194.271' xlink:href='#g3-115' y='188.688'/>
+<use x='197.517' xlink:href='#g3-116' y='188.688'/>
+<use x='200.575' xlink:href='#g3-49' y='188.688'/>
+</g>
+<g transform='matrix(1 0 0 1 36.532 74.992)'>
+<use x='168.285' xlink:href='#g3-97' y='188.688'/>
+<use x='172.353' xlink:href='#g3-108' y='188.688'/>
+<use x='174.373' xlink:href='#g3-108' y='188.688'/>
+<use x='176.393' xlink:href='#g3-111' y='188.688'/>
+<use x='180.863' xlink:href='#g3-99' y='188.688'/>
+<use x='184.627' xlink:href='#g3-45' y='188.688'/>
+<use x='187.449' xlink:href='#g3-116' y='188.688'/>
+<use x='190.507' xlink:href='#g3-101' y='188.688'/>
+<use x='194.271' xlink:href='#g3-115' y='188.688'/>
+<use x='197.517' xlink:href='#g3-116' y='188.688'/>
+<use x='200.575' xlink:href='#g3-78' y='188.688'/>
+</g>
+<g transform='matrix(1 0 0 1 94.98 74.992)'>
+<use x='168.285' xlink:href='#g3-115' y='188.688'/>
+<use x='171.531' xlink:href='#g3-104' y='188.688'/>
+<use x='175.904' xlink:href='#g3-54' y='188.688'/>
+<use x='180.138' xlink:href='#g3-98' y='188.688'/>
+<use x='184.746' xlink:href='#g3-101' y='188.688'/>
+<use x='188.509' xlink:href='#g3-110' y='188.688'/>
+<use x='192.882' xlink:href='#g3-99' y='188.688'/>
+<use x='196.646' xlink:href='#g3-104' y='188.688'/>
+<use x='201.018' xlink:href='#g3-78' y='188.688'/>
+</g>
+<g transform='matrix(1 0 0 1 153.649 74.992)'>
+<use x='168.285' xlink:href='#g3-115' y='188.688'/>
+<use x='171.531' xlink:href='#g3-104' y='188.688'/>
+<use x='175.904' xlink:href='#g3-56' y='188.688'/>
+<use x='180.138' xlink:href='#g3-98' y='188.688'/>
+<use x='184.746' xlink:href='#g3-101' y='188.688'/>
+<use x='188.509' xlink:href='#g3-110' y='188.688'/>
+<use x='192.882' xlink:href='#g3-99' y='188.688'/>
+<use x='196.646' xlink:href='#g3-104' y='188.688'/>
+<use x='201.018' xlink:href='#g3-78' y='188.688'/>
+</g>
+<g transform='matrix(1 0 0 1 207.225 74.992)'>
+<use x='168.285' xlink:href='#g3-120' y='188.688'/>
+<use x='172.187' xlink:href='#g3-109' y='188.688'/>
+<use x='178.912' xlink:href='#g3-97' y='188.688'/>
+<use x='182.98' xlink:href='#g3-108' y='188.688'/>
+<use x='185' xlink:href='#g3-108' y='188.688'/>
+<use x='187.02' xlink:href='#g3-111' y='188.688'/>
+<use x='191.49' xlink:href='#g3-99' y='188.688'/>
+<use x='195.254' xlink:href='#g3-45' y='188.688'/>
+<use x='198.076' xlink:href='#g3-116' y='188.688'/>
+<use x='201.134' xlink:href='#g3-101' y='188.688'/>
+<use x='204.898' xlink:href='#g3-115' y='188.688'/>
+<use x='208.144' xlink:href='#g3-116' y='188.688'/>
+<use x='211.202' xlink:href='#g3-78' y='188.688'/>
+</g>
+<g transform='matrix(1 0 0 1 263.494 74.992)'>
+<use x='168.285' xlink:href='#g3-99' y='188.688'/>
+<use x='172.049' xlink:href='#g3-97' y='188.688'/>
+<use x='176.117' xlink:href='#g3-99' y='188.688'/>
+<use x='179.88' xlink:href='#g3-104' y='188.688'/>
+<use x='184.253' xlink:href='#g3-101' y='188.688'/>
+<use x='188.017' xlink:href='#g3-45' y='188.688'/>
+<use x='190.839' xlink:href='#g3-115' y='188.688'/>
+<use x='194.086' xlink:href='#g3-99' y='188.688'/>
+<use x='197.849' xlink:href='#g3-114' y='188.688'/>
+<use x='200.741' xlink:href='#g3-97' y='188.688'/>
+<use x='204.81' xlink:href='#g3-116' y='188.688'/>
+<use x='207.868' xlink:href='#g3-99' y='188.688'/>
+<use x='211.631' xlink:href='#g3-104' y='188.688'/>
+<use x='216.004' xlink:href='#g3-78' y='188.688'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 62.23)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-120' y='188.688'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 28.677)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-53' y='188.688'/>
+<use x='175.048' xlink:href='#g2-120' y='188.688'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -4.876)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-120' y='188.688'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -38.43)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-53' y='188.688'/>
+<use x='175.048' xlink:href='#g2-120' y='188.688'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -71.983)'>
+<use x='168.285' xlink:href='#g2-50' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-120' y='188.688'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -105.536)'>
+<use x='168.285' xlink:href='#g2-50' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-53' y='188.688'/>
+<use x='175.048' xlink:href='#g2-120' y='188.688'/>
+</g>
+<path clip-path='url(#clip8)' d='M135.949 182.176H487.961' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M142.742 249.281H145.981V182.176H142.742ZM201.41 249.281H204.649V182.176H201.41ZM260.078 249.281H263.317V182.176H260.078ZM318.75 249.281H321.988V182.176H318.75ZM377.418 249.281H380.656V182.176H377.418ZM436.086 249.281H439.324V182.176H436.086Z' fill='#ffffff'/>
+<path clip-path='url(#clip8)' d='M142.742 249.281H145.981V182.176H142.742ZM201.41 249.281H204.649V182.176H201.41ZM260.078 249.281H263.317V182.176H260.078ZM318.75 249.281H321.988V182.176H318.75ZM377.418 249.281H380.656V182.176H377.418ZM436.086 249.281H439.324V182.176H436.086Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M144.359 182.176V182.176' fill='#ffffff'/>
+<path clip-path='url(#clip8)' d='M142.367 182.176H146.351' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M144.359 182.176V182.176' fill='#ffffff'/>
+<path clip-path='url(#clip8)' d='M142.367 182.176H146.351' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M203.031 182.176V182.176' fill='#ffffff'/>
+<path clip-path='url(#clip8)' d='M201.035 182.176H205.023' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M203.031 182.176V182.176' fill='#ffffff'/>
+<path clip-path='url(#clip8)' d='M201.035 182.176H205.023' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M261.699 182.176V182.176' fill='#ffffff'/>
+<path clip-path='url(#clip8)' d='M259.707 182.176H263.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M261.699 182.176V182.176' fill='#ffffff'/>
+<path clip-path='url(#clip8)' d='M259.707 182.176H263.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M320.367 182.176V182.176' fill='#ffffff'/>
+<path clip-path='url(#clip8)' d='M318.375 182.176H322.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M320.367 182.176V182.176' fill='#ffffff'/>
+<path clip-path='url(#clip8)' d='M318.375 182.176H322.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M379.035 182.176V182.176' fill='#ffffff'/>
+<path clip-path='url(#clip8)' d='M377.043 182.176H381.027' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M379.035 182.176V182.176' fill='#ffffff'/>
+<path clip-path='url(#clip8)' d='M377.043 182.176H381.027' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M437.707 182.176V182.176' fill='#ffffff'/>
+<path clip-path='url(#clip8)' d='M435.711 182.176H439.699' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M437.707 182.176V182.176' fill='#ffffff'/>
+<path clip-path='url(#clip8)' d='M435.711 182.176H439.699' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M147.973 249.281H151.211V164.73H147.973ZM206.641 249.281H209.879V161.977H206.641ZM265.309 249.281H268.547V183.25H265.309ZM323.981 249.281H327.219V209.891H323.981ZM382.649 249.281H385.887V224.184H382.649ZM441.317 249.281H444.555V141.242H441.317Z' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M147.973 249.281H151.211V164.73H147.973ZM206.641 249.281H209.879V161.977H206.641ZM265.309 249.281H268.547V183.25H265.309ZM323.981 249.281H327.219V209.891H323.981ZM382.649 249.281H385.887V224.184H382.649ZM441.317 249.281H444.555V141.242H441.317Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M149.59 164.73V164.73' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M147.598 164.73H151.582' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M149.59 164.73V164.73' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M147.598 164.73H151.582' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M208.262 161.977V161.977' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M206.266 161.976H210.254' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M208.262 161.977V161.977' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M206.266 161.976H210.254' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M266.93 183.25V183.25' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M264.938 183.25H268.922' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M266.93 183.25V183.25' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M264.938 183.25H268.922' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M325.598 209.891V209.891' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M323.606 209.891H327.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M325.598 209.891V209.891' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M323.606 209.891H327.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M384.266 224.184V224.184' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M382.274 224.183H386.258' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M384.266 224.184V224.184' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M382.274 224.183H386.258' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M442.938 141.242V141.242' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M440.942 141.242H444.93' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M442.938 141.242V141.242' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M440.942 141.242H444.93' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M153.203 249.281H156.442V185.129H153.203ZM211.871 249.281H215.11V177.883H211.871ZM270.539 249.281H273.777V181.437H270.539ZM329.211 249.281H332.449V192.848H329.211ZM387.879 249.281H391.117V183.719H387.879ZM446.547 249.281H449.785V145H446.547Z' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M153.203 249.281H156.442V185.129H153.203ZM211.871 249.281H215.11V177.883H211.871ZM270.539 249.281H273.777V181.437H270.539ZM329.211 249.281H332.449V192.848H329.211ZM387.879 249.281H391.117V183.719H387.879ZM446.547 249.281H449.785V145H446.547Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M154.82 185.129V185.129' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M152.828 185.129H156.812' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M154.82 185.129V185.129' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M152.828 185.129H156.812' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M213.492 177.883V177.883' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M211.496 177.883H215.484' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M213.492 177.883V177.883' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M211.496 177.883H215.484' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M272.16 181.437V181.437' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M270.168 181.438H274.152' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M272.16 181.437V181.437' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M270.168 181.438H274.152' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M330.828 192.848V192.848' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M328.836 192.848H332.82' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M330.828 192.848V192.848' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M328.836 192.848H332.82' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M389.496 183.719V183.719' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M387.504 183.718H391.488' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M389.496 183.719V183.719' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M387.504 183.718H391.488' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M448.168 145V145' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M446.172 145H450.16' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M448.168 145V145' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M446.172 145H450.16' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M158.434 249.281H161.672V180.969H158.434ZM217.102 249.281H220.34V172.043H217.102ZM275.77 249.281H279.008V172.312H275.77ZM334.442 249.281H337.68V194.996H334.442ZM393.109 249.281H396.348V195.531H393.109ZM451.777 249.281H455.016V180.098H451.777Z' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M158.434 249.281H161.672V180.969H158.434ZM217.102 249.281H220.34V172.043H217.102ZM275.77 249.281H279.008V172.312H275.77ZM334.442 249.281H337.68V194.996H334.442ZM393.109 249.281H396.348V195.531H393.109ZM451.777 249.281H455.016V180.098H451.777Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M160.051 180.969V180.969' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M158.058 180.969H162.042' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M160.051 180.969V180.969' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M158.058 180.969H162.042' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M218.723 172.043V172.043' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M216.726 172.043H220.714' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M218.723 172.043V172.043' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M216.726 172.043H220.714' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M277.391 172.312V172.312' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M275.398 172.312H279.382' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M277.391 172.312V172.312' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M275.398 172.312H279.382' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M336.059 194.996V194.996' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M334.066 194.996H338.05' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M336.059 194.996V194.996' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M334.066 194.996H338.05' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M394.727 195.531V195.531' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M392.734 195.531H396.718' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M394.727 195.531V195.531' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M392.734 195.531H396.718' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M453.399 180.098V180.098' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M451.402 180.098H455.39' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M453.399 180.098V180.098' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M451.402 180.098H455.39' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M163.664 249.281H166.903V180.363H163.664ZM222.332 249.281H225.57V144.465H222.332ZM281 249.281H284.238V171.707H281ZM339.672 249.281H342.91V191.641H339.672ZM398.34 249.281H401.578V81.519H398.34ZM457.008 249.281H460.246V190.027H457.008Z' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M163.664 249.281H166.903V180.363H163.664ZM222.332 249.281H225.57V144.465H222.332ZM281 249.281H284.238V171.707H281ZM339.672 249.281H342.91V191.641H339.672ZM398.34 249.281H401.578V81.519H398.34ZM457.008 249.281H460.246V190.027H457.008Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M165.281 180.363V180.363' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M163.289 180.363H167.274' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M165.281 180.363V180.363' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M163.289 180.363H167.274' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M223.953 144.465V144.465' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M221.957 144.464H225.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M223.953 144.465V144.465' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M221.957 144.464H225.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M282.621 171.707V171.707' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M280.629 171.707H284.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M282.621 171.707V171.707' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M280.629 171.707H284.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M341.289 191.641V191.641' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M339.297 191.641H343.281' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M341.289 191.641V191.641' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M339.297 191.641H343.281' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M399.957 81.519V81.519' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M397.965 81.52H401.949' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M399.957 81.519V81.519' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M397.965 81.52H401.949' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M458.629 190.027V190.027' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M456.633 190.027H460.621' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M458.629 190.027V190.027' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M456.633 190.027H460.621' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M168.895 249.281H172.133V157.348H168.895ZM227.563 249.281H230.801V81.519H227.563ZM286.231 249.281H289.469V141.711H286.231ZM344.902 249.281H348.141V124.601H344.902ZM403.57 249.281H406.809V132.25H403.57ZM462.238 249.281H465.477V189.894H462.238Z' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M168.895 249.281H172.133V157.348H168.895ZM227.563 249.281H230.801V81.519H227.563ZM286.231 249.281H289.469V141.711H286.231ZM344.902 249.281H348.141V124.601H344.902ZM403.57 249.281H406.809V132.25H403.57ZM462.238 249.281H465.477V189.894H462.238Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M170.512 157.348V157.348' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M168.519 157.348H172.504' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M170.512 157.348V157.348' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M168.519 157.348H172.504' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M229.184 81.519V81.519' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M227.187 81.52H231.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M229.184 81.519V81.519' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M227.187 81.52H231.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M287.852 141.711V141.711' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M285.859 141.711H289.843' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M287.852 141.711V141.711' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M285.859 141.711H289.843' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M346.52 124.601V124.601' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M344.527 124.602H348.511' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M346.52 124.601V124.601' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M344.527 124.602H348.511' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M405.188 132.25V132.25' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M403.195 132.25H407.179' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M405.188 132.25V132.25' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M403.195 132.25H407.179' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M463.859 189.894V189.894' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M461.863 189.894H465.851' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M463.859 189.894V189.894' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M461.863 189.894H465.851' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M174.125 249.281H177.363V171.574H174.125ZM232.793 249.281H236.031V146.344H232.793ZM291.461 249.281H294.699V169.426H291.461ZM350.133 249.281H353.371V204.254H350.133ZM408.801 249.281H412.039V224.992H408.801ZM467.469 249.281H470.707V163.254H467.469Z' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M174.125 249.281H177.363V171.574H174.125ZM232.793 249.281H236.031V146.344H232.793ZM291.461 249.281H294.699V169.426H291.461ZM350.133 249.281H353.371V204.254H350.133ZM408.801 249.281H412.039V224.992H408.801ZM467.469 249.281H470.707V163.254H467.469Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M175.742 171.574V171.574' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M173.75 171.574H177.735' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M175.742 171.574V171.574' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M173.75 171.574H177.735' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M234.414 146.344V146.344' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M232.418 146.343H236.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M234.414 146.344V146.344' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M232.418 146.343H236.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M293.082 169.426V169.426' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M291.09 169.426H295.074' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M293.082 169.426V169.426' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M291.09 169.426H295.074' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M351.75 204.254V204.254' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M349.758 204.254H353.742' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M351.75 204.254V204.254' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M349.758 204.254H353.742' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M410.418 224.992V224.992' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M408.426 224.992H412.41' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M410.418 224.992V224.992' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M408.426 224.992H412.41' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M469.09 163.254V163.254' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M467.094 163.254H471.082' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M469.09 163.254V163.254' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M467.094 163.254H471.082' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M179.356 249.281H182.594V176.004H179.356ZM238.024 249.281H241.262V169.16H238.024ZM296.692 249.281H299.93V149.699H296.692ZM355.363 249.281H358.602V121.043H355.363ZM414.031 249.281H417.27V214.051H414.031ZM472.699 249.281H475.938V194.121H472.699Z' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M179.356 249.281H182.594V176.004H179.356ZM238.024 249.281H241.262V169.16H238.024ZM296.692 249.281H299.93V149.699H296.692ZM355.363 249.281H358.602V121.043H355.363ZM414.031 249.281H417.27V214.051H414.031ZM472.699 249.281H475.938V194.121H472.699Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M180.973 176.004V176.004' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M178.98 176.004H182.965' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M180.973 176.004V176.004' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M178.98 176.004H182.965' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M239.645 169.16V169.16' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M237.648 169.16H241.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M239.645 169.16V169.16' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M237.648 169.16H241.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M298.313 149.699V149.699' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M296.32 149.7H300.304' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M298.313 149.699V149.699' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M296.32 149.7H300.304' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M356.981 121.043V121.043' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M354.988 121.043H358.972' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M356.981 121.043V121.043' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M354.988 121.043H358.972' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M415.649 214.051V214.051' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M413.656 214.05H417.64' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M415.649 214.051V214.051' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M413.656 214.05H417.64' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M474.32 194.121V194.121' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M472.324 194.121H476.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M474.32 194.121V194.121' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M472.324 194.121H476.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M184.586 249.281H187.824V181.641H184.586ZM243.254 249.281H246.492V115.137H243.254ZM301.922 249.281H305.16V176.605H301.922ZM360.594 249.281H363.832V185.734H360.594ZM419.262 249.281H422.5V164.125H419.262ZM477.93 249.281H481.168V179.828H477.93Z' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M184.586 249.281H187.824V181.641H184.586ZM243.254 249.281H246.492V115.137H243.254ZM301.922 249.281H305.16V176.605H301.922ZM360.594 249.281H363.832V185.734H360.594ZM419.262 249.281H422.5V164.125H419.262ZM477.93 249.281H481.168V179.828H477.93Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M186.203 181.641V181.641' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M184.211 181.64H188.196' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M186.203 181.641V181.641' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M184.211 181.64H188.196' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M244.875 115.137V115.137' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M242.879 115.137H246.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M244.875 115.137V115.137' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M242.879 115.137H246.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M303.543 176.605V176.605' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M301.551 176.605H305.535' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M303.543 176.605V176.605' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M301.551 176.605H305.535' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M362.211 185.734V185.734' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M360.219 185.735H364.203' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M362.211 185.734V185.734' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M360.219 185.735H364.203' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M420.879 164.125V164.125' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M418.887 164.125H422.871' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M420.879 164.125V164.125' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M418.887 164.125H422.871' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M479.551 179.828V179.828' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M477.555 179.828H481.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M479.551 179.828V179.828' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M477.555 179.828H481.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M420.539 310.336H487.762V276.324H420.539Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 255.575 118.327)'>
+<use x='168.285' xlink:href='#g2-99' y='164.777'/>
+<use x='170.637' xlink:href='#g2-53' y='164.777'/>
+<use x='173.284' xlink:href='#g2-45' y='164.777'/>
+<use x='175.048' xlink:href='#g2-49' y='164.777'/>
+<use x='177.694' xlink:href='#g2-56' y='164.777'/>
+<use x='180.341' xlink:href='#g2-120' y='164.777'/>
+<use x='182.779' xlink:href='#g2-108' y='164.777'/>
+<use x='184.042' xlink:href='#g2-97' y='164.777'/>
+<use x='186.438' xlink:href='#g2-114' y='164.777'/>
+<use x='188.245' xlink:href='#g2-103' y='164.777'/>
+<use x='190.891' xlink:href='#g2-101' y='164.777'/>
+<use x='193.244' xlink:href='#g2-44' y='164.777'/>
+<use x='196.478' xlink:href='#g2-49' y='164.777'/>
+<use x='199.124' xlink:href='#g2-52' y='164.777'/>
+<use x='201.771' xlink:href='#g2-52' y='164.777'/>
+<use x='204.417' xlink:href='#g2-71' y='164.777'/>
+<use x='207.946' xlink:href='#g2-98' y='164.777'/>
+<use x='168.285' xlink:href='#g2-55' y='170.755'/>
+<use x='170.931' xlink:href='#g2-50' y='170.755'/>
+<use x='175.342' xlink:href='#g2-112' y='170.755'/>
+<use x='177.928' xlink:href='#g2-114' y='170.755'/>
+<use x='179.735' xlink:href='#g2-111' y='170.755'/>
+<use x='182.529' xlink:href='#g2-99' y='170.755'/>
+<use x='186.645' xlink:href='#g2-73' y='170.755'/>
+<use x='188.115' xlink:href='#g2-110' y='170.755'/>
+<use x='190.848' xlink:href='#g2-116' y='170.755'/>
+<use x='192.759' xlink:href='#g2-101' y='170.755'/>
+<use x='195.112' xlink:href='#g2-108' y='170.755'/>
+<use x='198.139' xlink:href='#g2-88' y='170.755'/>
+<use x='201.667' xlink:href='#g2-101' y='170.755'/>
+<use x='204.019' xlink:href='#g2-111' y='170.755'/>
+<use x='206.666' xlink:href='#g2-110' y='170.755'/>
+<use x='211.163' xlink:href='#g2-64' y='170.755'/>
+<use x='214.691' xlink:href='#g2-51' y='170.755'/>
+<use x='217.338' xlink:href='#g2-71' y='170.755'/>
+<use x='220.866' xlink:href='#g2-104' y='170.755'/>
+<use x='223.599' xlink:href='#g2-122' y='170.755'/>
+<use x='168.285' xlink:href='#g2-85' y='176.733'/>
+<use x='171.917' xlink:href='#g2-98' y='176.733'/>
+<use x='174.65' xlink:href='#g2-117' y='176.733'/>
+<use x='177.383' xlink:href='#g2-110' y='176.733'/>
+<use x='180.116' xlink:href='#g2-116' y='176.733'/>
+<use x='182.027' xlink:href='#g2-117' y='176.733'/>
+<use x='186.524' xlink:href='#g2-49' y='176.733'/>
+<use x='189.17' xlink:href='#g2-56' y='176.733'/>
+<use x='191.817' xlink:href='#g2-46' y='176.733'/>
+<use x='193.287' xlink:href='#g2-48' y='176.733'/>
+<use x='195.933' xlink:href='#g2-52' y='176.733'/>
+<use x='198.58' xlink:href='#g2-46' y='176.733'/>
+<use x='200.05' xlink:href='#g2-49' y='176.733'/>
+<use x='202.696' xlink:href='#g2-44' y='176.733'/>
+<use x='205.931' xlink:href='#g2-71' y='176.733'/>
+<use x='209.459' xlink:href='#g2-67' y='176.733'/>
+<use x='212.841' xlink:href='#g2-67' y='176.733'/>
+<use x='217.986' xlink:href='#g2-55' y='176.733'/>
+<use x='220.633' xlink:href='#g2-46' y='176.733'/>
+<use x='222.103' xlink:href='#g2-52' y='176.733'/>
+<use x='224.749' xlink:href='#g2-46' y='176.733'/>
+<use x='226.219' xlink:href='#g2-48' y='176.733'/>
+<use x='168.285' xlink:href='#g2-51' y='182.71'/>
+<use x='170.931' xlink:href='#g2-54' y='182.71'/>
+<use x='175.342' xlink:href='#g2-99' y='182.71'/>
+<use x='177.694' xlink:href='#g2-111' y='182.71'/>
+<use x='180.193' xlink:href='#g2-114' y='182.71'/>
+<use x='182.001' xlink:href='#g2-101' y='182.71'/>
+<use x='184.353' xlink:href='#g2-115' y='182.71'/>
+<use x='188.146' xlink:href='#g2-111' y='182.71'/>
+<use x='190.793' xlink:href='#g2-110' y='182.71'/>
+<use x='195.29' xlink:href='#g2-50' y='182.71'/>
+<use x='199.7' xlink:href='#g2-110' y='182.71'/>
+<use x='202.433' xlink:href='#g2-117' y='182.71'/>
+<use x='205.166' xlink:href='#g2-109' y='182.71'/>
+<use x='209.369' xlink:href='#g2-97' y='182.71'/>
+<use x='213.676' xlink:href='#g2-110' y='182.71'/>
+<use x='216.409' xlink:href='#g2-111' y='182.71'/>
+<use x='219.202' xlink:href='#g2-100' y='182.71'/>
+<use x='221.935' xlink:href='#g2-101' y='182.71'/>
+<use x='224.287' xlink:href='#g2-115' y='182.71'/>
+<use x='168.285' xlink:href='#g2-50' y='188.688'/>
+<use x='170.931' xlink:href='#g2-48' y='188.688'/>
+<use x='173.578' xlink:href='#g2-50' y='188.688'/>
+<use x='176.224' xlink:href='#g2-48' y='188.688'/>
+<use x='178.87' xlink:href='#g2-45' y='188.688'/>
+<use x='180.635' xlink:href='#g2-48' y='188.688'/>
+<use x='183.281' xlink:href='#g2-49' y='188.688'/>
+<use x='185.927' xlink:href='#g2-45' y='188.688'/>
+<use x='187.692' xlink:href='#g2-49' y='188.688'/>
+<use x='190.338' xlink:href='#g2-56' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 -42.693 344.258)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-48' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 15.976 344.258)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-48' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 74.645 344.258)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-48' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 133.314 344.258)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-48' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 191.983 344.258)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-48' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 250.652 344.258)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-48' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 -37.462 326.81)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-50' y='188.688'/>
+<use x='175.048' xlink:href='#g2-54' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 21.207 324.059)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-51' y='188.688'/>
+<use x='175.048' xlink:href='#g2-48' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 79.876 345.332)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-57' y='188.688'/>
+<use x='175.048' xlink:href='#g2-56' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 138.545 371.973)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-53' y='188.688'/>
+<use x='175.048' xlink:href='#g2-57' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 197.214 386.266)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-51' y='188.688'/>
+<use x='175.048' xlink:href='#g2-55' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 255.882 303.323)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-54' y='188.688'/>
+<use x='175.048' xlink:href='#g2-49' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 -32.232 347.211)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-57' y='188.688'/>
+<use x='175.048' xlink:href='#g2-54' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 26.437 339.963)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-54' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 85.106 343.52)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-49' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 143.775 354.928)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-56' y='188.688'/>
+<use x='175.048' xlink:href='#g2-52' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 202.444 345.801)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-57' y='188.688'/>
+<use x='175.048' xlink:href='#g2-56' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 261.113 307.081)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-53' y='188.688'/>
+<use x='175.048' xlink:href='#g2-53' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 -27.002 343.05)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-50' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 31.667 334.125)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-49' y='188.688'/>
+<use x='175.048' xlink:href='#g2-53' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 90.336 334.393)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-49' y='188.688'/>
+<use x='175.048' xlink:href='#g2-53' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 149.005 357.075)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-56' y='188.688'/>
+<use x='175.048' xlink:href='#g2-49' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 207.674 357.612)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-56' y='188.688'/>
+<use x='175.048' xlink:href='#g2-48' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 266.343 342.178)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-51' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 -21.771 342.446)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-51' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 36.898 306.544)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-53' y='188.688'/>
+<use x='175.048' xlink:href='#g2-54' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 95.567 333.789)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-49' y='188.688'/>
+<use x='175.048' xlink:href='#g2-54' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 154.236 353.72)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-56' y='188.688'/>
+<use x='175.048' xlink:href='#g2-54' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 212.905 243.599)'>
+<use x='163.396' xlink:href='#g4-1' y='188.688'/>
+<use x='166.901' xlink:href='#g4-1' y='188.688'/>
+<use x='170.407' xlink:href='#g4-1' y='188.688'/>
+<use x='173.912' xlink:href='#g2-50' y='188.688'/>
+<use x='176.558' xlink:href='#g2-55' y='188.688'/>
+<use x='179.205' xlink:href='#g2-46' y='188.688'/>
+<use x='180.675' xlink:href='#g2-51' y='188.688'/>
+<use x='183.321' xlink:href='#g2-51' y='188.688'/>
+<use x='185.968' xlink:href='#g2-120' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 271.574 352.109)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-56' y='188.688'/>
+<use x='175.048' xlink:href='#g2-56' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 -16.541 319.429)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-51' y='188.688'/>
+<use x='175.048' xlink:href='#g2-55' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 42.128 243.599)'>
+<use x='163.396' xlink:href='#g4-1' y='188.688'/>
+<use x='166.901' xlink:href='#g4-1' y='188.688'/>
+<use x='170.407' xlink:href='#g4-1' y='188.688'/>
+<use x='173.912' xlink:href='#g2-51' y='188.688'/>
+<use x='176.558' xlink:href='#g2-46' y='188.688'/>
+<use x='178.029' xlink:href='#g2-51' y='188.688'/>
+<use x='180.675' xlink:href='#g2-54' y='188.688'/>
+<use x='183.321' xlink:href='#g2-120' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 100.797 303.793)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-54' y='188.688'/>
+<use x='175.048' xlink:href='#g2-48' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 159.466 286.681)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-56' y='188.688'/>
+<use x='175.048' xlink:href='#g2-54' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 218.135 294.331)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-55' y='188.688'/>
+<use x='175.048' xlink:href='#g2-52' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 276.804 351.975)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-56' y='188.688'/>
+<use x='175.048' xlink:href='#g2-57' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 -11.31 333.655)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-49' y='188.688'/>
+<use x='175.048' xlink:href='#g2-54' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 47.359 308.423)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-53' y='188.688'/>
+<use x='175.048' xlink:href='#g2-51' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 106.028 331.508)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-49' y='188.688'/>
+<use x='175.048' xlink:href='#g2-57' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 164.697 366.336)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-54' y='188.688'/>
+<use x='175.048' xlink:href='#g2-55' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 223.366 387.072)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-51' y='188.688'/>
+<use x='175.048' xlink:href='#g2-54' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 282.035 325.334)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-50' y='188.688'/>
+<use x='175.048' xlink:href='#g2-56' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 -6.08 338.084)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-57' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 52.589 331.239)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-49' y='188.688'/>
+<use x='175.048' xlink:href='#g2-57' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 111.258 311.779)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-52' y='188.688'/>
+<use x='175.048' xlink:href='#g2-56' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 169.927 283.124)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-57' y='188.688'/>
+<use x='175.048' xlink:href='#g2-49' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 228.596 376.133)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-53' y='188.688'/>
+<use x='175.048' xlink:href='#g2-50' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 287.265 356.203)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-56' y='188.688'/>
+<use x='175.048' xlink:href='#g2-50' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 -0.849 343.721)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-49' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 57.82 277.219)'>
+<use x='168.285' xlink:href='#g2-50' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-48' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 116.489 338.688)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-56' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 175.158 347.815)'>
+<use x='168.285' xlink:href='#g2-48' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-57' y='188.688'/>
+<use x='175.048' xlink:href='#g2-53' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 233.827 326.206)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-50' y='188.688'/>
+<use x='175.048' xlink:href='#g2-55' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 292.496 341.909)'>
+<use x='168.285' xlink:href='#g2-49' y='188.688'/>
+<use x='170.931' xlink:href='#g2-46' y='188.688'/>
+<use x='172.401' xlink:href='#g2-48' y='188.688'/>
+<use x='175.048' xlink:href='#g2-52' y='188.688'/>
+</g>
+<g transform='matrix(0 -1 1 0 -75.53 385.58)'>
+<use x='168.285' xlink:href='#g1-82' y='188.688'/>
+<use x='174.255' xlink:href='#g1-101' y='188.688'/>
+<use x='178.351' xlink:href='#g1-108' y='188.688'/>
+<use x='180.551' xlink:href='#g1-97' y='188.688'/>
+<use x='184.979' xlink:href='#g1-116' y='188.688'/>
+<use x='188.307' xlink:href='#g1-105' y='188.688'/>
+<use x='190.507' xlink:href='#g1-118' y='188.688'/>
+<use x='194.755' xlink:href='#g1-101' y='188.688'/>
+<use x='201.922' xlink:href='#g1-114' y='188.688'/>
+<use x='205.07' xlink:href='#g1-115' y='188.688'/>
+<use x='208.603' xlink:href='#g1-115' y='188.688'/>
+<use x='215.207' xlink:href='#g3-40' y='188.688'/>
+<use x='218.5' xlink:href='#g3-108' y='188.688'/>
+<use x='220.521' xlink:href='#g3-111' y='188.688'/>
+<use x='224.52' xlink:href='#g3-119' y='188.688'/>
+<use x='230.068' xlink:href='#g3-101' y='188.688'/>
+<use x='233.832' xlink:href='#g3-114' y='188.688'/>
+<use x='239.547' xlink:href='#g3-105' y='188.688'/>
+<use x='241.567' xlink:href='#g3-115' y='188.688'/>
+<use x='247.636' xlink:href='#g3-98' y='188.688'/>
+<use x='252.244' xlink:href='#g3-101' y='188.688'/>
+<use x='256.008' xlink:href='#g3-116' y='188.688'/>
+<use x='259.066' xlink:href='#g3-116' y='188.688'/>
+<use x='262.124' xlink:href='#g3-101' y='188.688'/>
+<use x='265.887' xlink:href='#g3-114' y='188.688'/>
+<use x='268.779' xlink:href='#g3-41' y='188.688'/>
+</g>
+<path d='M136.149 309.68H341.363V279.68H136.149Z' fill='#ffffff'/>
+<path d='M136.149 309.68H341.363V279.68H136.149Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path d='M139.336 290.168H142.324V282.199H139.336ZM145.313 290.168H148.301V284.191H145.313Z' fill='#ffffff'/>
+<path d='M139.336 290.168H142.324V282.199H139.336ZM145.313 290.168H148.301V284.191H145.313Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -40.354 114.479)'>
+<use x='195.372' xlink:href='#g3-120' y='175.681'/>
+<use x='199.274' xlink:href='#g3-109' y='175.681'/>
+<use x='205.999' xlink:href='#g3-105' y='175.681'/>
+<use x='208.019' xlink:href='#g3-58' y='175.681'/>
+<use x='210.371' xlink:href='#g0-54' y='175.681'/>
+<use x='214.076' xlink:href='#g0-57' y='175.681'/>
+</g>
+<path d='M184.145 290.168H187.133V282.199H184.145ZM190.121 290.168H193.109V284.191H190.121Z' fill='#f0e0f0'/>
+<path d='M184.145 290.168H187.133V282.199H184.145ZM190.121 290.168H193.109V284.191H190.121Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.441 114.036)'>
+<use x='239.382' xlink:href='#g3-116' y='175.681'/>
+<use x='242.44' xlink:href='#g3-99' y='175.681'/>
+<use x='246.204' xlink:href='#g3-58' y='175.681'/>
+<use x='248.556' xlink:href='#g0-54' y='175.681'/>
+<use x='252.261' xlink:href='#g0-54' y='175.681'/>
+</g>
+<path d='M227.356 290.168H230.344V282.199H227.356ZM233.332 290.168H236.32V284.191H233.332Z' fill='#e1c2e1'/>
+<path d='M227.356 290.168H230.344V282.199H227.356ZM233.332 290.168H236.32V284.191H233.332Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.04 114.479)'>
+<use x='281.863' xlink:href='#g3-106' y='175.681'/>
+<use x='284.119' xlink:href='#g3-101' y='175.681'/>
+<use x='287.882' xlink:href='#g3-58' y='175.681'/>
+<use x='290.235' xlink:href='#g0-54' y='175.681'/>
+<use x='293.94' xlink:href='#g0-54' y='175.681'/>
+</g>
+<path d='M269.106 290.168H272.094V282.199H269.106ZM275.086 290.168H278.074V284.191H275.086Z' fill='#d1a3d1'/>
+<path d='M269.106 290.168H272.094V282.199H269.106ZM275.086 290.168H278.074V284.191H275.086Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -39.932 114.515)'>
+<use x='321.287' xlink:href='#g3-116' y='175.681'/>
+<use x='324.345' xlink:href='#g3-98' y='175.681'/>
+<use x='328.717' xlink:href='#g3-98' y='175.681'/>
+<use x='333.09' xlink:href='#g3-58' y='175.681'/>
+<use x='335.442' xlink:href='#g0-54' y='175.681'/>
+<use x='339.147' xlink:href='#g0-53' y='175.681'/>
+</g>
+<path d='M306.203 290.168H309.191V282.199H306.203ZM312.18 290.168H315.168V284.191H312.18Z' fill='#c285c2'/>
+<path d='M306.203 290.168H309.191V282.199H306.203ZM312.18 290.168H315.168V284.191H312.18Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.662 114.036)'>
+<use x='356.019' xlink:href='#g3-114' y='175.681'/>
+<use x='358.911' xlink:href='#g3-112' y='175.681'/>
+<use x='363.284' xlink:href='#g3-58' y='175.681'/>
+<use x='365.636' xlink:href='#g0-52' y='175.681'/>
+<use x='369.341' xlink:href='#g0-52' y='175.681'/>
+</g>
+<path d='M139.336 303.176H142.324V295.207H139.336ZM145.313 303.176H148.301V297.199H145.313Z' fill='#b366b3'/>
+<path d='M139.336 303.176H142.324V295.207H139.336ZM145.313 303.176H148.301V297.199H145.313Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -43.882 114.515)'>
+<use x='195.372' xlink:href='#g3-104' y='188.688'/>
+<use x='199.744' xlink:href='#g3-111' y='188.688'/>
+<use x='203.978' xlink:href='#g3-97' y='188.688'/>
+<use x='207.811' xlink:href='#g3-114' y='188.688'/>
+<use x='210.703' xlink:href='#g3-100' y='188.688'/>
+<use x='215.076' xlink:href='#g3-58' y='188.688'/>
+<use x='217.428' xlink:href='#g0-51' y='188.688'/>
+<use x='221.133' xlink:href='#g0-54' y='188.688'/>
+</g>
+<path d='M184.145 303.176H187.133V295.207H184.145ZM190.121 303.176H193.109V297.199H190.121Z' fill='#a447a4'/>
+<path d='M184.145 303.176H187.133V295.207H184.145ZM190.121 303.176H193.109V297.199H190.121Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -43.084 114.515)'>
+<use x='239.382' xlink:href='#g3-109' y='188.688'/>
+<use x='246.107' xlink:href='#g3-101' y='188.688'/>
+<use x='249.871' xlink:href='#g3-115' y='188.688'/>
+<use x='253.117' xlink:href='#g3-104' y='188.688'/>
+<use x='257.489' xlink:href='#g3-58' y='188.688'/>
+<use x='259.842' xlink:href='#g0-54' y='188.688'/>
+<use x='263.547' xlink:href='#g0-49' y='188.688'/>
+</g>
+<path d='M227.356 303.176H230.344V295.207H227.356ZM233.332 303.176H236.32V297.199H233.332Z' fill='#942994'/>
+<path d='M227.356 303.176H230.344V295.207H227.356ZM233.332 303.176H236.32V297.199H233.332Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -42.353 114.515)'>
+<use x='281.863' xlink:href='#g3-103' y='188.688'/>
+<use x='286.097' xlink:href='#g3-108' y='188.688'/>
+<use x='288.118' xlink:href='#g3-105' y='188.688'/>
+<use x='290.138' xlink:href='#g3-98' y='188.688'/>
+<use x='294.746' xlink:href='#g3-99' y='188.688'/>
+<use x='298.509' xlink:href='#g3-58' y='188.688'/>
+<use x='300.862' xlink:href='#g0-54' y='188.688'/>
+<use x='304.567' xlink:href='#g0-52' y='188.688'/>
+</g>
+<path d='M269.106 303.176H272.094V295.207H269.106ZM275.086 303.176H278.074V297.199H275.086Z' fill='#850a85'/>
+<path d='M269.106 303.176H272.094V295.207H269.106ZM275.086 303.176H278.074V297.199H275.086Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -40.026 114.479)'>
+<use x='321.287' xlink:href='#g3-115' y='188.688'/>
+<use x='324.533' xlink:href='#g3-109' y='188.688'/>
+<use x='331.258' xlink:href='#g3-105' y='188.688'/>
+<use x='333.278' xlink:href='#g3-58' y='188.688'/>
+<use x='335.63' xlink:href='#g0-52' y='188.688'/>
+<use x='339.335' xlink:href='#g0-53' y='188.688'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='164.687pt' version='1.1' viewBox='52.938 54.996 381.625 164.687' width='381.625pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip1'>
+<path d='M82.148 203.937H434.164V78.691H82.148Z'/>
+</clipPath>
+<use id='g2-40' transform='scale(1.6)' xlink:href='#g1-40'/>
+<use id='g2-41' transform='scale(1.6)' xlink:href='#g1-41'/>
+<use id='g2-78' transform='scale(1.6)' xlink:href='#g1-78'/>
+<use id='g2-97' transform='scale(1.6)' xlink:href='#g1-97'/>
+<use id='g2-98' transform='scale(1.6)' xlink:href='#g1-98'/>
+<use id='g2-99' transform='scale(1.6)' xlink:href='#g1-99'/>
+<use id='g2-100' transform='scale(1.6)' xlink:href='#g1-100'/>
+<use id='g2-101' transform='scale(1.6)' xlink:href='#g1-101'/>
+<use id='g2-102' transform='scale(1.6)' xlink:href='#g1-102'/>
+<use id='g2-105' transform='scale(1.6)' xlink:href='#g1-105'/>
+<use id='g2-108' transform='scale(1.6)' xlink:href='#g1-108'/>
+<use id='g2-110' transform='scale(1.6)' xlink:href='#g1-110'/>
+<use id='g2-111' transform='scale(1.6)' xlink:href='#g1-111'/>
+<use id='g2-112' transform='scale(1.6)' xlink:href='#g1-112'/>
+<use id='g2-114' transform='scale(1.6)' xlink:href='#g1-114'/>
+<use id='g2-115' transform='scale(1.6)' xlink:href='#g1-115'/>
+<use id='g2-116' transform='scale(1.6)' xlink:href='#g1-116'/>
+<use id='g2-119' transform='scale(1.6)' xlink:href='#g1-119'/>
+<path d='M1.519 -3.736C1.435 -3.736 1.425 -3.736 1.365 -3.681C0.737 -3.133 0.418 -2.247 0.418 -1.245C0.418 -0.304 0.702 0.603 1.36 1.181C1.425 1.245 1.435 1.245 1.519 1.245H1.758C1.743 1.235 1.26 0.822 1.031 0.045C0.912 -0.344 0.852 -0.752 0.852 -1.245C0.852 -2.969 1.659 -3.651 1.758 -3.736H1.519Z' id='g1-40'/>
+<path d='M0.533 1.245C0.618 1.245 0.628 1.245 0.687 1.191C1.315 0.643 1.634 -0.244 1.634 -1.245C1.634 -2.187 1.35 -3.093 0.692 -3.671C0.628 -3.736 0.618 -3.736 0.533 -3.736H0.294C0.309 -3.726 0.792 -3.313 1.021 -2.535C1.141 -2.147 1.2 -1.738 1.2 -1.245C1.2 0.478 0.394 1.161 0.294 1.245H0.533Z' id='g1-41'/>
+<path d='M0.956 -0.448H0.508V0H0.956V-0.448Z' id='g1-46'/>
+<path d='M2.431 -1.619C2.431 -1.858 2.431 -2.441 2.197 -2.849C1.943 -3.298 1.559 -3.372 1.32 -3.372C1.096 -3.372 0.707 -3.303 0.458 -2.874C0.219 -2.476 0.209 -1.933 0.209 -1.619C0.209 -1.25 0.229 -0.797 0.438 -0.418C0.658 -0.015 1.026 0.105 1.32 0.105C1.818 0.105 2.092 -0.184 2.242 -0.498C2.416 -0.852 2.431 -1.31 2.431 -1.619ZM1.32 -0.224C1.111 -0.224 0.872 -0.344 0.747 -0.702C0.648 -1.006 0.643 -1.32 0.643 -1.684C0.643 -2.142 0.643 -3.044 1.32 -3.044S1.998 -2.142 1.998 -1.684C1.998 -1.355 1.998 -0.981 1.878 -0.663C1.738 -0.304 1.484 -0.224 1.32 -0.224Z' id='g1-48'/>
+<path d='M1.599 -3.372H1.489C1.166 -3.073 0.757 -3.054 0.458 -3.044V-2.73C0.653 -2.735 0.902 -2.745 1.151 -2.844V-0.314H0.488V0H2.262V-0.314H1.599V-3.372Z' id='g1-49'/>
+<path d='M1.41 -0.384C1.35 -0.384 1.29 -0.379 1.23 -0.379H0.663L1.435 -1.061C1.524 -1.141 1.768 -1.325 1.863 -1.405C2.082 -1.604 2.376 -1.863 2.376 -2.296C2.376 -2.859 1.958 -3.372 1.245 -3.372C0.717 -3.372 0.389 -3.088 0.219 -2.58L0.453 -2.286C0.568 -2.705 0.742 -3.029 1.176 -3.029C1.594 -3.029 1.913 -2.735 1.913 -2.286C1.913 -1.873 1.669 -1.639 1.37 -1.355C1.27 -1.255 1.001 -1.031 0.897 -0.932C0.752 -0.802 0.408 -0.468 0.264 -0.344V0H2.376V-0.384H1.41Z' id='g1-50'/>
+<path d='M0.498 -2.555C0.702 -2.954 1.061 -3.064 1.3 -3.064C1.594 -3.064 1.813 -2.894 1.813 -2.61C1.813 -2.346 1.634 -2.022 1.255 -1.958C1.23 -1.953 1.21 -1.953 0.882 -1.928V-1.599H1.27C1.743 -1.599 1.918 -1.225 1.918 -0.912C1.918 -0.523 1.679 -0.224 1.29 -0.224C0.936 -0.224 0.533 -0.394 0.284 -0.712L0.219 -0.389C0.508 -0.065 0.912 0.105 1.3 0.105C1.953 0.105 2.421 -0.384 2.421 -0.907C2.421 -1.315 2.092 -1.644 1.699 -1.758C2.077 -1.953 2.271 -2.286 2.271 -2.61C2.271 -3.034 1.838 -3.372 1.305 -3.372C0.867 -3.372 0.503 -3.143 0.294 -2.844L0.498 -2.555Z' id='g1-51'/>
+<path d='M1.973 -0.832H2.491V-1.161H1.973V-3.268H1.479L0.149 -1.161V-0.832H1.544V0H1.973V-0.832ZM0.573 -1.161C0.722 -1.395 1.579 -2.725 1.579 -3.024V-1.161H0.573Z' id='g1-52'/>
+<path d='M0.817 -2.924H2.197V-3.268H0.418V-1.405H0.782C0.902 -1.674 1.136 -1.793 1.36 -1.793C1.564 -1.793 1.873 -1.654 1.873 -1.021C1.873 -0.369 1.46 -0.224 1.205 -0.224C0.877 -0.224 0.558 -0.399 0.389 -0.682L0.199 -0.384C0.443 -0.08 0.812 0.105 1.205 0.105C1.863 0.105 2.376 -0.403 2.376 -1.011C2.376 -1.629 1.918 -2.122 1.37 -2.122C1.156 -2.122 0.966 -2.047 0.817 -1.923V-2.924Z' id='g1-53'/>
+<path d='M2.187 -3.273C1.918 -3.372 1.729 -3.372 1.634 -3.372C0.877 -3.372 0.219 -2.66 0.219 -1.609C0.219 -0.259 0.827 0.105 1.33 0.105C1.733 0.105 1.943 -0.085 2.097 -0.244C2.416 -0.583 2.421 -0.936 2.421 -1.111C2.421 -1.763 2.067 -2.306 1.584 -2.306C1.096 -2.306 0.832 -2.052 0.687 -1.908C0.752 -2.59 1.101 -3.064 1.639 -3.064C1.738 -3.064 1.938 -3.054 2.187 -2.959V-3.273ZM0.692 -1.096C0.692 -1.126 0.692 -1.2 0.697 -1.225C0.697 -1.564 0.912 -1.978 1.355 -1.978C1.963 -1.978 1.963 -1.28 1.963 -1.111C1.963 -0.922 1.963 -0.712 1.828 -0.503C1.709 -0.324 1.559 -0.224 1.33 -0.224C0.802 -0.224 0.717 -0.877 0.692 -1.096Z' id='g1-54'/>
+<path d='M1.23 -2.884C1.29 -2.884 1.35 -2.889 1.41 -2.889H2.037C1.28 -2.147 0.797 -1.106 0.797 0.05H1.265C1.265 -1.405 1.973 -2.451 2.421 -2.919V-3.268H0.219V-2.884H1.23Z' id='g1-55'/>
+<path d='M1.704 -1.763C2.032 -1.868 2.346 -2.132 2.346 -2.501C2.346 -2.954 1.913 -3.372 1.32 -3.372S0.294 -2.954 0.294 -2.501C0.294 -2.127 0.618 -1.863 0.936 -1.763C0.498 -1.629 0.219 -1.29 0.219 -0.907C0.219 -0.374 0.692 0.105 1.32 0.105S2.421 -0.374 2.421 -0.907C2.421 -1.29 2.137 -1.629 1.704 -1.763ZM1.32 -1.928C0.966 -1.928 0.677 -2.132 0.677 -2.496C0.677 -2.814 0.902 -3.064 1.32 -3.064C1.733 -3.064 1.963 -2.814 1.963 -2.496C1.963 -2.142 1.684 -1.928 1.32 -1.928ZM1.32 -0.224C0.976 -0.224 0.672 -0.443 0.672 -0.912C0.672 -1.36 0.961 -1.599 1.32 -1.599S1.968 -1.355 1.968 -0.912C1.968 -0.443 1.659 -0.224 1.32 -0.224Z' id='g1-56'/>
+<path d='M0.384 -0.125C0.628 0.055 0.852 0.105 1.086 0.105C1.783 0.105 2.421 -0.598 2.421 -1.669C2.421 -3.029 1.818 -3.372 1.34 -3.372C0.897 -3.372 0.692 -3.163 0.548 -3.019C0.229 -2.695 0.219 -2.351 0.219 -2.157C0.219 -1.514 0.568 -0.961 1.056 -0.961C1.619 -0.961 1.928 -1.335 1.953 -1.365C1.883 -0.573 1.494 -0.224 1.086 -0.224C0.827 -0.224 0.667 -0.319 0.553 -0.413L0.384 -0.125ZM1.938 -2.162C1.943 -2.132 1.943 -2.082 1.943 -2.052C1.943 -1.684 1.719 -1.29 1.285 -1.29C1.096 -1.29 0.946 -1.345 0.817 -1.549C0.687 -1.743 0.677 -1.933 0.677 -2.157C0.677 -2.351 0.677 -2.575 0.832 -2.795C0.936 -2.944 1.086 -3.064 1.335 -3.064C1.818 -3.064 1.923 -2.481 1.938 -2.162Z' id='g1-57'/>
+<path d='M1.176 -3.457H0.498V0H0.917V-3.064H0.922L2.555 0H3.233V-3.457H2.814V-0.394H2.809L1.176 -3.457Z' id='g1-78'/>
+<path d='M2.122 -1.435C2.122 -1.943 1.733 -2.286 1.24 -2.286C0.927 -2.286 0.687 -2.222 0.408 -2.072L0.438 -1.709C0.603 -1.818 0.847 -1.968 1.24 -1.968C1.46 -1.968 1.689 -1.803 1.689 -1.43V-1.23C0.951 -1.205 0.224 -1.051 0.224 -0.588C0.224 -0.339 0.394 0.05 0.832 0.05C1.046 0.05 1.44 0.005 1.704 -0.189V0H2.122V-1.435ZM1.689 -0.707C1.689 -0.608 1.689 -0.478 1.514 -0.374C1.355 -0.284 1.161 -0.279 1.106 -0.279C0.832 -0.279 0.623 -0.403 0.623 -0.593C0.623 -0.912 1.465 -0.941 1.689 -0.951V-0.707Z' id='g1-97'/>
+<path d='M0.842 -3.457H0.423V0H0.857V-0.234C0.966 -0.139 1.205 0.05 1.569 0.05C2.112 0.05 2.55 -0.458 2.55 -1.111C2.55 -1.714 2.207 -2.262 1.709 -2.262C1.395 -2.262 1.091 -2.162 0.842 -1.978V-3.457ZM0.857 -1.569C0.857 -1.649 0.857 -1.709 1.031 -1.823C1.106 -1.868 1.24 -1.933 1.41 -1.933C1.743 -1.933 2.117 -1.709 2.117 -1.111C2.117 -0.503 1.704 -0.279 1.355 -0.279C1.171 -0.279 0.996 -0.364 0.857 -0.588V-1.569Z' id='g1-98'/>
+<path d='M2.167 -0.543C1.918 -0.384 1.649 -0.294 1.34 -0.294C0.882 -0.294 0.613 -0.663 0.613 -1.111C0.613 -1.494 0.812 -1.943 1.355 -1.943C1.694 -1.943 1.853 -1.873 2.107 -1.714L2.172 -2.072C1.873 -2.222 1.743 -2.286 1.355 -2.286C0.608 -2.286 0.179 -1.684 0.179 -1.106C0.179 -0.498 0.658 0.05 1.335 0.05C1.684 0.05 1.983 -0.055 2.197 -0.179L2.167 -0.543Z' id='g1-99'/>
+<path d='M2.306 -3.457H1.888V-1.998C1.569 -2.232 1.245 -2.262 1.101 -2.262C0.578 -2.262 0.179 -1.738 0.179 -1.106S0.573 0.05 1.086 0.05C1.395 0.05 1.684 -0.09 1.873 -0.259V0H2.306V-3.457ZM1.873 -0.618C1.748 -0.413 1.559 -0.279 1.32 -0.279C0.971 -0.279 0.613 -0.523 0.613 -1.101C0.613 -1.724 1.036 -1.933 1.375 -1.933C1.574 -1.933 1.743 -1.848 1.873 -1.679V-0.618Z' id='g1-100'/>
+<path d='M2.142 -0.543C1.863 -0.344 1.549 -0.279 1.335 -0.279C0.902 -0.279 0.573 -0.633 0.558 -1.091H2.192C2.192 -1.32 2.167 -1.654 1.973 -1.938C1.793 -2.192 1.494 -2.286 1.25 -2.286C0.643 -2.286 0.174 -1.753 0.174 -1.121C0.174 -0.483 0.672 0.05 1.33 0.05C1.619 0.05 1.918 -0.035 2.172 -0.189L2.142 -0.543ZM0.593 -1.39C0.707 -1.788 1.001 -1.958 1.25 -1.958C1.469 -1.958 1.793 -1.853 1.888 -1.39H0.593Z' id='g1-101'/>
+<path d='M0.946 -1.898H1.514V-2.212H0.932V-2.785C0.932 -3.128 1.245 -3.178 1.41 -3.178C1.514 -3.178 1.649 -3.163 1.833 -3.093V-3.457C1.704 -3.487 1.549 -3.507 1.415 -3.507C0.902 -3.507 0.528 -3.138 0.528 -2.645V-2.212H0.144V-1.898H0.528V0H0.946V-1.898Z' id='g1-102'/>
+<path d='M0.877 -3.417H0.374V-2.914H0.877V-3.417ZM0.837 -2.212H0.418V0H0.837V-2.212Z' id='g1-105'/>
+<path d='M0.837 -3.457H0.418V0H0.837V-3.457Z' id='g1-108'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-110'/>
+<path d='M2.491 -1.091C2.491 -1.748 1.968 -2.286 1.32 -2.286S0.149 -1.743 0.149 -1.091C0.149 -0.458 0.677 0.05 1.32 0.05C1.968 0.05 2.491 -0.458 2.491 -1.091ZM1.32 -0.294C0.927 -0.294 0.583 -0.583 0.583 -1.146S0.951 -1.958 1.32 -1.958C1.694 -1.958 2.057 -1.699 2.057 -1.146C2.057 -0.578 1.704 -0.294 1.32 -0.294Z' id='g1-111'/>
+<path d='M0.857 -0.234C1.121 0.005 1.405 0.05 1.574 0.05C2.102 0.05 2.55 -0.453 2.55 -1.111C2.55 -1.709 2.222 -2.262 1.729 -2.262C1.504 -2.262 1.131 -2.197 0.842 -1.973V-2.212H0.423V0.966H0.857V-0.234ZM0.857 -1.654C0.971 -1.793 1.166 -1.918 1.405 -1.918C1.803 -1.918 2.117 -1.549 2.117 -1.111C2.117 -0.618 1.743 -0.279 1.355 -0.279C1.28 -0.279 1.156 -0.289 1.026 -0.394C0.877 -0.508 0.857 -0.583 0.857 -0.677V-1.654Z' id='g1-112'/>
+<path d='M0.842 -1.061C0.842 -1.599 1.29 -1.888 1.729 -1.893V-2.262C1.31 -2.257 1.006 -2.052 0.807 -1.788V-2.247H0.423V0H0.842V-1.061Z' id='g1-114'/>
+<path d='M1.818 -2.132C1.479 -2.271 1.23 -2.286 1.051 -2.286C0.927 -2.286 0.174 -2.286 0.174 -1.624C0.174 -1.39 0.304 -1.26 0.369 -1.2C0.543 -1.026 0.752 -0.986 1.016 -0.936C1.25 -0.892 1.519 -0.842 1.519 -0.603C1.519 -0.289 1.106 -0.289 1.036 -0.289C0.717 -0.289 0.418 -0.403 0.219 -0.543L0.149 -0.169C0.319 -0.085 0.623 0.05 1.036 0.05C1.26 0.05 1.479 0.015 1.664 -0.12C1.848 -0.259 1.908 -0.478 1.908 -0.648C1.908 -0.737 1.898 -0.932 1.689 -1.121C1.504 -1.285 1.325 -1.32 1.086 -1.365C0.792 -1.42 0.563 -1.465 0.563 -1.684C0.563 -1.968 0.927 -1.968 1.001 -1.968C1.285 -1.968 1.504 -1.908 1.753 -1.778L1.818 -2.132Z' id='g1-115'/>
+<path d='M0.936 -1.898H1.674V-2.212H0.936V-2.844H0.553V-2.212H0.1V-1.898H0.538V-0.638C0.538 -0.304 0.623 0.05 0.981 0.05S1.614 -0.065 1.763 -0.134L1.679 -0.453C1.514 -0.334 1.34 -0.294 1.2 -0.294C0.991 -0.294 0.936 -0.498 0.936 -0.727V-1.898Z' id='g1-116'/>
+<path d='M3.537 -2.212H3.148C3.103 -2.072 2.824 -1.23 2.67 -0.712C2.63 -0.568 2.58 -0.408 2.565 -0.294H2.56C2.531 -0.498 2.356 -1.036 2.346 -1.071L1.978 -2.212H1.599C1.455 -1.783 1.081 -0.667 1.041 -0.304H1.036C0.996 -0.658 0.628 -1.758 0.548 -1.998C0.508 -2.117 0.508 -2.127 0.483 -2.212H0.075L0.802 0H1.22C1.225 -0.02 1.36 -0.413 1.534 -0.966C1.609 -1.21 1.758 -1.689 1.783 -1.908L1.788 -1.913C1.798 -1.808 1.828 -1.699 1.863 -1.574S1.933 -1.315 1.968 -1.2L2.351 0H2.809L3.537 -2.212Z' id='g1-119'/>
+<path d='M1.38 -1.141L2.346 -2.212H1.908L1.2 -1.395L0.478 -2.212H0.03L1.026 -1.141L0 0H0.443L1.2 -0.936L1.988 0H2.436L1.38 -1.141Z' id='g1-120'/>
+<path d='M1.445 -1.245C1.445 -1.41 1.305 -1.549 1.141 -1.549S0.837 -1.41 0.837 -1.245S0.976 -0.941 1.141 -0.941S1.445 -1.081 1.445 -1.245Z' id='g3-1'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g0-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g0-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g0-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g0-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g0-108'/>
+<path d='M6.581 -2.663C6.581 -3.327 6.402 -4.08 5.317 -4.08C4.564 -4.08 4.142 -3.622 3.927 -3.344C3.865 -3.524 3.676 -4.08 2.762 -4.08C2.053 -4.08 1.623 -3.667 1.417 -3.398V-4.035H0.726V0H1.47V-2.188C1.47 -2.78 1.704 -3.497 2.385 -3.497C3.282 -3.497 3.282 -2.86 3.282 -2.6V0H4.026V-2.188C4.026 -2.78 4.259 -3.497 4.94 -3.497C5.837 -3.497 5.837 -2.86 5.837 -2.6V0H6.581V-2.663Z' id='g0-109'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g0-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g0-118'/>
+</defs>
+<g id='page1'>
+<path d='M140.82 212.793V203.937M199.488 212.793V203.937M258.156 212.793V203.937M316.824 212.793V203.937M375.496 212.793V203.937M140.82 69.836V78.691M199.488 69.836V78.691M258.156 69.836V78.691M316.824 69.836V78.691M375.496 69.836V78.691' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M111.484 208.191V203.937M170.152 208.191V203.937M228.824 208.191V203.937M287.492 208.191V203.937M346.16 208.191V203.937M404.828 208.191V203.937M111.484 74.441V78.691M170.152 74.441V78.691M228.824 74.441V78.691M287.492 74.441V78.691M346.16 74.441V78.691M404.828 74.441V78.691' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 203.937H86.402M82.148 172.625H86.402M82.148 141.316H86.402M82.148 110.004H86.402M82.148 78.691H86.402M434.164 203.937H429.91M434.164 172.625H429.91M434.164 141.316H429.91M434.164 110.004H429.91M434.164 78.691H429.91' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 203.937V78.691H434.164V203.937H82.148Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -11.54 33.989)'>
+<use x='114.487' xlink:href='#g2-99' y='183.949'/>
+<use x='118.25' xlink:href='#g2-102' y='183.949'/>
+<use x='120.838' xlink:href='#g2-114' y='183.949'/>
+<use x='123.73' xlink:href='#g2-97' y='183.949'/>
+<use x='127.798' xlink:href='#g2-99' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 41.401 33.989)'>
+<use x='114.487' xlink:href='#g2-101' y='183.949'/>
+<use x='118.25' xlink:href='#g2-115' y='183.949'/>
+<use x='121.497' xlink:href='#g2-112' y='183.949'/>
+<use x='125.634' xlink:href='#g2-114' y='183.949'/>
+<use x='128.526' xlink:href='#g2-101' y='183.949'/>
+<use x='132.29' xlink:href='#g2-115' y='183.949'/>
+<use x='135.536' xlink:href='#g2-115' y='183.949'/>
+<use x='138.782' xlink:href='#g2-111' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 103.095 33.989)'>
+<use x='114.487' xlink:href='#g2-98' y='183.949'/>
+<use x='118.859' xlink:href='#g2-97' y='183.949'/>
+<use x='122.692' xlink:href='#g2-114' y='183.949'/>
+<use x='125.584' xlink:href='#g2-110' y='183.949'/>
+<use x='129.957' xlink:href='#g2-101' y='183.949'/>
+<use x='133.72' xlink:href='#g2-115' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 162.903 33.989)'>
+<use x='114.487' xlink:href='#g2-108' y='183.949'/>
+<use x='116.507' xlink:href='#g2-101' y='183.949'/>
+<use x='120.271' xlink:href='#g2-97' y='183.949'/>
+<use x='124.339' xlink:href='#g2-110' y='183.949'/>
+<use x='128.711' xlink:href='#g2-78' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 223.526 33.989)'>
+<use x='114.487' xlink:href='#g2-114' y='183.949'/>
+<use x='117.379' xlink:href='#g2-101' y='183.949'/>
+<use x='121.142' xlink:href='#g2-100' y='183.949'/>
+<use x='125.515' xlink:href='#g2-105' y='183.949'/>
+<use x='127.535' xlink:href='#g2-115' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 277.054 33.989)'>
+<use x='114.487' xlink:href='#g2-108' y='183.949'/>
+<use x='116.507' xlink:href='#g2-97' y='183.949'/>
+<use x='120.34' xlink:href='#g2-114' y='183.949'/>
+<use x='123.232' xlink:href='#g2-115' y='183.949'/>
+<use x='126.478' xlink:href='#g2-111' y='183.949'/>
+<use x='130.712' xlink:href='#g2-110' y='183.949'/>
+<use x='135.085' xlink:href='#g2-78' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 21.624)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -9.688)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -40.999)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -72.311)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -103.622)'>
+<use x='114.487' xlink:href='#g1-50' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<path clip-path='url(#clip1)' d='M82.148 141.316H434.164' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M91.559 203.937H94.797V141.316H91.559ZM150.227 203.937H153.465V141.316H150.227ZM208.899 203.937H212.133V141.316H208.899ZM267.567 203.937H270.805V141.316H267.567ZM326.234 203.937H329.473V141.316H326.234ZM384.902 203.937H388.141V141.316H384.902Z' fill='#e0e0f0'/>
+<path clip-path='url(#clip1)' d='M91.559 203.937H94.797V141.316H91.559ZM150.227 203.937H153.465V141.316H150.227ZM208.899 203.937H212.133V141.316H208.899ZM267.567 203.937H270.805V141.316H267.567ZM326.234 203.937H329.473V141.316H326.234ZM384.902 203.937H388.141V141.316H384.902Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M93.18 141.316V141.191' fill='#e0e0f0'/>
+<path clip-path='url(#clip1)' d='M93.18 141.316V141.191' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M91.184 141.191H95.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M93.18 141.316V141.441' fill='#e0e0f0'/>
+<path clip-path='url(#clip1)' d='M93.18 141.316V141.441' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M95.172 141.442H91.187' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M151.848 141.316V141.316' fill='#e0e0f0'/>
+<path clip-path='url(#clip1)' d='M149.855 141.316H153.84' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M151.848 141.316V141.316' fill='#e0e0f0'/>
+<path clip-path='url(#clip1)' d='M149.855 141.316H153.84' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M210.516 141.316V141.004' fill='#e0e0f0'/>
+<path clip-path='url(#clip1)' d='M210.516 141.316V141.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M208.523 141.004H212.507' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M210.516 141.316V141.629' fill='#e0e0f0'/>
+<path clip-path='url(#clip1)' d='M210.516 141.316V141.629' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M212.508 141.629H208.523' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M269.184 141.316V141.066' fill='#e0e0f0'/>
+<path clip-path='url(#clip1)' d='M269.184 141.316V141.066' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M267.191 141.067H271.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M269.184 141.316V141.566' fill='#e0e0f0'/>
+<path clip-path='url(#clip1)' d='M269.184 141.316V141.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M271.179 141.567H267.191' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M327.856 141.316V140.625' fill='#e0e0f0'/>
+<path clip-path='url(#clip1)' d='M327.856 141.316V140.625' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M325.859 140.625H329.847' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M327.856 141.316V142.004' fill='#e0e0f0'/>
+<path clip-path='url(#clip1)' d='M327.856 141.316V142.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M329.847 142.004H325.863' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M386.524 141.316V141.004' fill='#e0e0f0'/>
+<path clip-path='url(#clip1)' d='M386.524 141.316V141.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M384.531 141.004H388.515' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M386.524 141.316V141.629' fill='#e0e0f0'/>
+<path clip-path='url(#clip1)' d='M386.524 141.316V141.629' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M388.515 141.629H384.531' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M96.789 203.937H100.027V138.937H96.789ZM155.457 203.937H158.695V139.312H155.457ZM214.129 203.937H217.363V141.004H214.129ZM272.797 203.937H276.035V136.179H272.797ZM331.465 203.937H334.703V136.617H331.465ZM390.133 203.937H393.371V78.691H390.133Z' fill='#c2c2e1'/>
+<path clip-path='url(#clip1)' d='M96.789 203.937H100.027V138.937H96.789ZM155.457 203.937H158.695V139.312H155.457ZM214.129 203.937H217.363V141.004H214.129ZM272.797 203.937H276.035V136.179H272.797ZM331.465 203.937H334.703V136.617H331.465ZM390.133 203.937H393.371V78.691H390.133Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M98.41 138.937V138.683' fill='#c2c2e1'/>
+<path clip-path='url(#clip1)' d='M98.41 138.937V138.683' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M96.414 138.683H100.399' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M98.41 138.937V139.187' fill='#c2c2e1'/>
+<path clip-path='url(#clip1)' d='M98.41 138.937V139.187' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M100.402 139.187H96.418' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M157.078 139.312V139.312' fill='#c2c2e1'/>
+<path clip-path='url(#clip1)' d='M155.086 139.312H159.071' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M157.078 139.312V139.312' fill='#c2c2e1'/>
+<path clip-path='url(#clip1)' d='M155.086 139.312H159.071' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M215.746 141.004V140.625' fill='#c2c2e1'/>
+<path clip-path='url(#clip1)' d='M215.746 141.004V140.625' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M213.754 140.625H217.739' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M215.746 141.004V141.379' fill='#c2c2e1'/>
+<path clip-path='url(#clip1)' d='M215.746 141.004V141.379' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M217.739 141.379H213.754' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M274.414 136.179V135.992' fill='#c2c2e1'/>
+<path clip-path='url(#clip1)' d='M274.414 136.179V135.992' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M272.422 135.992H276.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M274.414 136.179V136.367' fill='#c2c2e1'/>
+<path clip-path='url(#clip1)' d='M274.414 136.179V136.367' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M276.41 136.367H272.422' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M333.086 136.617V136.429' fill='#c2c2e1'/>
+<path clip-path='url(#clip1)' d='M333.086 136.617V136.429' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M331.09 136.43H335.078' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M333.086 136.617V136.804' fill='#c2c2e1'/>
+<path clip-path='url(#clip1)' d='M333.086 136.617V136.804' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M335.078 136.804H331.094' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M391.754 78.691V78.691' fill='#c2c2e1'/>
+<path clip-path='url(#clip1)' d='M389.762 78.691H393.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M391.754 78.691V78.691' fill='#c2c2e1'/>
+<path clip-path='url(#clip1)' d='M389.762 78.691H393.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M102.02 203.937H105.258V138.433H102.02ZM160.688 203.937H163.926V136.742H160.688ZM219.359 203.937H222.594V141.004H219.359ZM278.027 203.937H281.266V137.933H278.027ZM336.695 203.937H339.934V132.234H336.695ZM395.363 203.937H398.602V78.691H395.363Z' fill='#a3a3d1'/>
+<path clip-path='url(#clip1)' d='M102.02 203.937H105.258V138.433H102.02ZM160.688 203.937H163.926V136.742H160.688ZM219.359 203.937H222.594V141.004H219.359ZM278.027 203.937H281.266V137.933H278.027ZM336.695 203.937H339.934V132.234H336.695ZM395.363 203.937H398.602V78.691H395.363Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M103.641 138.433V138.121' fill='#a3a3d1'/>
+<path clip-path='url(#clip1)' d='M103.641 138.433V138.121' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M101.644 138.122H105.629' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M103.641 138.433V138.746' fill='#a3a3d1'/>
+<path clip-path='url(#clip1)' d='M103.641 138.433V138.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M105.633 138.746H101.648' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M162.309 136.742V135.867' fill='#a3a3d1'/>
+<path clip-path='url(#clip1)' d='M162.309 136.742V135.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M160.316 135.867H164.301' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M162.309 136.742V137.621' fill='#a3a3d1'/>
+<path clip-path='url(#clip1)' d='M162.309 136.742V137.621' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M164.301 137.622H160.316' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M220.977 141.004V140.562' fill='#a3a3d1'/>
+<path clip-path='url(#clip1)' d='M220.977 141.004V140.562' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M218.984 140.563H222.969' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M220.977 141.004V141.441' fill='#a3a3d1'/>
+<path clip-path='url(#clip1)' d='M220.977 141.004V141.441' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M222.969 141.442H218.984' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M279.645 137.933V137.496' fill='#a3a3d1'/>
+<path clip-path='url(#clip1)' d='M279.645 137.933V137.496' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M277.652 137.496H281.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M279.645 137.933V138.371' fill='#a3a3d1'/>
+<path clip-path='url(#clip1)' d='M279.645 137.933V138.371' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M281.64 138.371H277.652' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M338.317 132.234V131.859' fill='#a3a3d1'/>
+<path clip-path='url(#clip1)' d='M338.317 132.234V131.859' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M336.32 131.859H340.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M338.317 132.234V132.609' fill='#a3a3d1'/>
+<path clip-path='url(#clip1)' d='M338.317 132.234V132.609' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M340.308 132.61H336.324' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M396.984 78.691V78.691' fill='#a3a3d1'/>
+<path clip-path='url(#clip1)' d='M394.992 78.691H398.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M396.984 78.691V78.691' fill='#a3a3d1'/>
+<path clip-path='url(#clip1)' d='M394.992 78.691H398.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M107.25 203.937H110.488V130.48H107.25ZM165.918 203.937H169.156V133.238H165.918ZM224.59 203.937H227.824V141.254H224.59ZM283.258 203.937H286.496V137.308H283.258ZM341.926 203.937H345.164V135.742H341.926ZM400.594 203.937H403.832V78.691H400.594Z' fill='#8585c2'/>
+<path clip-path='url(#clip1)' d='M107.25 203.937H110.488V130.48H107.25ZM165.918 203.937H169.156V133.238H165.918ZM224.59 203.937H227.824V141.254H224.59ZM283.258 203.937H286.496V137.308H283.258ZM341.926 203.937H345.164V135.742H341.926ZM400.594 203.937H403.832V78.691H400.594Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M108.871 130.48V130.168' fill='#8585c2'/>
+<path clip-path='url(#clip1)' d='M108.871 130.48V130.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M106.875 130.168H110.86' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M108.871 130.48V130.793' fill='#8585c2'/>
+<path clip-path='url(#clip1)' d='M108.871 130.48V130.793' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M110.864 130.793H106.879' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M167.539 133.238V133.238' fill='#8585c2'/>
+<path clip-path='url(#clip1)' d='M165.547 133.238H169.532' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M167.539 133.238V133.238' fill='#8585c2'/>
+<path clip-path='url(#clip1)' d='M165.547 133.238H169.532' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M226.207 141.254V140.75' fill='#8585c2'/>
+<path clip-path='url(#clip1)' d='M226.207 141.254V140.75' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M224.215 140.75H228.2' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M226.207 141.254V141.754' fill='#8585c2'/>
+<path clip-path='url(#clip1)' d='M226.207 141.254V141.754' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M228.2 141.754H224.215' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M284.875 137.308V136.933' fill='#8585c2'/>
+<path clip-path='url(#clip1)' d='M284.875 137.308V136.933' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M282.883 136.934H286.868' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M284.875 137.308V137.683' fill='#8585c2'/>
+<path clip-path='url(#clip1)' d='M284.875 137.308V137.683' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M286.868 137.683H282.883' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M343.547 135.742V135.617' fill='#8585c2'/>
+<path clip-path='url(#clip1)' d='M343.547 135.742V135.617' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M341.551 135.618H345.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M343.547 135.742V135.867' fill='#8585c2'/>
+<path clip-path='url(#clip1)' d='M343.547 135.742V135.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M345.539 135.867H341.555' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M402.215 78.691V78.691' fill='#8585c2'/>
+<path clip-path='url(#clip1)' d='M400.223 78.691H404.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M402.215 78.691V78.691' fill='#8585c2'/>
+<path clip-path='url(#clip1)' d='M400.223 78.691H404.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M112.481 203.937H115.719V137.871H112.481ZM171.149 203.937H174.387V133.238H171.149ZM229.82 203.937H233.055V141.254H229.82ZM288.488 203.937H291.727V132.797H288.488ZM347.156 203.937H350.395V136.554H347.156ZM405.824 203.937H409.063V103.742H405.824Z' fill='#6666b3'/>
+<path clip-path='url(#clip1)' d='M112.481 203.937H115.719V137.871H112.481ZM171.149 203.937H174.387V133.238H171.149ZM229.82 203.937H233.055V141.254H229.82ZM288.488 203.937H291.727V132.797H288.488ZM347.156 203.937H350.395V136.554H347.156ZM405.824 203.937H409.063V103.742H405.824Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M114.098 137.871V137.683' fill='#6666b3'/>
+<path clip-path='url(#clip1)' d='M114.098 137.871V137.683' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M112.105 137.683H116.09' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M114.098 137.871V138.058' fill='#6666b3'/>
+<path clip-path='url(#clip1)' d='M114.098 137.871V138.058' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M116.094 138.059H112.109' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M172.77 133.238V133.238' fill='#6666b3'/>
+<path clip-path='url(#clip1)' d='M170.777 133.238H174.762' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M172.77 133.238V133.238' fill='#6666b3'/>
+<path clip-path='url(#clip1)' d='M170.777 133.238H174.762' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M231.438 141.254V141.129' fill='#6666b3'/>
+<path clip-path='url(#clip1)' d='M231.438 141.254V141.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M229.445 141.128H233.43' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M231.438 141.254V141.379' fill='#6666b3'/>
+<path clip-path='url(#clip1)' d='M231.438 141.254V141.379' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M233.43 141.379H229.445' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M290.106 132.797V132.547' fill='#6666b3'/>
+<path clip-path='url(#clip1)' d='M290.106 132.797V132.547' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M288.113 132.547H292.098' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M290.106 132.797V133.051' fill='#6666b3'/>
+<path clip-path='url(#clip1)' d='M290.106 132.797V133.051' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M292.098 133.051H288.113' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M348.777 136.554V134.992' fill='#6666b3'/>
+<path clip-path='url(#clip1)' d='M348.777 136.554V134.992' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M346.781 134.992H350.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M348.777 136.554V138.121' fill='#6666b3'/>
+<path clip-path='url(#clip1)' d='M348.777 136.554V138.121' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M350.769 138.122H346.785' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M407.445 103.742V102.363' fill='#6666b3'/>
+<path clip-path='url(#clip1)' d='M407.445 103.742V102.363' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M405.453 102.364H409.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M407.445 103.742V105.121' fill='#6666b3'/>
+<path clip-path='url(#clip1)' d='M407.445 103.742V105.121' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M409.437 105.121H405.453' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M117.711 203.937H120.949V135.867H117.711ZM176.379 203.937H179.617V134.238H176.379ZM235.051 203.937H238.285V140.941H235.051ZM293.719 203.937H296.957V132.734H293.719ZM352.387 203.937H355.625V116.203H352.387ZM411.055 203.937H414.293V84.39H411.055Z' fill='#4747a4'/>
+<path clip-path='url(#clip1)' d='M117.711 203.937H120.949V135.867H117.711ZM176.379 203.937H179.617V134.238H176.379ZM235.051 203.937H238.285V140.941H235.051ZM293.719 203.937H296.957V132.734H293.719ZM352.387 203.937H355.625V116.203H352.387ZM411.055 203.937H414.293V84.39H411.055Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M119.328 135.867V135.554' fill='#4747a4'/>
+<path clip-path='url(#clip1)' d='M119.328 135.867V135.554' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M117.336 135.555H121.321' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M119.328 135.867V136.179' fill='#4747a4'/>
+<path clip-path='url(#clip1)' d='M119.328 135.867V136.179' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M121.325 136.179H117.34' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M178 134.238V133.238' fill='#4747a4'/>
+<path clip-path='url(#clip1)' d='M178 134.238V133.238' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M176.008 133.238H179.993' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M178 134.238V135.242' fill='#4747a4'/>
+<path clip-path='url(#clip1)' d='M178 134.238V135.242' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M179.993 135.242H176.008' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M236.668 140.941V140.687' fill='#4747a4'/>
+<path clip-path='url(#clip1)' d='M236.668 140.941V140.687' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M234.676 140.687H238.661' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M236.668 140.941V141.191' fill='#4747a4'/>
+<path clip-path='url(#clip1)' d='M236.668 140.941V141.191' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M238.66 141.191H234.675' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M295.336 132.734V132.609' fill='#4747a4'/>
+<path clip-path='url(#clip1)' d='M295.336 132.734V132.609' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M293.344 132.61H297.329' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M295.336 132.734V132.859' fill='#4747a4'/>
+<path clip-path='url(#clip1)' d='M295.336 132.734V132.859' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M297.328 132.859H293.343' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M354.008 116.203V115.39' fill='#4747a4'/>
+<path clip-path='url(#clip1)' d='M354.008 116.203V115.39' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M352.012 115.39H356' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M354.008 116.203V117.015' fill='#4747a4'/>
+<path clip-path='url(#clip1)' d='M354.008 116.203V117.015' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M355.999 117.016H352.015' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M412.676 84.39V83.89' fill='#4747a4'/>
+<path clip-path='url(#clip1)' d='M412.676 84.39V83.89' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M410.684 83.891H414.668' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M412.676 84.39V84.89' fill='#4747a4'/>
+<path clip-path='url(#clip1)' d='M412.676 84.39V84.89' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M414.667 84.891H410.683' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M122.941 203.937H126.18V138.183H122.941ZM181.609 203.937H184.848V133.238H181.609ZM240.281 203.937H243.516V140.312H240.281ZM298.949 203.937H302.188V132.422H298.949ZM357.617 203.937H360.856V135.179H357.617ZM416.285 203.937H419.524V102.675H416.285Z' fill='#292994'/>
+<path clip-path='url(#clip1)' d='M122.941 203.937H126.18V138.183H122.941ZM181.609 203.937H184.848V133.238H181.609ZM240.281 203.937H243.516V140.312H240.281ZM298.949 203.937H302.188V132.422H298.949ZM357.617 203.937H360.856V135.179H357.617ZM416.285 203.937H419.524V102.675H416.285Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M124.559 138.183V137.996' fill='#292994'/>
+<path clip-path='url(#clip1)' d='M124.559 138.183V137.996' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M122.566 137.996H126.551' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M124.559 138.183V138.371' fill='#292994'/>
+<path clip-path='url(#clip1)' d='M124.559 138.183V138.371' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M126.555 138.371H122.57' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M183.231 133.238V133.238' fill='#292994'/>
+<path clip-path='url(#clip1)' d='M181.238 133.238H185.223' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M183.231 133.238V133.238' fill='#292994'/>
+<path clip-path='url(#clip1)' d='M181.238 133.238H185.223' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M241.899 140.312V138.871' fill='#292994'/>
+<path clip-path='url(#clip1)' d='M241.899 140.312V138.871' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M239.906 138.871H243.891' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M241.899 140.312V141.754' fill='#292994'/>
+<path clip-path='url(#clip1)' d='M241.899 140.312V141.754' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M243.891 141.754H239.906' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M300.567 132.422V132.109' fill='#292994'/>
+<path clip-path='url(#clip1)' d='M300.567 132.422V132.109' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M298.574 132.11H302.559' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M300.567 132.422V132.734' fill='#292994'/>
+<path clip-path='url(#clip1)' d='M300.567 132.422V132.734' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M302.559 132.734H298.574' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M359.238 135.179V134.801' fill='#292994'/>
+<path clip-path='url(#clip1)' d='M359.238 135.179V134.801' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M357.242 134.8H361.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M359.238 135.179V135.554' fill='#292994'/>
+<path clip-path='url(#clip1)' d='M359.238 135.179V135.554' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M361.23 135.555H357.246' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M417.906 102.675V102.55' fill='#292994'/>
+<path clip-path='url(#clip1)' d='M417.906 102.675V102.55' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M415.914 102.551H419.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M417.906 102.675V102.8' fill='#292994'/>
+<path clip-path='url(#clip1)' d='M417.906 102.675V102.8' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M419.898 102.801H415.914' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M128.172 203.937H131.41V123.969H128.172ZM186.84 203.937H190.078V127.851H186.84ZM245.512 203.937H248.746V141.504H245.512ZM304.18 203.937H307.418V137.746H304.18ZM362.848 203.937H366.086V104.492H362.848ZM421.516 203.937H424.754V78.691H421.516Z' fill='#0a0a85'/>
+<path clip-path='url(#clip1)' d='M128.172 203.937H131.41V123.969H128.172ZM186.84 203.937H190.078V127.851H186.84ZM245.512 203.937H248.746V141.504H245.512ZM304.18 203.937H307.418V137.746H304.18ZM362.848 203.937H366.086V104.492H362.848ZM421.516 203.937H424.754V78.691H421.516Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M129.789 123.969V123.781' fill='#0a0a85'/>
+<path clip-path='url(#clip1)' d='M129.789 123.969V123.781' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M127.797 123.781H131.782' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M129.789 123.969V124.156' fill='#0a0a85'/>
+<path clip-path='url(#clip1)' d='M129.789 123.969V124.156' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M131.785 124.157H127.8' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M188.461 127.851V126.91' fill='#0a0a85'/>
+<path clip-path='url(#clip1)' d='M188.461 127.851V126.91' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M186.469 126.91H190.454' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M188.461 127.851V128.789' fill='#0a0a85'/>
+<path clip-path='url(#clip1)' d='M188.461 127.851V128.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M190.453 128.789H186.468' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M247.129 141.504V141.254' fill='#0a0a85'/>
+<path clip-path='url(#clip1)' d='M247.129 141.504V141.254' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M245.137 141.254H249.122' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M247.129 141.504V141.754' fill='#0a0a85'/>
+<path clip-path='url(#clip1)' d='M247.129 141.504V141.754' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M249.121 141.754H245.136' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M305.797 137.746V137.558' fill='#0a0a85'/>
+<path clip-path='url(#clip1)' d='M305.797 137.746V137.558' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M303.804 137.559H307.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M305.797 137.746V137.933' fill='#0a0a85'/>
+<path clip-path='url(#clip1)' d='M305.797 137.746V137.933' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M307.789 137.934H303.804' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M364.469 104.492V103.742' fill='#0a0a85'/>
+<path clip-path='url(#clip1)' d='M364.469 104.492V103.742' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M362.473 103.742H366.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M364.469 104.492V105.246' fill='#0a0a85'/>
+<path clip-path='url(#clip1)' d='M364.469 104.492V105.246' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M366.46 105.246H362.476' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M423.137 78.691V78.691' fill='#0a0a85'/>
+<path clip-path='url(#clip1)' d='M421.144 78.691H425.128' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip1)' d='M423.137 78.691V78.691' fill='#0a0a85'/>
+<path clip-path='url(#clip1)' d='M421.144 78.691H425.128' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(0 -1 1 0 -89.137 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -30.468 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 28.201 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 86.87 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 145.539 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 204.208 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -83.906 247.398)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -25.237 247.774)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 33.432 249.464)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 92.101 244.642)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 150.77 245.081)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-55' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 209.439 187.155)'>
+<use x='109.598' xlink:href='#g3-1' y='183.949'/>
+<use x='113.103' xlink:href='#g3-1' y='183.949'/>
+<use x='116.608' xlink:href='#g3-1' y='183.949'/>
+<use x='120.114' xlink:href='#g1-50' y='183.949'/>
+<use x='122.76' xlink:href='#g1-46' y='183.949'/>
+<use x='124.23' xlink:href='#g1-56' y='183.949'/>
+<use x='126.877' xlink:href='#g1-52' y='183.949'/>
+<use x='129.523' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -78.676 246.897)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -20.007 245.206)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-55' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 38.662 249.464)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 97.331 246.396)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 156 240.697)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 214.669 187.155)'>
+<use x='109.598' xlink:href='#g3-1' y='183.949'/>
+<use x='113.103' xlink:href='#g3-1' y='183.949'/>
+<use x='116.608' xlink:href='#g3-1' y='183.949'/>
+<use x='120.114' xlink:href='#g1-51' y='183.949'/>
+<use x='122.76' xlink:href='#g1-46' y='183.949'/>
+<use x='124.23' xlink:href='#g1-49' y='183.949'/>
+<use x='126.877' xlink:href='#g1-52' y='183.949'/>
+<use x='129.523' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -73.445 238.944)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-55' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -14.776 241.699)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 43.893 249.715)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 102.562 245.77)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 161.231 244.204)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-57' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 219.9 187.155)'>
+<use x='109.598' xlink:href='#g3-1' y='183.949'/>
+<use x='113.103' xlink:href='#g3-1' y='183.949'/>
+<use x='116.608' xlink:href='#g3-1' y='183.949'/>
+<use x='120.114' xlink:href='#g1-50' y='183.949'/>
+<use x='122.76' xlink:href='#g1-46' y='183.949'/>
+<use x='124.23' xlink:href='#g1-48' y='183.949'/>
+<use x='126.877' xlink:href='#g1-55' y='183.949'/>
+<use x='129.523' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -68.215 246.333)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -9.546 241.699)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 49.123 249.715)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 107.792 241.261)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 166.461 245.018)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 225.13 212.204)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-54' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -62.984 244.329)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-57' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -4.315 242.701)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 54.354 249.402)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 113.023 241.198)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 171.692 224.666)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-52' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 230.361 192.853)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-57' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -57.754 246.646)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 0.915 241.699)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 59.584 248.776)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 118.253 240.885)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 176.922 243.64)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 235.591 211.139)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-54' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -52.524 232.431)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 6.145 236.314)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 64.814 249.965)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 123.483 246.208)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 182.152 212.955)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-57' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 240.821 187.155)'>
+<use x='109.598' xlink:href='#g3-1' y='183.949'/>
+<use x='113.103' xlink:href='#g3-1' y='183.949'/>
+<use x='116.608' xlink:href='#g3-1' y='183.949'/>
+<use x='120.114' xlink:href='#g1-50' y='183.949'/>
+<use x='122.76' xlink:href='#g1-46' y='183.949'/>
+<use x='124.23' xlink:href='#g1-49' y='183.949'/>
+<use x='126.877' xlink:href='#g1-49' y='183.949'/>
+<use x='129.523' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -124.589 311.062)'>
+<use x='114.487' xlink:href='#g0-82' y='183.949'/>
+<use x='120.457' xlink:href='#g0-101' y='183.949'/>
+<use x='124.553' xlink:href='#g0-108' y='183.949'/>
+<use x='126.753' xlink:href='#g0-97' y='183.949'/>
+<use x='131.181' xlink:href='#g0-116' y='183.949'/>
+<use x='134.509' xlink:href='#g0-105' y='183.949'/>
+<use x='136.709' xlink:href='#g0-118' y='183.949'/>
+<use x='140.957' xlink:href='#g0-101' y='183.949'/>
+<use x='148.124' xlink:href='#g0-116' y='183.949'/>
+<use x='151.452' xlink:href='#g0-105' y='183.949'/>
+<use x='153.652' xlink:href='#g0-109' y='183.949'/>
+<use x='160.972' xlink:href='#g0-101' y='183.949'/>
+<use x='168.139' xlink:href='#g2-40' y='183.949'/>
+<use x='171.432' xlink:href='#g2-108' y='183.949'/>
+<use x='173.453' xlink:href='#g2-111' y='183.949'/>
+<use x='177.452' xlink:href='#g2-119' y='183.949'/>
+<use x='183' xlink:href='#g2-101' y='183.949'/>
+<use x='186.764' xlink:href='#g2-114' y='183.949'/>
+<use x='192.479' xlink:href='#g2-105' y='183.949'/>
+<use x='194.499' xlink:href='#g2-115' y='183.949'/>
+<use x='200.568' xlink:href='#g2-98' y='183.949'/>
+<use x='205.176' xlink:href='#g2-101' y='183.949'/>
+<use x='208.94' xlink:href='#g2-116' y='183.949'/>
+<use x='211.998' xlink:href='#g2-116' y='183.949'/>
+<use x='215.056' xlink:href='#g2-101' y='183.949'/>
+<use x='218.819' xlink:href='#g2-114' y='183.949'/>
+<use x='221.711' xlink:href='#g2-41' y='183.949'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='165.084pt' version='1.1' viewBox='52.938 54.996 381.624 165.084' width='381.624pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip5'>
+<path d='M82.148 203.937H434.164V78.691H82.148Z'/>
+</clipPath>
+<use id='g3-40' transform='scale(1.143)' xlink:href='#g0-40'/>
+<use id='g3-41' transform='scale(1.143)' xlink:href='#g0-41'/>
+<use id='g3-78' transform='scale(1.143)' xlink:href='#g0-78'/>
+<use id='g3-97' transform='scale(1.143)' xlink:href='#g0-97'/>
+<use id='g3-98' transform='scale(1.143)' xlink:href='#g0-98'/>
+<use id='g3-99' transform='scale(1.143)' xlink:href='#g0-99'/>
+<use id='g3-100' transform='scale(1.143)' xlink:href='#g0-100'/>
+<use id='g3-101' transform='scale(1.143)' xlink:href='#g0-101'/>
+<use id='g3-102' transform='scale(1.143)' xlink:href='#g0-102'/>
+<use id='g3-105' transform='scale(1.143)' xlink:href='#g0-105'/>
+<use id='g3-108' transform='scale(1.143)' xlink:href='#g0-108'/>
+<use id='g3-109' transform='scale(1.143)' xlink:href='#g0-109'/>
+<use id='g3-110' transform='scale(1.143)' xlink:href='#g0-110'/>
+<use id='g3-111' transform='scale(1.143)' xlink:href='#g0-111'/>
+<use id='g3-112' transform='scale(1.143)' xlink:href='#g0-112'/>
+<use id='g3-114' transform='scale(1.143)' xlink:href='#g0-114'/>
+<use id='g3-115' transform='scale(1.143)' xlink:href='#g0-115'/>
+<use id='g3-116' transform='scale(1.143)' xlink:href='#g0-116'/>
+<use id='g3-119' transform='scale(1.143)' xlink:href='#g0-119'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g1-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g1-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g1-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g1-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g1-108'/>
+<path d='M6.581 -2.663C6.581 -3.327 6.402 -4.08 5.317 -4.08C4.564 -4.08 4.142 -3.622 3.927 -3.344C3.865 -3.524 3.676 -4.08 2.762 -4.08C2.053 -4.08 1.623 -3.667 1.417 -3.398V-4.035H0.726V0H1.47V-2.188C1.47 -2.78 1.704 -3.497 2.385 -3.497C3.282 -3.497 3.282 -2.86 3.282 -2.6V0H4.026V-2.188C4.026 -2.78 4.259 -3.497 4.94 -3.497C5.837 -3.497 5.837 -2.86 5.837 -2.6V0H6.581V-2.663Z' id='g1-109'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g1-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g1-118'/>
+<use id='g2-46' transform='scale(0.714)' xlink:href='#g0-46'/>
+<use id='g2-48' transform='scale(0.714)' xlink:href='#g0-48'/>
+<use id='g2-49' transform='scale(0.714)' xlink:href='#g0-49'/>
+<use id='g2-50' transform='scale(0.714)' xlink:href='#g0-50'/>
+<use id='g2-51' transform='scale(0.714)' xlink:href='#g0-51'/>
+<use id='g2-52' transform='scale(0.714)' xlink:href='#g0-52'/>
+<use id='g2-53' transform='scale(0.714)' xlink:href='#g0-53'/>
+<use id='g2-54' transform='scale(0.714)' xlink:href='#g0-54'/>
+<use id='g2-55' transform='scale(0.714)' xlink:href='#g0-55'/>
+<use id='g2-56' transform='scale(0.714)' xlink:href='#g0-56'/>
+<use id='g2-57' transform='scale(0.714)' xlink:href='#g0-57'/>
+<use id='g2-120' transform='scale(0.714)' xlink:href='#g0-120'/>
+<path d='M1.445 -1.245C1.445 -1.41 1.305 -1.549 1.141 -1.549S0.837 -1.41 0.837 -1.245S0.976 -0.941 1.141 -0.941S1.445 -1.081 1.445 -1.245Z' id='g4-1'/>
+<path d='M2.127 -5.23C2.008 -5.23 1.995 -5.23 1.911 -5.154C1.032 -4.387 0.586 -3.145 0.586 -1.743C0.586 -0.425 0.983 0.844 1.904 1.653C1.995 1.743 2.008 1.743 2.127 1.743H2.462C2.441 1.73 1.764 1.151 1.444 0.063C1.276 -0.481 1.193 -1.053 1.193 -1.743C1.193 -4.156 2.322 -5.112 2.462 -5.23H2.127Z' id='g0-40'/>
+<path d='M0.746 1.743C0.865 1.743 0.879 1.743 0.962 1.667C1.841 0.9 2.287 -0.342 2.287 -1.743C2.287 -3.062 1.89 -4.331 0.969 -5.14C0.879 -5.23 0.865 -5.23 0.746 -5.23H0.411C0.432 -5.216 1.109 -4.638 1.43 -3.55C1.597 -3.006 1.681 -2.434 1.681 -1.743C1.681 0.669 0.551 1.625 0.411 1.743H0.746Z' id='g0-41'/>
+<path d='M1.339 -0.628H0.711V0H1.339V-0.628Z' id='g0-46'/>
+<path d='M3.403 -2.267C3.403 -2.601 3.403 -3.417 3.075 -3.989C2.72 -4.617 2.183 -4.721 1.848 -4.721C1.534 -4.721 0.99 -4.624 0.642 -4.024C0.307 -3.466 0.293 -2.706 0.293 -2.267C0.293 -1.75 0.321 -1.116 0.614 -0.586C0.921 -0.021 1.437 0.146 1.848 0.146C2.545 0.146 2.929 -0.258 3.138 -0.697C3.382 -1.193 3.403 -1.834 3.403 -2.267ZM1.848 -0.314C1.555 -0.314 1.22 -0.481 1.046 -0.983C0.907 -1.409 0.9 -1.848 0.9 -2.357C0.9 -2.999 0.9 -4.261 1.848 -4.261S2.797 -2.999 2.797 -2.357C2.797 -1.897 2.797 -1.374 2.629 -0.928C2.434 -0.425 2.078 -0.314 1.848 -0.314Z' id='g0-48'/>
+<path d='M2.239 -4.721H2.085C1.632 -4.303 1.06 -4.275 0.642 -4.261V-3.822C0.914 -3.829 1.262 -3.843 1.611 -3.982V-0.439H0.683V0H3.166V-0.439H2.239V-4.721Z' id='g0-49'/>
+<path d='M1.974 -0.537C1.89 -0.537 1.806 -0.53 1.723 -0.53H0.928L2.008 -1.485C2.134 -1.597 2.476 -1.855 2.608 -1.967C2.915 -2.246 3.327 -2.608 3.327 -3.215C3.327 -4.003 2.741 -4.721 1.743 -4.721C1.004 -4.721 0.544 -4.324 0.307 -3.612L0.635 -3.201C0.795 -3.787 1.039 -4.24 1.646 -4.24C2.232 -4.24 2.678 -3.829 2.678 -3.201C2.678 -2.622 2.336 -2.294 1.918 -1.897C1.778 -1.757 1.402 -1.444 1.255 -1.304C1.053 -1.123 0.572 -0.656 0.37 -0.481V0H3.327V-0.537H1.974Z' id='g0-50'/>
+<path d='M0.697 -3.578C0.983 -4.135 1.485 -4.289 1.82 -4.289C2.232 -4.289 2.538 -4.052 2.538 -3.654C2.538 -3.285 2.287 -2.831 1.757 -2.741C1.723 -2.734 1.695 -2.734 1.234 -2.699V-2.239H1.778C2.441 -2.239 2.685 -1.716 2.685 -1.276C2.685 -0.732 2.35 -0.314 1.806 -0.314C1.311 -0.314 0.746 -0.551 0.398 -0.997L0.307 -0.544C0.711 -0.091 1.276 0.146 1.82 0.146C2.734 0.146 3.389 -0.537 3.389 -1.269C3.389 -1.841 2.929 -2.301 2.378 -2.462C2.908 -2.734 3.18 -3.201 3.18 -3.654C3.18 -4.247 2.573 -4.721 1.827 -4.721C1.213 -4.721 0.704 -4.4 0.411 -3.982L0.697 -3.578Z' id='g0-51'/>
+<path d='M2.762 -1.165H3.487V-1.625H2.762V-4.575H2.071L0.209 -1.625V-1.165H2.162V0H2.762V-1.165ZM0.802 -1.625C1.011 -1.953 2.211 -3.815 2.211 -4.233V-1.625H0.802Z' id='g0-52'/>
+<path d='M1.144 -4.094H3.075V-4.575H0.586V-1.967H1.095C1.262 -2.343 1.59 -2.511 1.904 -2.511C2.19 -2.511 2.622 -2.315 2.622 -1.43C2.622 -0.516 2.043 -0.314 1.688 -0.314C1.227 -0.314 0.781 -0.558 0.544 -0.955L0.279 -0.537C0.621 -0.112 1.137 0.146 1.688 0.146C2.608 0.146 3.327 -0.565 3.327 -1.416C3.327 -2.28 2.685 -2.971 1.918 -2.971C1.618 -2.971 1.353 -2.866 1.144 -2.692V-4.094Z' id='g0-53'/>
+<path d='M3.062 -4.582C2.685 -4.721 2.42 -4.721 2.287 -4.721C1.227 -4.721 0.307 -3.724 0.307 -2.253C0.307 -0.363 1.158 0.146 1.862 0.146C2.427 0.146 2.72 -0.119 2.936 -0.342C3.382 -0.816 3.389 -1.311 3.389 -1.555C3.389 -2.469 2.894 -3.229 2.218 -3.229C1.534 -3.229 1.165 -2.873 0.962 -2.671C1.053 -3.626 1.541 -4.289 2.294 -4.289C2.434 -4.289 2.713 -4.275 3.062 -4.142V-4.582ZM0.969 -1.534C0.969 -1.576 0.969 -1.681 0.976 -1.716C0.976 -2.19 1.276 -2.769 1.897 -2.769C2.748 -2.769 2.748 -1.792 2.748 -1.555C2.748 -1.29 2.748 -0.997 2.559 -0.704C2.392 -0.453 2.183 -0.314 1.862 -0.314C1.123 -0.314 1.004 -1.227 0.969 -1.534Z' id='g0-54'/>
+<path d='M1.723 -4.038C1.806 -4.038 1.89 -4.045 1.974 -4.045H2.852C1.792 -3.006 1.116 -1.548 1.116 0.07H1.771C1.771 -1.967 2.762 -3.431 3.389 -4.087V-4.575H0.307V-4.038H1.723Z' id='g0-55'/>
+<path d='M2.385 -2.469C2.845 -2.615 3.285 -2.985 3.285 -3.501C3.285 -4.135 2.678 -4.721 1.848 -4.721S0.411 -4.135 0.411 -3.501C0.411 -2.978 0.865 -2.608 1.311 -2.469C0.697 -2.28 0.307 -1.806 0.307 -1.269C0.307 -0.523 0.969 0.146 1.848 0.146S3.389 -0.523 3.389 -1.269C3.389 -1.806 2.992 -2.28 2.385 -2.469ZM1.848 -2.699C1.353 -2.699 0.948 -2.985 0.948 -3.494C0.948 -3.94 1.262 -4.289 1.848 -4.289C2.427 -4.289 2.748 -3.94 2.748 -3.494C2.748 -2.999 2.357 -2.699 1.848 -2.699ZM1.848 -0.314C1.367 -0.314 0.941 -0.621 0.941 -1.276C0.941 -1.904 1.346 -2.239 1.848 -2.239S2.755 -1.897 2.755 -1.276C2.755 -0.621 2.322 -0.314 1.848 -0.314Z' id='g0-56'/>
+<path d='M0.537 -0.174C0.879 0.077 1.193 0.146 1.52 0.146C2.497 0.146 3.389 -0.837 3.389 -2.336C3.389 -4.24 2.545 -4.721 1.876 -4.721C1.255 -4.721 0.969 -4.428 0.767 -4.226C0.321 -3.773 0.307 -3.292 0.307 -3.02C0.307 -2.12 0.795 -1.346 1.478 -1.346C2.267 -1.346 2.699 -1.869 2.734 -1.911C2.636 -0.802 2.092 -0.314 1.52 -0.314C1.158 -0.314 0.934 -0.446 0.774 -0.579L0.537 -0.174ZM2.713 -3.027C2.72 -2.985 2.72 -2.915 2.72 -2.873C2.72 -2.357 2.406 -1.806 1.799 -1.806C1.534 -1.806 1.325 -1.883 1.144 -2.169C0.962 -2.441 0.948 -2.706 0.948 -3.02C0.948 -3.292 0.948 -3.605 1.165 -3.912C1.311 -4.122 1.52 -4.289 1.869 -4.289C2.545 -4.289 2.692 -3.473 2.713 -3.027Z' id='g0-57'/>
+<path d='M1.646 -4.84H0.697V0H1.283V-4.289H1.29L3.578 0H4.526V-4.84H3.94V-0.551H3.933L1.646 -4.84Z' id='g0-78'/>
+<path d='M2.971 -2.008C2.971 -2.72 2.427 -3.201 1.736 -3.201C1.297 -3.201 0.962 -3.11 0.572 -2.901L0.614 -2.392C0.844 -2.545 1.186 -2.755 1.736 -2.755C2.043 -2.755 2.364 -2.525 2.364 -2.001V-1.723C1.332 -1.688 0.314 -1.471 0.314 -0.823C0.314 -0.474 0.551 0.07 1.165 0.07C1.465 0.07 2.015 0.007 2.385 -0.265V0H2.971V-2.008ZM2.364 -0.99C2.364 -0.851 2.364 -0.669 2.12 -0.523C1.897 -0.398 1.625 -0.391 1.548 -0.391C1.165 -0.391 0.872 -0.565 0.872 -0.83C0.872 -1.276 2.05 -1.318 2.364 -1.332V-0.99Z' id='g0-97'/>
+<path d='M1.179 -4.84H0.593V0H1.2V-0.328C1.353 -0.195 1.688 0.07 2.197 0.07C2.957 0.07 3.571 -0.642 3.571 -1.555C3.571 -2.399 3.089 -3.166 2.392 -3.166C1.953 -3.166 1.527 -3.027 1.179 -2.769V-4.84ZM1.2 -2.197C1.2 -2.308 1.2 -2.392 1.444 -2.552C1.548 -2.615 1.736 -2.706 1.974 -2.706C2.441 -2.706 2.964 -2.392 2.964 -1.555C2.964 -0.704 2.385 -0.391 1.897 -0.391C1.639 -0.391 1.395 -0.509 1.2 -0.823V-2.197Z' id='g0-98'/>
+<path d='M3.034 -0.76C2.685 -0.537 2.308 -0.411 1.876 -0.411C1.234 -0.411 0.858 -0.928 0.858 -1.555C0.858 -2.092 1.137 -2.72 1.897 -2.72C2.371 -2.72 2.594 -2.622 2.95 -2.399L3.041 -2.901C2.622 -3.11 2.441 -3.201 1.897 -3.201C0.851 -3.201 0.251 -2.357 0.251 -1.548C0.251 -0.697 0.921 0.07 1.869 0.07C2.357 0.07 2.776 -0.077 3.075 -0.251L3.034 -0.76Z' id='g0-99'/>
+<path d='M3.229 -4.84H2.643V-2.797C2.197 -3.124 1.743 -3.166 1.541 -3.166C0.809 -3.166 0.251 -2.434 0.251 -1.548S0.802 0.07 1.52 0.07C1.953 0.07 2.357 -0.126 2.622 -0.363V0H3.229V-4.84ZM2.622 -0.865C2.448 -0.579 2.183 -0.391 1.848 -0.391C1.36 -0.391 0.858 -0.732 0.858 -1.541C0.858 -2.413 1.451 -2.706 1.925 -2.706C2.204 -2.706 2.441 -2.587 2.622 -2.35V-0.865Z' id='g0-100'/>
+<path d='M2.999 -0.76C2.608 -0.481 2.169 -0.391 1.869 -0.391C1.262 -0.391 0.802 -0.886 0.781 -1.527H3.068C3.068 -1.848 3.034 -2.315 2.762 -2.713C2.511 -3.068 2.092 -3.201 1.75 -3.201C0.9 -3.201 0.244 -2.455 0.244 -1.569C0.244 -0.676 0.941 0.07 1.862 0.07C2.267 0.07 2.685 -0.049 3.041 -0.265L2.999 -0.76ZM0.83 -1.946C0.99 -2.504 1.402 -2.741 1.75 -2.741C2.057 -2.741 2.511 -2.594 2.643 -1.946H0.83Z' id='g0-101'/>
+<path d='M1.325 -2.657H2.12V-3.096H1.304V-3.898C1.304 -4.38 1.743 -4.449 1.974 -4.449C2.12 -4.449 2.308 -4.428 2.566 -4.331V-4.84C2.385 -4.882 2.169 -4.91 1.981 -4.91C1.262 -4.91 0.739 -4.394 0.739 -3.703V-3.096H0.202V-2.657H0.739V0H1.325V-2.657Z' id='g0-102'/>
+<path d='M1.227 -4.784H0.523V-4.08H1.227V-4.784ZM1.172 -3.096H0.586V0H1.172V-3.096Z' id='g0-105'/>
+<path d='M1.172 -4.84H0.586V0H1.172V-4.84Z' id='g0-108'/>
+<path d='M5.3 -2.064C5.3 -2.608 5.14 -3.166 4.282 -3.166C3.696 -3.166 3.333 -2.824 3.166 -2.601C3.096 -2.79 2.922 -3.166 2.225 -3.166C1.827 -3.166 1.444 -3.006 1.137 -2.636V-3.145H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-1.695C3.243 -2.155 3.438 -2.706 3.975 -2.706C4.693 -2.706 4.693 -2.218 4.693 -2.015V0H5.3V-2.064Z' id='g0-109'/>
+<path d='M3.243 -2.064C3.243 -2.608 3.082 -3.166 2.225 -3.166C1.827 -3.166 1.444 -3.006 1.137 -2.636V-3.145H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-2.064Z' id='g0-110'/>
+<path d='M3.487 -1.527C3.487 -2.448 2.755 -3.201 1.848 -3.201S0.209 -2.441 0.209 -1.527C0.209 -0.642 0.948 0.07 1.848 0.07C2.755 0.07 3.487 -0.642 3.487 -1.527ZM1.848 -0.411C1.297 -0.411 0.816 -0.816 0.816 -1.604S1.332 -2.741 1.848 -2.741C2.371 -2.741 2.88 -2.378 2.88 -1.604C2.88 -0.809 2.385 -0.411 1.848 -0.411Z' id='g0-111'/>
+<path d='M1.2 -0.328C1.569 0.007 1.967 0.07 2.204 0.07C2.943 0.07 3.571 -0.635 3.571 -1.555C3.571 -2.392 3.11 -3.166 2.42 -3.166C2.106 -3.166 1.583 -3.075 1.179 -2.762V-3.096H0.593V1.353H1.2V-0.328ZM1.2 -2.315C1.36 -2.511 1.632 -2.685 1.967 -2.685C2.525 -2.685 2.964 -2.169 2.964 -1.555C2.964 -0.865 2.441 -0.391 1.897 -0.391C1.792 -0.391 1.618 -0.404 1.437 -0.551C1.227 -0.711 1.2 -0.816 1.2 -0.948V-2.315Z' id='g0-112'/>
+<path d='M1.179 -1.485C1.179 -2.239 1.806 -2.643 2.42 -2.65V-3.166C1.834 -3.159 1.409 -2.873 1.13 -2.504V-3.145H0.593V0H1.179V-1.485Z' id='g0-114'/>
+<path d='M2.545 -2.985C2.071 -3.18 1.723 -3.201 1.471 -3.201C1.297 -3.201 0.244 -3.201 0.244 -2.273C0.244 -1.946 0.425 -1.764 0.516 -1.681C0.76 -1.437 1.053 -1.381 1.423 -1.311C1.75 -1.248 2.127 -1.179 2.127 -0.844C2.127 -0.404 1.548 -0.404 1.451 -0.404C1.004 -0.404 0.586 -0.565 0.307 -0.76L0.209 -0.237C0.446 -0.119 0.872 0.07 1.451 0.07C1.764 0.07 2.071 0.021 2.329 -0.167C2.587 -0.363 2.671 -0.669 2.671 -0.907C2.671 -1.032 2.657 -1.304 2.364 -1.569C2.106 -1.799 1.855 -1.848 1.52 -1.911C1.109 -1.988 0.788 -2.05 0.788 -2.357C0.788 -2.755 1.297 -2.755 1.402 -2.755C1.799 -2.755 2.106 -2.671 2.455 -2.49L2.545 -2.985Z' id='g0-115'/>
+<path d='M1.311 -2.657H2.343V-3.096H1.311V-3.982H0.774V-3.096H0.139V-2.657H0.753V-0.893C0.753 -0.425 0.872 0.07 1.374 0.07S2.26 -0.091 2.469 -0.188L2.35 -0.635C2.12 -0.467 1.876 -0.411 1.681 -0.411C1.388 -0.411 1.311 -0.697 1.311 -1.018V-2.657Z' id='g0-116'/>
+<path d='M4.951 -3.096H4.407C4.345 -2.901 3.954 -1.723 3.738 -0.997C3.682 -0.795 3.612 -0.572 3.592 -0.411H3.585C3.543 -0.697 3.299 -1.451 3.285 -1.499L2.769 -3.096H2.239C2.036 -2.497 1.513 -0.934 1.458 -0.425H1.451C1.395 -0.921 0.879 -2.462 0.767 -2.797C0.711 -2.964 0.711 -2.978 0.676 -3.096H0.105L1.123 0H1.709C1.716 -0.028 1.904 -0.579 2.148 -1.353C2.253 -1.695 2.462 -2.364 2.497 -2.671L2.504 -2.678C2.518 -2.532 2.559 -2.378 2.608 -2.204S2.706 -1.841 2.755 -1.681L3.292 0H3.933L4.951 -3.096Z' id='g0-119'/>
+<path d='M1.932 -1.597L3.285 -3.096H2.671L1.681 -1.953L0.669 -3.096H0.042L1.437 -1.597L0 0H0.621L1.681 -1.311L2.783 0H3.41L1.932 -1.597Z' id='g0-120'/>
+</defs>
+<g id='page5'>
+<path d='M140.82 212.793V203.937M199.488 212.793V203.937M258.156 212.793V203.937M316.824 212.793V203.937M375.496 212.793V203.937M140.82 69.836V78.691M199.488 69.836V78.691M258.156 69.836V78.691M316.824 69.836V78.691M375.496 69.836V78.691' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M111.484 208.191V203.937M170.152 208.191V203.937M228.824 208.191V203.937M287.492 208.191V203.937M346.16 208.191V203.937M404.828 208.191V203.937M111.484 74.441V78.691M170.152 74.441V78.691M228.824 74.441V78.691M287.492 74.441V78.691M346.16 74.441V78.691M404.828 74.441V78.691' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 203.937H86.402M82.148 172.625H86.402M82.148 141.316H86.402M82.148 110.004H86.402M82.148 78.691H86.402M434.164 203.937H429.91M434.164 172.625H429.91M434.164 141.316H429.91M434.164 110.004H429.91M434.164 78.691H429.91' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 203.937V78.691H434.164V203.937H82.148Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -11.54 34.954)'>
+<use x='114.487' xlink:href='#g3-99' y='183.381'/>
+<use x='118.25' xlink:href='#g3-102' y='183.381'/>
+<use x='120.838' xlink:href='#g3-114' y='183.381'/>
+<use x='123.73' xlink:href='#g3-97' y='183.381'/>
+<use x='127.798' xlink:href='#g3-99' y='183.381'/>
+</g>
+<g transform='matrix(1 0 0 1 45.565 34.954)'>
+<use x='114.487' xlink:href='#g3-108' y='183.381'/>
+<use x='116.507' xlink:href='#g3-101' y='183.381'/>
+<use x='120.271' xlink:href='#g3-97' y='183.381'/>
+<use x='124.339' xlink:href='#g3-110' y='183.381'/>
+<use x='128.711' xlink:href='#g3-78' y='183.381'/>
+</g>
+<g transform='matrix(1 0 0 1 106.188 34.954)'>
+<use x='114.487' xlink:href='#g3-114' y='183.381'/>
+<use x='117.379' xlink:href='#g3-101' y='183.381'/>
+<use x='121.142' xlink:href='#g3-100' y='183.381'/>
+<use x='125.515' xlink:href='#g3-105' y='183.381'/>
+<use x='127.535' xlink:href='#g3-115' y='183.381'/>
+</g>
+<g transform='matrix(1 0 0 1 159.716 34.954)'>
+<use x='114.487' xlink:href='#g3-108' y='183.381'/>
+<use x='116.507' xlink:href='#g3-97' y='183.381'/>
+<use x='120.34' xlink:href='#g3-114' y='183.381'/>
+<use x='123.232' xlink:href='#g3-115' y='183.381'/>
+<use x='126.478' xlink:href='#g3-111' y='183.381'/>
+<use x='130.712' xlink:href='#g3-110' y='183.381'/>
+<use x='135.085' xlink:href='#g3-78' y='183.381'/>
+</g>
+<g transform='matrix(1 0 0 1 215.596 34.954)'>
+<use x='114.487' xlink:href='#g3-109' y='183.381'/>
+<use x='121.211' xlink:href='#g3-115' y='183.381'/>
+<use x='124.458' xlink:href='#g3-116' y='183.381'/>
+<use x='127.516' xlink:href='#g3-114' y='183.381'/>
+<use x='130.408' xlink:href='#g3-101' y='183.381'/>
+<use x='134.171' xlink:href='#g3-115' y='183.381'/>
+<use x='137.418' xlink:href='#g3-115' y='183.381'/>
+<use x='140.664' xlink:href='#g3-78' y='183.381'/>
+</g>
+<g transform='matrix(1 0 0 1 277.158 34.954)'>
+<use x='114.487' xlink:href='#g3-114' y='183.381'/>
+<use x='117.379' xlink:href='#g3-112' y='183.381'/>
+<use x='121.751' xlink:href='#g3-116' y='183.381'/>
+<use x='124.809' xlink:href='#g3-101' y='183.381'/>
+<use x='128.573' xlink:href='#g3-115' y='183.381'/>
+<use x='131.819' xlink:href='#g3-116' y='183.381'/>
+<use x='134.877' xlink:href='#g3-78' y='183.381'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 22.192)'>
+<use x='114.487' xlink:href='#g2-48' y='183.381'/>
+<use x='117.133' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -9.12)'>
+<use x='114.487' xlink:href='#g2-48' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-53' y='183.381'/>
+<use x='121.25' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -40.431)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -71.743)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-53' y='183.381'/>
+<use x='121.25' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -103.054)'>
+<use x='114.487' xlink:href='#g2-50' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-120' y='183.381'/>
+</g>
+<path clip-path='url(#clip5)' d='M82.148 141.316H434.164' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M88.945 203.937H92.18V141.316H88.945ZM147.613 203.937H150.852V141.316H147.613ZM206.281 203.937H209.52V141.316H206.281ZM264.949 203.937H268.188V141.316H264.949ZM323.621 203.937H326.859V141.316H323.621ZM382.289 203.937H385.527V141.316H382.289Z' fill='#993333'/>
+<path clip-path='url(#clip5)' d='M88.945 203.937H92.18V141.316H88.945ZM147.613 203.937H150.852V141.316H147.613ZM206.281 203.937H209.52V141.316H206.281ZM264.949 203.937H268.188V141.316H264.949ZM323.621 203.937H326.859V141.316H323.621ZM382.289 203.937H385.527V141.316H382.289Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M90.563 141.316V141.129' fill='#993333'/>
+<path clip-path='url(#clip5)' d='M90.563 141.316V141.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M88.57 141.129H92.555' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M90.563 141.316V141.504' fill='#993333'/>
+<path clip-path='url(#clip5)' d='M90.563 141.316V141.504' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M92.555 141.504H88.57' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M149.231 141.316V140.937' fill='#993333'/>
+<path clip-path='url(#clip5)' d='M149.231 141.316V140.937' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M147.238 140.937H151.223' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M149.231 141.316V141.691' fill='#993333'/>
+<path clip-path='url(#clip5)' d='M149.231 141.316V141.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M151.223 141.692H147.238' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M207.902 141.316V139.562' fill='#993333'/>
+<path clip-path='url(#clip5)' d='M207.902 141.316V139.562' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M205.906 139.563H209.894' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M207.902 141.316V143.07' fill='#993333'/>
+<path clip-path='url(#clip5)' d='M207.902 141.316V143.07' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M209.895 143.071H205.91' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M266.57 141.316V140.25' fill='#993333'/>
+<path clip-path='url(#clip5)' d='M266.57 141.316V140.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M264.578 140.25H268.562' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M266.57 141.316V142.379' fill='#993333'/>
+<path clip-path='url(#clip5)' d='M266.57 141.316V142.379' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M268.562 142.379H264.578' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M325.238 141.316V139.312' fill='#993333'/>
+<path clip-path='url(#clip5)' d='M325.238 141.316V139.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M323.246 139.312H327.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M325.238 141.316V143.32' fill='#993333'/>
+<path clip-path='url(#clip5)' d='M325.238 141.316V143.32' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M327.231 143.32H323.246' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M383.906 141.316V139.875' fill='#993333'/>
+<path clip-path='url(#clip5)' d='M383.906 141.316V139.875' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M381.914 139.875H385.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M383.906 141.316V142.754' fill='#993333'/>
+<path clip-path='url(#clip5)' d='M383.906 141.316V142.754' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M385.902 142.753H381.914' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M94.176 203.937H97.41V140.875H94.176ZM152.844 203.937H156.082V135.617H152.844ZM211.512 203.937H214.75V136.805H211.512ZM270.18 203.937H273.418V78.691H270.18ZM328.852 203.937H332.09V139.625H328.852ZM387.52 203.937H390.758V105.746H387.52Z' fill='#8080bf'/>
+<path clip-path='url(#clip5)' d='M94.176 203.937H97.41V140.875H94.176ZM152.844 203.937H156.082V135.617H152.844ZM211.512 203.937H214.75V136.805H211.512ZM270.18 203.937H273.418V78.691H270.18ZM328.852 203.937H332.09V139.625H328.852ZM387.52 203.937H390.758V105.746H387.52Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M95.793 140.875V140.625' fill='#8080bf'/>
+<path clip-path='url(#clip5)' d='M95.793 140.875V140.625' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M93.801 140.625H97.785' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M95.793 140.875V141.129' fill='#8080bf'/>
+<path clip-path='url(#clip5)' d='M95.793 140.875V141.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M97.785 141.129H93.801' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M154.461 135.617V135.242' fill='#8080bf'/>
+<path clip-path='url(#clip5)' d='M154.461 135.617V135.242' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M152.469 135.242H156.454' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M154.461 135.617V135.992' fill='#8080bf'/>
+<path clip-path='url(#clip5)' d='M154.461 135.617V135.992' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M156.453 135.992H152.468' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M213.133 136.805V135.68' fill='#8080bf'/>
+<path clip-path='url(#clip5)' d='M213.133 136.805V135.68' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M211.137 135.68H215.125' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M213.133 136.805V137.934' fill='#8080bf'/>
+<path clip-path='url(#clip5)' d='M213.133 136.805V137.934' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M215.125 137.934H211.14' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M271.801 78.691V78.691' fill='#8080bf'/>
+<path clip-path='url(#clip5)' d='M269.809 78.691H273.793' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M271.801 78.691V78.691' fill='#8080bf'/>
+<path clip-path='url(#clip5)' d='M269.809 78.691H273.793' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M330.469 139.625V139.312' fill='#8080bf'/>
+<path clip-path='url(#clip5)' d='M330.469 139.625V139.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M328.477 139.312H332.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M330.469 139.625V139.937' fill='#8080bf'/>
+<path clip-path='url(#clip5)' d='M330.469 139.625V139.937' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M332.461 139.937H328.476' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M389.137 105.746V99.609' fill='#8080bf'/>
+<path clip-path='url(#clip5)' d='M389.137 105.746V99.609' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M387.145 99.609H391.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M389.137 105.746V111.883' fill='#8080bf'/>
+<path clip-path='url(#clip5)' d='M389.137 105.746V111.883' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M391.132 111.883H387.144' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M99.406 203.937H102.641V139.437H99.406ZM158.074 203.937H161.313V140.437H158.074ZM216.742 203.937H219.981V132.922H216.742ZM275.41 203.937H278.649V78.691H275.41ZM334.082 203.937H337.32V106.996H334.082ZM392.75 203.937H395.988V78.691H392.75Z' fill='#ffb733'/>
+<path clip-path='url(#clip5)' d='M99.406 203.937H102.641V139.437H99.406ZM158.074 203.937H161.313V140.437H158.074ZM216.742 203.937H219.981V132.922H216.742ZM275.41 203.937H278.649V78.691H275.41ZM334.082 203.937H337.32V106.996H334.082ZM392.75 203.937H395.988V78.691H392.75Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M101.024 139.437V139.25' fill='#ffb733'/>
+<path clip-path='url(#clip5)' d='M101.024 139.437V139.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M99.031 139.25H103.016' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M101.024 139.437V139.625' fill='#ffb733'/>
+<path clip-path='url(#clip5)' d='M101.024 139.437V139.625' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M103.016 139.625H99.031' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M159.691 140.437V140.062' fill='#ffb733'/>
+<path clip-path='url(#clip5)' d='M159.691 140.437V140.062' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M157.699 140.063H161.684' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M159.691 140.437V140.812' fill='#ffb733'/>
+<path clip-path='url(#clip5)' d='M159.691 140.437V140.812' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M161.684 140.812H157.699' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M218.363 132.922V131.547' fill='#ffb733'/>
+<path clip-path='url(#clip5)' d='M218.363 132.922V131.547' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M216.367 131.547H220.355' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M218.363 132.922V134.301' fill='#ffb733'/>
+<path clip-path='url(#clip5)' d='M218.363 132.922V134.301' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M220.356 134.3H216.371' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M277.031 78.691V78.691' fill='#ffb733'/>
+<path clip-path='url(#clip5)' d='M275.039 78.691H279.023' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M277.031 78.691V78.691' fill='#ffb733'/>
+<path clip-path='url(#clip5)' d='M275.039 78.691H279.023' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M335.699 106.996V105.621' fill='#ffb733'/>
+<path clip-path='url(#clip5)' d='M335.699 106.996V105.621' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M333.707 105.621H337.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M335.699 106.996V108.375' fill='#ffb733'/>
+<path clip-path='url(#clip5)' d='M335.699 106.996V108.375' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M337.692 108.375H333.707' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M394.367 78.691V78.691' fill='#ffb733'/>
+<path clip-path='url(#clip5)' d='M392.375 78.691H396.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M394.367 78.691V78.691' fill='#ffb733'/>
+<path clip-path='url(#clip5)' d='M392.375 78.691H396.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M104.637 203.937H107.871V124.344H104.637ZM163.305 203.937H166.543V135.742H163.305ZM221.973 203.937H225.211V105.996H221.973ZM280.641 203.937H283.879V98.168H280.641ZM339.313 203.937H342.551V122.715H339.313ZM397.981 203.937H401.219V120.336H397.981Z' fill='#bf80bf'/>
+<path clip-path='url(#clip5)' d='M104.637 203.937H107.871V124.344H104.637ZM163.305 203.937H166.543V135.742H163.305ZM221.973 203.937H225.211V105.996H221.973ZM280.641 203.937H283.879V98.168H280.641ZM339.313 203.937H342.551V122.715H339.313ZM397.981 203.937H401.219V120.336H397.981Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M106.254 124.344V124.031' fill='#bf80bf'/>
+<path clip-path='url(#clip5)' d='M106.254 124.344V124.031' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M104.261 124.031H108.246' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M106.254 124.344V124.656' fill='#bf80bf'/>
+<path clip-path='url(#clip5)' d='M106.254 124.344V124.656' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M108.246 124.656H104.261' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M164.922 135.742V135.555' fill='#bf80bf'/>
+<path clip-path='url(#clip5)' d='M164.922 135.742V135.555' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M162.929 135.555H166.914' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M164.922 135.742V135.93' fill='#bf80bf'/>
+<path clip-path='url(#clip5)' d='M164.922 135.742V135.93' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M166.914 135.93H162.929' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M223.594 105.996V104.805' fill='#bf80bf'/>
+<path clip-path='url(#clip5)' d='M223.594 105.996V104.805' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M221.597 104.805H225.585' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M223.594 105.996V107.187' fill='#bf80bf'/>
+<path clip-path='url(#clip5)' d='M223.594 105.996V107.187' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M225.586 107.188H221.601' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M282.262 98.168V96.289' fill='#bf80bf'/>
+<path clip-path='url(#clip5)' d='M282.262 98.168V96.289' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M280.269 96.289H284.253' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M282.262 98.168V100.047' fill='#bf80bf'/>
+<path clip-path='url(#clip5)' d='M282.262 98.168V100.047' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M284.254 100.047H280.269' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M340.93 122.715V121.527' fill='#bf80bf'/>
+<path clip-path='url(#clip5)' d='M340.93 122.715V121.527' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M338.937 121.527H342.921' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M340.93 122.715V123.906' fill='#bf80bf'/>
+<path clip-path='url(#clip5)' d='M340.93 122.715V123.906' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M342.922 123.906H338.937' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M399.598 120.336V114.137' fill='#bf80bf'/>
+<path clip-path='url(#clip5)' d='M399.598 120.336V114.137' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M397.605 114.137H401.589' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M399.598 120.336V126.535' fill='#bf80bf'/>
+<path clip-path='url(#clip5)' d='M399.598 120.336V126.535' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M401.593 126.535H397.605' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M109.867 203.937H113.102V140.687H109.867ZM168.535 203.937H171.774V141.316H168.535ZM227.203 203.937H230.442V140.625H227.203ZM285.871 203.937H289.109V136.555H285.871ZM344.543 203.937H347.781V115.453H344.543ZM403.211 203.937H406.449V135.242H403.211Z' fill='#dfbf9f'/>
+<path clip-path='url(#clip5)' d='M109.867 203.937H113.102V140.687H109.867ZM168.535 203.937H171.774V141.316H168.535ZM227.203 203.937H230.442V140.625H227.203ZM285.871 203.937H289.109V136.555H285.871ZM344.543 203.937H347.781V115.453H344.543ZM403.211 203.937H406.449V135.242H403.211Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M111.484 140.687V140.562' fill='#dfbf9f'/>
+<path clip-path='url(#clip5)' d='M111.484 140.687V140.562' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M109.492 140.563H113.477' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M111.484 140.687V140.812' fill='#dfbf9f'/>
+<path clip-path='url(#clip5)' d='M111.484 140.687V140.812' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M113.477 140.812H109.492' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M170.152 141.316V140.937' fill='#dfbf9f'/>
+<path clip-path='url(#clip5)' d='M170.152 141.316V140.937' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M168.16 140.937H172.145' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M170.152 141.316V141.691' fill='#dfbf9f'/>
+<path clip-path='url(#clip5)' d='M170.152 141.316V141.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M172.145 141.692H168.16' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M228.824 140.625V139.25' fill='#dfbf9f'/>
+<path clip-path='url(#clip5)' d='M228.824 140.625V139.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M226.828 139.25H230.816' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M228.824 140.625V142.004' fill='#dfbf9f'/>
+<path clip-path='url(#clip5)' d='M228.824 140.625V142.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M230.817 142.004H226.832' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M287.492 136.555V134.801' fill='#dfbf9f'/>
+<path clip-path='url(#clip5)' d='M287.492 136.555V134.801' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M285.5 134.8H289.484' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M287.492 136.555V138.309' fill='#dfbf9f'/>
+<path clip-path='url(#clip5)' d='M287.492 136.555V138.309' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M289.485 138.308H285.5' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M346.16 115.453V114.074' fill='#dfbf9f'/>
+<path clip-path='url(#clip5)' d='M346.16 115.453V114.074' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M344.168 114.074H348.152' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M346.16 115.453V116.828' fill='#dfbf9f'/>
+<path clip-path='url(#clip5)' d='M346.16 115.453V116.828' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M348.153 116.829H344.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M404.828 135.242V128.539' fill='#dfbf9f'/>
+<path clip-path='url(#clip5)' d='M404.828 135.242V128.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M402.836 128.539H406.82' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M404.828 135.242V141.941' fill='#dfbf9f'/>
+<path clip-path='url(#clip5)' d='M404.828 135.242V141.941' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M406.824 141.941H402.836' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M115.098 203.937H118.332V136.055H115.098ZM173.766 203.937H177.004V128.539H173.766ZM232.434 203.937H235.672V116.016H232.434ZM291.102 203.937H294.34V78.691H291.102ZM349.774 203.937H353.012V78.691H349.774ZM408.442 203.937H411.68V78.691H408.442Z' fill='#80bf80'/>
+<path clip-path='url(#clip5)' d='M115.098 203.937H118.332V136.055H115.098ZM173.766 203.937H177.004V128.539H173.766ZM232.434 203.937H235.672V116.016H232.434ZM291.102 203.937H294.34V78.691H291.102ZM349.774 203.937H353.012V78.691H349.774ZM408.442 203.937H411.68V78.691H408.442Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M116.715 136.055V135.93' fill='#80bf80'/>
+<path clip-path='url(#clip5)' d='M116.715 136.055V135.93' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M114.722 135.93H118.707' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M116.715 136.055V136.18' fill='#80bf80'/>
+<path clip-path='url(#clip5)' d='M116.715 136.055V136.18' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M118.707 136.18H114.722' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M175.383 128.539V126.723' fill='#80bf80'/>
+<path clip-path='url(#clip5)' d='M175.383 128.539V126.723' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M173.39 126.723H177.375' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M175.383 128.539V130.355' fill='#80bf80'/>
+<path clip-path='url(#clip5)' d='M175.383 128.539V130.355' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M177.375 130.355H173.39' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M234.055 116.016V114.449' fill='#80bf80'/>
+<path clip-path='url(#clip5)' d='M234.055 116.016V114.449' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M232.058 114.449H236.046' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M234.055 116.016V117.582' fill='#80bf80'/>
+<path clip-path='url(#clip5)' d='M234.055 116.016V117.582' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M236.047 117.582H232.062' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M292.723 78.691V78.691' fill='#80bf80'/>
+<path clip-path='url(#clip5)' d='M290.73 78.691H294.714' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M292.723 78.691V78.691' fill='#80bf80'/>
+<path clip-path='url(#clip5)' d='M290.73 78.691H294.714' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M351.391 78.691V78.691' fill='#80bf80'/>
+<path clip-path='url(#clip5)' d='M349.398 78.691H353.382' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M351.391 78.691V78.691' fill='#80bf80'/>
+<path clip-path='url(#clip5)' d='M349.398 78.691H353.382' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M410.059 78.691V78.691' fill='#80bf80'/>
+<path clip-path='url(#clip5)' d='M408.066 78.691H412.05' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M410.059 78.691V78.691' fill='#80bf80'/>
+<path clip-path='url(#clip5)' d='M408.066 78.691H412.05' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M120.328 203.937H123.563V137.621H120.328ZM178.996 203.937H182.234V197.676H178.996ZM237.664 203.937H240.902V136.43H237.664ZM296.332 203.937H299.57V116.953H296.332ZM355.004 203.937H358.238V197.676H355.004ZM413.672 203.937H416.91V78.691H413.672Z' fill='#bfbf80'/>
+<path clip-path='url(#clip5)' d='M120.328 203.937H123.563V137.621H120.328ZM178.996 203.937H182.234V197.676H178.996ZM237.664 203.937H240.902V136.43H237.664ZM296.332 203.937H299.57V116.953H296.332ZM355.004 203.937H358.238V197.676H355.004ZM413.672 203.937H416.91V78.691H413.672Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M121.945 137.621V137.371' fill='#bfbf80'/>
+<path clip-path='url(#clip5)' d='M121.945 137.621V137.371' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M119.953 137.371H123.938' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M121.945 137.621V137.871' fill='#bfbf80'/>
+<path clip-path='url(#clip5)' d='M121.945 137.621V137.871' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M123.938 137.871H119.953' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M180.613 197.676V197.676' fill='#bfbf80'/>
+<path clip-path='url(#clip5)' d='M178.621 197.676H182.606' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M180.613 197.676V197.676' fill='#bfbf80'/>
+<path clip-path='url(#clip5)' d='M178.621 197.676H182.606' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M239.285 136.43V134.988' fill='#bfbf80'/>
+<path clip-path='url(#clip5)' d='M239.285 136.43V134.988' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M237.289 134.988H241.274' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M239.285 136.43V137.871' fill='#bfbf80'/>
+<path clip-path='url(#clip5)' d='M239.285 136.43V137.871' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M241.278 137.871H237.293' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M297.953 116.953V115.391' fill='#bfbf80'/>
+<path clip-path='url(#clip5)' d='M297.953 116.953V115.391' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M295.961 115.39H299.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M297.953 116.953V118.519' fill='#bfbf80'/>
+<path clip-path='url(#clip5)' d='M297.953 116.953V118.519' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M299.946 118.519H295.961' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M356.621 197.676V197.676' fill='#bfbf80'/>
+<path clip-path='url(#clip5)' d='M354.629 197.676H358.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M356.621 197.676V197.676' fill='#bfbf80'/>
+<path clip-path='url(#clip5)' d='M354.629 197.676H358.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M415.289 78.691V78.691' fill='#bfbf80'/>
+<path clip-path='url(#clip5)' d='M413.297 78.691H417.281' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M415.289 78.691V78.691' fill='#bfbf80'/>
+<path clip-path='url(#clip5)' d='M413.297 78.691H417.281' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M125.559 203.937H128.793V138.121H125.559ZM184.227 203.937H187.465V132.047H184.227ZM242.895 203.937H246.133V134.426H242.895ZM301.563 203.937H304.801V78.691H301.563ZM360.234 203.937H363.469V78.691H360.234ZM418.902 203.937H422.141V109.254H418.902Z' fill='#339999'/>
+<path clip-path='url(#clip5)' d='M125.559 203.937H128.793V138.121H125.559ZM184.227 203.937H187.465V132.047H184.227ZM242.895 203.937H246.133V134.426H242.895ZM301.563 203.937H304.801V78.691H301.563ZM360.234 203.937H363.469V78.691H360.234ZM418.902 203.937H422.141V109.254H418.902Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M127.176 138.121V137.934' fill='#339999'/>
+<path clip-path='url(#clip5)' d='M127.176 138.121V137.934' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M125.183 137.934H129.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M127.176 138.121V138.309' fill='#339999'/>
+<path clip-path='url(#clip5)' d='M127.176 138.121V138.309' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M129.168 138.308H125.183' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M185.844 132.047V131.672' fill='#339999'/>
+<path clip-path='url(#clip5)' d='M185.844 132.047V131.672' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M183.851 131.672H187.836' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M185.844 132.047V132.422' fill='#339999'/>
+<path clip-path='url(#clip5)' d='M185.844 132.047V132.422' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M187.836 132.422H183.851' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M244.516 134.426V133.488' fill='#339999'/>
+<path clip-path='url(#clip5)' d='M244.516 134.426V133.488' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M242.519 133.488H246.504' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M244.516 134.426V135.367' fill='#339999'/>
+<path clip-path='url(#clip5)' d='M244.516 134.426V135.367' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M246.508 135.367H242.523' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M303.184 78.691V78.691' fill='#339999'/>
+<path clip-path='url(#clip5)' d='M301.191 78.691H305.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M303.184 78.691V78.691' fill='#339999'/>
+<path clip-path='url(#clip5)' d='M301.191 78.691H305.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M361.852 78.691V78.691' fill='#339999'/>
+<path clip-path='url(#clip5)' d='M359.859 78.691H363.843' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M361.852 78.691V78.691' fill='#339999'/>
+<path clip-path='url(#clip5)' d='M359.859 78.691H363.843' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M420.52 109.254V107.25' fill='#339999'/>
+<path clip-path='url(#clip5)' d='M420.52 109.254V107.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M418.527 107.25H422.511' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M420.52 109.254V111.258' fill='#339999'/>
+<path clip-path='url(#clip5)' d='M420.52 109.254V111.258' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M422.512 111.258H418.527' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M130.789 203.937H134.024V139.187H130.789ZM189.457 203.937H192.695V139.312H189.457ZM248.125 203.937H251.363V140.062H248.125ZM306.793 203.937H310.031V126.035H306.793ZM365.465 203.937H368.699V103.176H365.465ZM424.133 203.937H427.371V113.762H424.133Z' fill='#bf8080'/>
+<path clip-path='url(#clip5)' d='M130.789 203.937H134.024V139.187H130.789ZM189.457 203.937H192.695V139.312H189.457ZM248.125 203.937H251.363V140.062H248.125ZM306.793 203.937H310.031V126.035H306.793ZM365.465 203.937H368.699V103.176H365.465ZM424.133 203.937H427.371V113.762H424.133Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M132.406 139.187V138.934' fill='#bf8080'/>
+<path clip-path='url(#clip5)' d='M132.406 139.187V138.934' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M130.414 138.933H134.399' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M132.406 139.187V139.437' fill='#bf8080'/>
+<path clip-path='url(#clip5)' d='M132.406 139.187V139.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M134.399 139.437H130.414' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M191.074 139.312V139.062' fill='#bf8080'/>
+<path clip-path='url(#clip5)' d='M191.074 139.312V139.062' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M189.082 139.063H193.067' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M191.074 139.312V139.562' fill='#bf8080'/>
+<path clip-path='url(#clip5)' d='M191.074 139.312V139.562' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M193.067 139.563H189.082' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M249.746 140.062V138.871' fill='#bf8080'/>
+<path clip-path='url(#clip5)' d='M249.746 140.062V138.871' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M247.75 138.871H251.735' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M249.746 140.062V141.254' fill='#bf8080'/>
+<path clip-path='url(#clip5)' d='M249.746 140.062V141.254' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M251.739 141.254H247.754' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M308.414 126.035V125.031' fill='#bf8080'/>
+<path clip-path='url(#clip5)' d='M308.414 126.035V125.031' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M306.422 125.031H310.407' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M308.414 126.035V127.035' fill='#bf8080'/>
+<path clip-path='url(#clip5)' d='M308.414 126.035V127.035' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M310.407 127.035H306.422' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M367.082 103.176V101.613' fill='#bf8080'/>
+<path clip-path='url(#clip5)' d='M367.082 103.176V101.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M365.09 101.613H369.074' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M367.082 103.176V104.742' fill='#bf8080'/>
+<path clip-path='url(#clip5)' d='M367.082 103.176V104.742' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M369.075 104.742H365.09' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M425.75 113.762V105.184' fill='#bf8080'/>
+<path clip-path='url(#clip5)' d='M425.75 113.762V105.184' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M423.758 105.184H427.742' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M425.75 113.762V122.34' fill='#bf8080'/>
+<path clip-path='url(#clip5)' d='M425.75 113.762V122.34' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M427.743 122.34H423.758' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(0 -1 1 0 -91.184 249.777)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-48' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -32.515 249.777)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-48' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 26.154 249.777)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-48' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 84.823 249.777)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-48' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 143.492 249.777)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-48' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 202.161 249.777)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-48' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -85.954 249.339)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-49' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -27.284 244.079)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-57' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 31.385 245.269)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-55' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 90.054 187.154)'>
+<use x='109.598' xlink:href='#g4-1' y='183.381'/>
+<use x='113.103' xlink:href='#g4-1' y='183.381'/>
+<use x='116.608' xlink:href='#g4-1' y='183.381'/>
+<use x='120.114' xlink:href='#g2-52' y='183.381'/>
+<use x='122.76' xlink:href='#g2-46' y='183.381'/>
+<use x='124.23' xlink:href='#g2-57' y='183.381'/>
+<use x='126.877' xlink:href='#g2-52' y='183.381'/>
+<use x='129.523' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 148.723 248.087)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-51' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 207.392 214.208)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-53' y='183.381'/>
+<use x='121.25' xlink:href='#g2-55' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -80.723 247.899)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-51' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -22.054 248.901)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-49' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 36.615 241.386)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-49' y='183.381'/>
+<use x='121.25' xlink:href='#g2-51' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 95.284 187.154)'>
+<use x='109.598' xlink:href='#g4-1' y='183.381'/>
+<use x='113.103' xlink:href='#g4-1' y='183.381'/>
+<use x='116.608' xlink:href='#g4-1' y='183.381'/>
+<use x='120.114' xlink:href='#g2-53' y='183.381'/>
+<use x='122.76' xlink:href='#g2-46' y='183.381'/>
+<use x='124.23' xlink:href='#g2-50' y='183.381'/>
+<use x='126.877' xlink:href='#g2-50' y='183.381'/>
+<use x='129.523' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 153.953 215.46)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-53' y='183.381'/>
+<use x='121.25' xlink:href='#g2-53' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 212.622 187.154)'>
+<use x='109.598' xlink:href='#g4-1' y='183.381'/>
+<use x='113.103' xlink:href='#g4-1' y='183.381'/>
+<use x='116.608' xlink:href='#g4-1' y='183.381'/>
+<use x='120.114' xlink:href='#g2-51' y='183.381'/>
+<use x='122.76' xlink:href='#g2-46' y='183.381'/>
+<use x='124.23' xlink:href='#g2-55' y='183.381'/>
+<use x='126.877' xlink:href='#g2-56' y='183.381'/>
+<use x='129.523' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -75.493 232.807)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-50' y='183.381'/>
+<use x='121.25' xlink:href='#g2-55' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -16.824 244.204)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-57' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 41.845 214.458)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-53' y='183.381'/>
+<use x='121.25' xlink:href='#g2-54' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 100.514 206.63)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-54' y='183.381'/>
+<use x='121.25' xlink:href='#g2-57' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 159.183 231.178)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-51' y='183.381'/>
+<use x='121.25' xlink:href='#g2-48' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 217.852 228.799)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-51' y='183.381'/>
+<use x='121.25' xlink:href='#g2-52' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -70.262 249.151)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-49' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -11.593 249.777)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-48' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 47.076 249.089)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-49' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 105.745 245.018)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-56' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 164.414 223.914)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-52' y='183.381'/>
+<use x='121.25' xlink:href='#g2-49' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 223.083 243.703)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-49' y='183.381'/>
+<use x='121.25' xlink:href='#g2-48' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -65.032 244.517)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-56' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -6.363 237.002)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-50' y='183.381'/>
+<use x='121.25' xlink:href='#g2-48' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 52.306 224.478)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-52' y='183.381'/>
+<use x='121.25' xlink:href='#g2-48' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 110.975 187.154)'>
+<use x='109.598' xlink:href='#g4-1' y='183.381'/>
+<use x='113.103' xlink:href='#g4-1' y='183.381'/>
+<use x='116.608' xlink:href='#g4-1' y='183.381'/>
+<use x='120.114' xlink:href='#g2-51' y='183.381'/>
+<use x='122.76' xlink:href='#g2-46' y='183.381'/>
+<use x='124.23' xlink:href='#g2-48' y='183.381'/>
+<use x='126.877' xlink:href='#g2-50' y='183.381'/>
+<use x='129.523' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 169.644 187.154)'>
+<use x='109.598' xlink:href='#g4-1' y='183.381'/>
+<use x='113.103' xlink:href='#g4-1' y='183.381'/>
+<use x='116.608' xlink:href='#g4-1' y='183.381'/>
+<use x='120.114' xlink:href='#g2-50' y='183.381'/>
+<use x='122.76' xlink:href='#g2-46' y='183.381'/>
+<use x='124.23' xlink:href='#g2-51' y='183.381'/>
+<use x='126.877' xlink:href='#g2-49' y='183.381'/>
+<use x='129.523' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 228.313 187.154)'>
+<use x='109.598' xlink:href='#g4-1' y='183.381'/>
+<use x='113.103' xlink:href='#g4-1' y='183.381'/>
+<use x='116.608' xlink:href='#g4-1' y='183.381'/>
+<use x='120.114' xlink:href='#g2-52' y='183.381'/>
+<use x='122.76' xlink:href='#g2-46' y='183.381'/>
+<use x='124.23' xlink:href='#g2-54' y='183.381'/>
+<use x='126.877' xlink:href='#g2-51' y='183.381'/>
+<use x='129.523' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -59.801 246.083)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-54' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -1.66 306.138)'>
+<use x='114.487' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 57.537 244.893)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-56' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 116.206 225.417)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-51' y='183.381'/>
+<use x='121.25' xlink:href='#g2-57' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 174.347 306.138)'>
+<use x='114.487' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 233.544 187.154)'>
+<use x='109.598' xlink:href='#g4-1' y='183.381'/>
+<use x='113.103' xlink:href='#g4-1' y='183.381'/>
+<use x='116.608' xlink:href='#g4-1' y='183.381'/>
+<use x='120.114' xlink:href='#g2-51' y='183.381'/>
+<use x='122.76' xlink:href='#g2-46' y='183.381'/>
+<use x='124.23' xlink:href='#g2-56' y='183.381'/>
+<use x='126.877' xlink:href='#g2-53' y='183.381'/>
+<use x='129.523' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -54.571 246.584)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-53' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 4.098 240.509)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-49' y='183.381'/>
+<use x='121.25' xlink:href='#g2-53' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 62.767 242.889)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-49' y='183.381'/>
+<use x='121.25' xlink:href='#g2-49' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 121.436 187.154)'>
+<use x='109.598' xlink:href='#g4-1' y='183.381'/>
+<use x='113.103' xlink:href='#g4-1' y='183.381'/>
+<use x='116.608' xlink:href='#g4-1' y='183.381'/>
+<use x='120.114' xlink:href='#g2-51' y='183.381'/>
+<use x='122.76' xlink:href='#g2-46' y='183.381'/>
+<use x='124.23' xlink:href='#g2-50' y='183.381'/>
+<use x='126.877' xlink:href='#g2-50' y='183.381'/>
+<use x='129.523' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 180.105 187.154)'>
+<use x='109.598' xlink:href='#g4-1' y='183.381'/>
+<use x='113.103' xlink:href='#g4-1' y='183.381'/>
+<use x='116.608' xlink:href='#g4-1' y='183.381'/>
+<use x='120.114' xlink:href='#g2-50' y='183.381'/>
+<use x='122.76' xlink:href='#g2-46' y='183.381'/>
+<use x='124.23' xlink:href='#g2-56' y='183.381'/>
+<use x='126.877' xlink:href='#g2-56' y='183.381'/>
+<use x='129.523' xlink:href='#g2-120' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 238.774 217.714)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-53' y='183.381'/>
+<use x='121.25' xlink:href='#g2-49' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -49.34 247.648)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-51' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 9.329 247.773)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-51' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 67.998 248.525)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-48' y='183.381'/>
+<use x='121.25' xlink:href='#g2-50' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 126.667 234.497)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-50' y='183.381'/>
+<use x='121.25' xlink:href='#g2-52' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 185.336 211.64)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-54' y='183.381'/>
+<use x='121.25' xlink:href='#g2-49' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 244.005 222.223)'>
+<use x='114.487' xlink:href='#g2-49' y='183.381'/>
+<use x='117.133' xlink:href='#g2-46' y='183.381'/>
+<use x='118.603' xlink:href='#g2-52' y='183.381'/>
+<use x='121.25' xlink:href='#g2-52' y='183.381'/>
+</g>
+<g transform='matrix(0 -1 1 0 -124.021 311.062)'>
+<use x='114.487' xlink:href='#g1-82' y='183.381'/>
+<use x='120.457' xlink:href='#g1-101' y='183.381'/>
+<use x='124.553' xlink:href='#g1-108' y='183.381'/>
+<use x='126.753' xlink:href='#g1-97' y='183.381'/>
+<use x='131.181' xlink:href='#g1-116' y='183.381'/>
+<use x='134.509' xlink:href='#g1-105' y='183.381'/>
+<use x='136.709' xlink:href='#g1-118' y='183.381'/>
+<use x='140.957' xlink:href='#g1-101' y='183.381'/>
+<use x='148.124' xlink:href='#g1-116' y='183.381'/>
+<use x='151.452' xlink:href='#g1-105' y='183.381'/>
+<use x='153.652' xlink:href='#g1-109' y='183.381'/>
+<use x='160.972' xlink:href='#g1-101' y='183.381'/>
+<use x='168.139' xlink:href='#g3-40' y='183.381'/>
+<use x='171.432' xlink:href='#g3-108' y='183.381'/>
+<use x='173.453' xlink:href='#g3-111' y='183.381'/>
+<use x='177.452' xlink:href='#g3-119' y='183.381'/>
+<use x='183' xlink:href='#g3-101' y='183.381'/>
+<use x='186.764' xlink:href='#g3-114' y='183.381'/>
+<use x='192.479' xlink:href='#g3-105' y='183.381'/>
+<use x='194.499' xlink:href='#g3-115' y='183.381'/>
+<use x='200.568' xlink:href='#g3-98' y='183.381'/>
+<use x='205.176' xlink:href='#g3-101' y='183.381'/>
+<use x='208.94' xlink:href='#g3-116' y='183.381'/>
+<use x='211.998' xlink:href='#g3-116' y='183.381'/>
+<use x='215.056' xlink:href='#g3-101' y='183.381'/>
+<use x='218.819' xlink:href='#g3-114' y='183.381'/>
+<use x='221.711' xlink:href='#g3-41' y='183.381'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='258.383pt' version='1.1' viewBox='106.736 54.996 381.623 258.383' width='381.623pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip6'>
+<path d='M135.949 251.93H487.961V84.164H135.949Z'/>
+</clipPath>
+<use id='g3-40' transform='scale(1.143)' xlink:href='#g0-40'/>
+<use id='g3-41' transform='scale(1.143)' xlink:href='#g0-41'/>
+<use id='g3-45' transform='scale(1.143)' xlink:href='#g0-45'/>
+<use id='g3-49' transform='scale(1.143)' xlink:href='#g0-49'/>
+<use id='g3-54' transform='scale(1.143)' xlink:href='#g0-54'/>
+<use id='g3-56' transform='scale(1.143)' xlink:href='#g0-56'/>
+<use id='g3-58' transform='scale(1.143)' xlink:href='#g0-58'/>
+<use id='g3-78' transform='scale(1.143)' xlink:href='#g0-78'/>
+<use id='g3-97' transform='scale(1.143)' xlink:href='#g0-97'/>
+<use id='g3-98' transform='scale(1.143)' xlink:href='#g0-98'/>
+<use id='g3-99' transform='scale(1.143)' xlink:href='#g0-99'/>
+<use id='g3-100' transform='scale(1.143)' xlink:href='#g0-100'/>
+<use id='g3-101' transform='scale(1.143)' xlink:href='#g0-101'/>
+<use id='g3-103' transform='scale(1.143)' xlink:href='#g0-103'/>
+<use id='g3-104' transform='scale(1.143)' xlink:href='#g0-104'/>
+<use id='g3-105' transform='scale(1.143)' xlink:href='#g0-105'/>
+<use id='g3-106' transform='scale(1.143)' xlink:href='#g0-106'/>
+<use id='g3-108' transform='scale(1.143)' xlink:href='#g0-108'/>
+<use id='g3-109' transform='scale(1.143)' xlink:href='#g0-109'/>
+<use id='g3-110' transform='scale(1.143)' xlink:href='#g0-110'/>
+<use id='g3-111' transform='scale(1.143)' xlink:href='#g0-111'/>
+<use id='g3-112' transform='scale(1.143)' xlink:href='#g0-112'/>
+<use id='g3-114' transform='scale(1.143)' xlink:href='#g0-114'/>
+<use id='g3-115' transform='scale(1.143)' xlink:href='#g0-115'/>
+<use id='g3-116' transform='scale(1.143)' xlink:href='#g0-116'/>
+<use id='g3-119' transform='scale(1.143)' xlink:href='#g0-119'/>
+<use id='g3-120' transform='scale(1.143)' xlink:href='#g0-120'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g1-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g1-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g1-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g1-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g1-108'/>
+<path d='M6.581 -2.663C6.581 -3.327 6.402 -4.08 5.317 -4.08C4.564 -4.08 4.142 -3.622 3.927 -3.344C3.865 -3.524 3.676 -4.08 2.762 -4.08C2.053 -4.08 1.623 -3.667 1.417 -3.398V-4.035H0.726V0H1.47V-2.188C1.47 -2.78 1.704 -3.497 2.385 -3.497C3.282 -3.497 3.282 -2.86 3.282 -2.6V0H4.026V-2.188C4.026 -2.78 4.259 -3.497 4.94 -3.497C5.837 -3.497 5.837 -2.86 5.837 -2.6V0H6.581V-2.663Z' id='g1-109'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g1-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g1-118'/>
+<use id='g2-44' transform='scale(0.714)' xlink:href='#g0-44'/>
+<use id='g2-45' transform='scale(0.714)' xlink:href='#g0-45'/>
+<use id='g2-46' transform='scale(0.714)' xlink:href='#g0-46'/>
+<use id='g2-48' transform='scale(0.714)' xlink:href='#g0-48'/>
+<use id='g2-49' transform='scale(0.714)' xlink:href='#g0-49'/>
+<use id='g2-50' transform='scale(0.714)' xlink:href='#g0-50'/>
+<use id='g2-51' transform='scale(0.714)' xlink:href='#g0-51'/>
+<use id='g2-52' transform='scale(0.714)' xlink:href='#g0-52'/>
+<use id='g2-53' transform='scale(0.714)' xlink:href='#g0-53'/>
+<use id='g2-54' transform='scale(0.714)' xlink:href='#g0-54'/>
+<use id='g2-55' transform='scale(0.714)' xlink:href='#g0-55'/>
+<use id='g2-56' transform='scale(0.714)' xlink:href='#g0-56'/>
+<use id='g2-57' transform='scale(0.714)' xlink:href='#g0-57'/>
+<use id='g2-64' transform='scale(0.714)' xlink:href='#g0-64'/>
+<use id='g2-65' transform='scale(0.714)' xlink:href='#g0-65'/>
+<use id='g2-67' transform='scale(0.714)' xlink:href='#g0-67'/>
+<use id='g2-68' transform='scale(0.714)' xlink:href='#g0-68'/>
+<use id='g2-71' transform='scale(0.714)' xlink:href='#g0-71'/>
+<use id='g2-77' transform='scale(0.714)' xlink:href='#g0-77'/>
+<use id='g2-85' transform='scale(0.714)' xlink:href='#g0-85'/>
+<use id='g2-97' transform='scale(0.714)' xlink:href='#g0-97'/>
+<use id='g2-98' transform='scale(0.714)' xlink:href='#g0-98'/>
+<use id='g2-99' transform='scale(0.714)' xlink:href='#g0-99'/>
+<use id='g2-100' transform='scale(0.714)' xlink:href='#g0-100'/>
+<use id='g2-101' transform='scale(0.714)' xlink:href='#g0-101'/>
+<use id='g2-103' transform='scale(0.714)' xlink:href='#g0-103'/>
+<use id='g2-104' transform='scale(0.714)' xlink:href='#g0-104'/>
+<use id='g2-108' transform='scale(0.714)' xlink:href='#g0-108'/>
+<use id='g2-109' transform='scale(0.714)' xlink:href='#g0-109'/>
+<use id='g2-110' transform='scale(0.714)' xlink:href='#g0-110'/>
+<use id='g2-111' transform='scale(0.714)' xlink:href='#g0-111'/>
+<use id='g2-112' transform='scale(0.714)' xlink:href='#g0-112'/>
+<use id='g2-114' transform='scale(0.714)' xlink:href='#g0-114'/>
+<use id='g2-115' transform='scale(0.714)' xlink:href='#g0-115'/>
+<use id='g2-116' transform='scale(0.714)' xlink:href='#g0-116'/>
+<use id='g2-117' transform='scale(0.714)' xlink:href='#g0-117'/>
+<use id='g2-120' transform='scale(0.714)' xlink:href='#g0-120'/>
+<use id='g2-122' transform='scale(0.714)' xlink:href='#g0-122'/>
+<path d='M1.445 -1.245C1.445 -1.41 1.305 -1.549 1.141 -1.549S0.837 -1.41 0.837 -1.245S0.976 -0.941 1.141 -0.941S1.445 -1.081 1.445 -1.245Z' id='g4-1'/>
+<path d='M2.127 -5.23C2.008 -5.23 1.995 -5.23 1.911 -5.154C1.032 -4.387 0.586 -3.145 0.586 -1.743C0.586 -0.425 0.983 0.844 1.904 1.653C1.995 1.743 2.008 1.743 2.127 1.743H2.462C2.441 1.73 1.764 1.151 1.444 0.063C1.276 -0.481 1.193 -1.053 1.193 -1.743C1.193 -4.156 2.322 -5.112 2.462 -5.23H2.127Z' id='g0-40'/>
+<path d='M0.746 1.743C0.865 1.743 0.879 1.743 0.962 1.667C1.841 0.9 2.287 -0.342 2.287 -1.743C2.287 -3.062 1.89 -4.331 0.969 -5.14C0.879 -5.23 0.865 -5.23 0.746 -5.23H0.411C0.432 -5.216 1.109 -4.638 1.43 -3.55C1.597 -3.006 1.681 -2.434 1.681 -1.743C1.681 0.669 0.551 1.625 0.411 1.743H0.746Z' id='g0-41'/>
+<path d='M1.339 -0.007V-0.628H0.711V0H0.907L0.704 0.893H1.018L1.339 -0.007Z' id='g0-44'/>
+<path d='M2.05 -1.332V-1.771H0.084V-1.332H2.05Z' id='g0-45'/>
+<path d='M1.339 -0.628H0.711V0H1.339V-0.628Z' id='g0-46'/>
+<path d='M3.403 -2.267C3.403 -2.601 3.403 -3.417 3.075 -3.989C2.72 -4.617 2.183 -4.721 1.848 -4.721C1.534 -4.721 0.99 -4.624 0.642 -4.024C0.307 -3.466 0.293 -2.706 0.293 -2.267C0.293 -1.75 0.321 -1.116 0.614 -0.586C0.921 -0.021 1.437 0.146 1.848 0.146C2.545 0.146 2.929 -0.258 3.138 -0.697C3.382 -1.193 3.403 -1.834 3.403 -2.267ZM1.848 -0.314C1.555 -0.314 1.22 -0.481 1.046 -0.983C0.907 -1.409 0.9 -1.848 0.9 -2.357C0.9 -2.999 0.9 -4.261 1.848 -4.261S2.797 -2.999 2.797 -2.357C2.797 -1.897 2.797 -1.374 2.629 -0.928C2.434 -0.425 2.078 -0.314 1.848 -0.314Z' id='g0-48'/>
+<path d='M2.239 -4.721H2.085C1.632 -4.303 1.06 -4.275 0.642 -4.261V-3.822C0.914 -3.829 1.262 -3.843 1.611 -3.982V-0.439H0.683V0H3.166V-0.439H2.239V-4.721Z' id='g0-49'/>
+<path d='M1.974 -0.537C1.89 -0.537 1.806 -0.53 1.723 -0.53H0.928L2.008 -1.485C2.134 -1.597 2.476 -1.855 2.608 -1.967C2.915 -2.246 3.327 -2.608 3.327 -3.215C3.327 -4.003 2.741 -4.721 1.743 -4.721C1.004 -4.721 0.544 -4.324 0.307 -3.612L0.635 -3.201C0.795 -3.787 1.039 -4.24 1.646 -4.24C2.232 -4.24 2.678 -3.829 2.678 -3.201C2.678 -2.622 2.336 -2.294 1.918 -1.897C1.778 -1.757 1.402 -1.444 1.255 -1.304C1.053 -1.123 0.572 -0.656 0.37 -0.481V0H3.327V-0.537H1.974Z' id='g0-50'/>
+<path d='M0.697 -3.578C0.983 -4.135 1.485 -4.289 1.82 -4.289C2.232 -4.289 2.538 -4.052 2.538 -3.654C2.538 -3.285 2.287 -2.831 1.757 -2.741C1.723 -2.734 1.695 -2.734 1.234 -2.699V-2.239H1.778C2.441 -2.239 2.685 -1.716 2.685 -1.276C2.685 -0.732 2.35 -0.314 1.806 -0.314C1.311 -0.314 0.746 -0.551 0.398 -0.997L0.307 -0.544C0.711 -0.091 1.276 0.146 1.82 0.146C2.734 0.146 3.389 -0.537 3.389 -1.269C3.389 -1.841 2.929 -2.301 2.378 -2.462C2.908 -2.734 3.18 -3.201 3.18 -3.654C3.18 -4.247 2.573 -4.721 1.827 -4.721C1.213 -4.721 0.704 -4.4 0.411 -3.982L0.697 -3.578Z' id='g0-51'/>
+<path d='M2.762 -1.165H3.487V-1.625H2.762V-4.575H2.071L0.209 -1.625V-1.165H2.162V0H2.762V-1.165ZM0.802 -1.625C1.011 -1.953 2.211 -3.815 2.211 -4.233V-1.625H0.802Z' id='g0-52'/>
+<path d='M1.144 -4.094H3.075V-4.575H0.586V-1.967H1.095C1.262 -2.343 1.59 -2.511 1.904 -2.511C2.19 -2.511 2.622 -2.315 2.622 -1.43C2.622 -0.516 2.043 -0.314 1.688 -0.314C1.227 -0.314 0.781 -0.558 0.544 -0.955L0.279 -0.537C0.621 -0.112 1.137 0.146 1.688 0.146C2.608 0.146 3.327 -0.565 3.327 -1.416C3.327 -2.28 2.685 -2.971 1.918 -2.971C1.618 -2.971 1.353 -2.866 1.144 -2.692V-4.094Z' id='g0-53'/>
+<path d='M3.062 -4.582C2.685 -4.721 2.42 -4.721 2.287 -4.721C1.227 -4.721 0.307 -3.724 0.307 -2.253C0.307 -0.363 1.158 0.146 1.862 0.146C2.427 0.146 2.72 -0.119 2.936 -0.342C3.382 -0.816 3.389 -1.311 3.389 -1.555C3.389 -2.469 2.894 -3.229 2.218 -3.229C1.534 -3.229 1.165 -2.873 0.962 -2.671C1.053 -3.626 1.541 -4.289 2.294 -4.289C2.434 -4.289 2.713 -4.275 3.062 -4.142V-4.582ZM0.969 -1.534C0.969 -1.576 0.969 -1.681 0.976 -1.716C0.976 -2.19 1.276 -2.769 1.897 -2.769C2.748 -2.769 2.748 -1.792 2.748 -1.555C2.748 -1.29 2.748 -0.997 2.559 -0.704C2.392 -0.453 2.183 -0.314 1.862 -0.314C1.123 -0.314 1.004 -1.227 0.969 -1.534Z' id='g0-54'/>
+<path d='M1.723 -4.038C1.806 -4.038 1.89 -4.045 1.974 -4.045H2.852C1.792 -3.006 1.116 -1.548 1.116 0.07H1.771C1.771 -1.967 2.762 -3.431 3.389 -4.087V-4.575H0.307V-4.038H1.723Z' id='g0-55'/>
+<path d='M2.385 -2.469C2.845 -2.615 3.285 -2.985 3.285 -3.501C3.285 -4.135 2.678 -4.721 1.848 -4.721S0.411 -4.135 0.411 -3.501C0.411 -2.978 0.865 -2.608 1.311 -2.469C0.697 -2.28 0.307 -1.806 0.307 -1.269C0.307 -0.523 0.969 0.146 1.848 0.146S3.389 -0.523 3.389 -1.269C3.389 -1.806 2.992 -2.28 2.385 -2.469ZM1.848 -2.699C1.353 -2.699 0.948 -2.985 0.948 -3.494C0.948 -3.94 1.262 -4.289 1.848 -4.289C2.427 -4.289 2.748 -3.94 2.748 -3.494C2.748 -2.999 2.357 -2.699 1.848 -2.699ZM1.848 -0.314C1.367 -0.314 0.941 -0.621 0.941 -1.276C0.941 -1.904 1.346 -2.239 1.848 -2.239S2.755 -1.897 2.755 -1.276C2.755 -0.621 2.322 -0.314 1.848 -0.314Z' id='g0-56'/>
+<path d='M0.537 -0.174C0.879 0.077 1.193 0.146 1.52 0.146C2.497 0.146 3.389 -0.837 3.389 -2.336C3.389 -4.24 2.545 -4.721 1.876 -4.721C1.255 -4.721 0.969 -4.428 0.767 -4.226C0.321 -3.773 0.307 -3.292 0.307 -3.02C0.307 -2.12 0.795 -1.346 1.478 -1.346C2.267 -1.346 2.699 -1.869 2.734 -1.911C2.636 -0.802 2.092 -0.314 1.52 -0.314C1.158 -0.314 0.934 -0.446 0.774 -0.579L0.537 -0.174ZM2.713 -3.027C2.72 -2.985 2.72 -2.915 2.72 -2.873C2.72 -2.357 2.406 -1.806 1.799 -1.806C1.534 -1.806 1.325 -1.883 1.144 -2.169C0.962 -2.441 0.948 -2.706 0.948 -3.02C0.948 -3.292 0.948 -3.605 1.165 -3.912C1.311 -4.122 1.52 -4.289 1.869 -4.289C2.545 -4.289 2.692 -3.473 2.713 -3.027Z' id='g0-57'/>
+<path d='M1.339 -3.096H0.711V-2.469H1.339V-3.096ZM0.711 -0.628V0H1.339V-0.628H0.711Z' id='g0-58'/>
+<path d='M4.142 -0.614C4.038 -0.614 4.024 -0.614 3.968 -0.586C3.626 -0.467 3.271 -0.391 2.901 -0.391C1.778 -0.391 0.976 -1.339 0.976 -2.42C0.976 -3.592 1.883 -4.449 2.859 -4.449C3.055 -4.449 3.515 -4.4 3.745 -3.843C3.55 -3.954 3.333 -4.003 3.152 -4.003C2.406 -4.003 1.778 -3.306 1.778 -2.42C1.778 -1.513 2.427 -0.837 3.145 -0.837C3.689 -0.837 4.519 -1.276 4.519 -2.518C4.519 -3.222 4.47 -4.91 2.866 -4.91C1.541 -4.91 0.411 -3.815 0.411 -2.42C0.411 -1.039 1.527 0.07 2.873 0.07C3.515 0.07 4.115 -0.195 4.519 -0.614H4.142ZM3.152 -1.297C2.72 -1.297 2.343 -1.778 2.343 -2.42C2.343 -3.082 2.734 -3.543 3.145 -3.543C3.578 -3.543 3.954 -3.062 3.954 -2.42C3.954 -1.757 3.564 -1.297 3.152 -1.297Z' id='g0-64'/>
+<path d='M2.803 -4.84H2.127L0.209 0H0.781L1.325 -1.381H3.445L3.989 0H4.721L2.803 -4.84ZM2.392 -4.31L3.271 -1.792H1.499L2.392 -4.31Z' id='g0-65'/>
+<path d='M4.317 -0.851C3.829 -0.551 3.605 -0.418 2.908 -0.418C1.827 -0.418 1.172 -1.43 1.172 -2.434C1.172 -3.466 1.89 -4.435 2.908 -4.435C3.368 -4.435 3.843 -4.289 4.163 -4.045L4.275 -4.679C3.787 -4.861 3.396 -4.917 2.887 -4.917C1.506 -4.917 0.474 -3.773 0.474 -2.427C0.474 -0.99 1.569 0.07 2.929 0.07C3.612 0.07 3.898 -0.07 4.359 -0.321L4.317 -0.851Z' id='g0-67'/>
+<path d='M0.683 -4.84V0H2.664C3.919 0 4.931 -1.06 4.931 -2.378C4.931 -3.745 3.912 -4.84 2.664 -4.84H0.683ZM1.367 -0.411V-4.428H2.476C3.431 -4.428 4.247 -3.668 4.247 -2.385C4.247 -1.088 3.396 -0.411 2.476 -0.411H1.367Z' id='g0-68'/>
+<path d='M4.442 -2.085H2.88V-1.625H3.829V-0.558C3.522 -0.481 3.222 -0.418 2.908 -0.418C1.834 -0.418 1.172 -1.43 1.172 -2.427C1.172 -3.382 1.82 -4.435 2.873 -4.435C3.515 -4.435 3.919 -4.24 4.268 -3.947L4.38 -4.582C3.898 -4.812 3.473 -4.924 2.943 -4.924C1.534 -4.924 0.474 -3.822 0.474 -2.427C0.474 -1.067 1.527 0.07 2.901 0.07C3.403 0.07 3.996 -0.042 4.442 -0.272V-2.085Z' id='g0-71'/>
+<path d='M3.884 -2.42C3.801 -2.211 3.299 -0.983 3.229 -0.69H3.222C3.173 -0.893 2.852 -1.695 2.783 -1.876L1.583 -4.84H0.732V0H1.318V-4.282H1.325C1.381 -4.038 1.743 -3.117 1.778 -3.041L2.943 -0.146H3.494L4.533 -2.713C4.533 -2.72 5.014 -3.905 5.133 -4.289H5.14V0H5.726V-4.84H4.868L3.884 -2.42Z' id='g0-77'/>
+<path d='M1.646 -4.84H0.697V0H1.283V-4.289H1.29L3.578 0H4.526V-4.84H3.94V-0.551H3.933L1.646 -4.84Z' id='g0-78'/>
+<path d='M4.4 -4.84H3.794V-1.625C3.794 -0.69 3.166 -0.265 2.566 -0.265S1.381 -0.697 1.381 -1.618V-4.84H0.676V-1.632C0.676 -0.607 1.555 0.146 2.559 0.146C3.557 0.146 4.4 -0.614 4.4 -1.632V-4.84Z' id='g0-85'/>
+<path d='M2.971 -2.008C2.971 -2.72 2.427 -3.201 1.736 -3.201C1.297 -3.201 0.962 -3.11 0.572 -2.901L0.614 -2.392C0.844 -2.545 1.186 -2.755 1.736 -2.755C2.043 -2.755 2.364 -2.525 2.364 -2.001V-1.723C1.332 -1.688 0.314 -1.471 0.314 -0.823C0.314 -0.474 0.551 0.07 1.165 0.07C1.465 0.07 2.015 0.007 2.385 -0.265V0H2.971V-2.008ZM2.364 -0.99C2.364 -0.851 2.364 -0.669 2.12 -0.523C1.897 -0.398 1.625 -0.391 1.548 -0.391C1.165 -0.391 0.872 -0.565 0.872 -0.83C0.872 -1.276 2.05 -1.318 2.364 -1.332V-0.99Z' id='g0-97'/>
+<path d='M1.179 -4.84H0.593V0H1.2V-0.328C1.353 -0.195 1.688 0.07 2.197 0.07C2.957 0.07 3.571 -0.642 3.571 -1.555C3.571 -2.399 3.089 -3.166 2.392 -3.166C1.953 -3.166 1.527 -3.027 1.179 -2.769V-4.84ZM1.2 -2.197C1.2 -2.308 1.2 -2.392 1.444 -2.552C1.548 -2.615 1.736 -2.706 1.974 -2.706C2.441 -2.706 2.964 -2.392 2.964 -1.555C2.964 -0.704 2.385 -0.391 1.897 -0.391C1.639 -0.391 1.395 -0.509 1.2 -0.823V-2.197Z' id='g0-98'/>
+<path d='M3.034 -0.76C2.685 -0.537 2.308 -0.411 1.876 -0.411C1.234 -0.411 0.858 -0.928 0.858 -1.555C0.858 -2.092 1.137 -2.72 1.897 -2.72C2.371 -2.72 2.594 -2.622 2.95 -2.399L3.041 -2.901C2.622 -3.11 2.441 -3.201 1.897 -3.201C0.851 -3.201 0.251 -2.357 0.251 -1.548C0.251 -0.697 0.921 0.07 1.869 0.07C2.357 0.07 2.776 -0.077 3.075 -0.251L3.034 -0.76Z' id='g0-99'/>
+<path d='M3.229 -4.84H2.643V-2.797C2.197 -3.124 1.743 -3.166 1.541 -3.166C0.809 -3.166 0.251 -2.434 0.251 -1.548S0.802 0.07 1.52 0.07C1.953 0.07 2.357 -0.126 2.622 -0.363V0H3.229V-4.84ZM2.622 -0.865C2.448 -0.579 2.183 -0.391 1.848 -0.391C1.36 -0.391 0.858 -0.732 0.858 -1.541C0.858 -2.413 1.451 -2.706 1.925 -2.706C2.204 -2.706 2.441 -2.587 2.622 -2.35V-0.865Z' id='g0-100'/>
+<path d='M2.999 -0.76C2.608 -0.481 2.169 -0.391 1.869 -0.391C1.262 -0.391 0.802 -0.886 0.781 -1.527H3.068C3.068 -1.848 3.034 -2.315 2.762 -2.713C2.511 -3.068 2.092 -3.201 1.75 -3.201C0.9 -3.201 0.244 -2.455 0.244 -1.569C0.244 -0.676 0.941 0.07 1.862 0.07C2.267 0.07 2.685 -0.049 3.041 -0.265L2.999 -0.76ZM0.83 -1.946C0.99 -2.504 1.402 -2.741 1.75 -2.741C2.057 -2.741 2.511 -2.594 2.643 -1.946H0.83Z' id='g0-101'/>
+<path d='M3.508 -3.166C3.354 -3.166 2.887 -3.159 2.357 -2.957L2.343 -2.95C2.092 -3.117 1.848 -3.166 1.646 -3.166C0.962 -3.166 0.453 -2.629 0.453 -2.029C0.453 -1.785 0.537 -1.534 0.697 -1.339C0.6 -1.22 0.495 -1.025 0.495 -0.76C0.495 -0.488 0.607 -0.314 0.669 -0.23C0.286 -0.007 0.209 0.314 0.209 0.481C0.209 1.011 0.941 1.43 1.848 1.43C2.762 1.43 3.487 1.011 3.487 0.481C3.487 -0.502 2.267 -0.502 1.967 -0.502H1.318C1.206 -0.502 0.907 -0.502 0.907 -0.865C0.907 -1.004 0.955 -1.074 0.962 -1.088C1.206 -0.934 1.451 -0.886 1.639 -0.886C2.322 -0.886 2.831 -1.423 2.831 -2.022C2.831 -2.246 2.769 -2.448 2.643 -2.636C2.615 -2.678 2.615 -2.685 2.615 -2.692C2.615 -2.72 3.034 -2.72 3.068 -2.72C3.075 -2.72 3.34 -2.72 3.592 -2.692L3.508 -3.166ZM1.646 -1.318C1.269 -1.318 0.99 -1.555 0.99 -2.022C0.99 -2.566 1.339 -2.734 1.639 -2.734C2.015 -2.734 2.294 -2.497 2.294 -2.029C2.294 -1.485 1.946 -1.318 1.646 -1.318ZM1.974 0.042C2.134 0.042 2.957 0.042 2.957 0.488C2.957 0.788 2.434 0.997 1.848 0.997S0.739 0.788 0.739 0.488C0.739 0.453 0.739 0.042 1.304 0.042H1.974Z' id='g0-103'/>
+<path d='M3.243 -2.064C3.243 -2.608 3.082 -3.166 2.225 -3.166C1.625 -3.166 1.304 -2.817 1.165 -2.671V-4.84H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-2.064Z' id='g0-104'/>
+<path d='M1.227 -4.784H0.523V-4.08H1.227V-4.784ZM1.172 -3.096H0.586V0H1.172V-3.096Z' id='g0-105'/>
+<path d='M1.381 -4.784H0.676V-4.08H1.381V-4.784ZM-0.453 1.186C-0.133 1.36 0.181 1.423 0.446 1.423C0.928 1.423 1.381 1.053 1.381 0.411V-3.096H0.795V0.46C0.795 0.586 0.795 0.697 0.649 0.816C0.488 0.934 0.293 0.934 0.23 0.934C-0.063 0.934 -0.244 0.802 -0.328 0.725L-0.453 1.186Z' id='g0-106'/>
+<path d='M1.172 -4.84H0.586V0H1.172V-4.84Z' id='g0-108'/>
+<path d='M5.3 -2.064C5.3 -2.608 5.14 -3.166 4.282 -3.166C3.696 -3.166 3.333 -2.824 3.166 -2.601C3.096 -2.79 2.922 -3.166 2.225 -3.166C1.827 -3.166 1.444 -3.006 1.137 -2.636V-3.145H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-1.695C3.243 -2.155 3.438 -2.706 3.975 -2.706C4.693 -2.706 4.693 -2.218 4.693 -2.015V0H5.3V-2.064Z' id='g0-109'/>
+<path d='M3.243 -2.064C3.243 -2.608 3.082 -3.166 2.225 -3.166C1.827 -3.166 1.444 -3.006 1.137 -2.636V-3.145H0.579V0H1.186V-1.695C1.186 -2.155 1.381 -2.706 1.918 -2.706C2.636 -2.706 2.636 -2.218 2.636 -2.015V0H3.243V-2.064Z' id='g0-110'/>
+<path d='M3.487 -1.527C3.487 -2.448 2.755 -3.201 1.848 -3.201S0.209 -2.441 0.209 -1.527C0.209 -0.642 0.948 0.07 1.848 0.07C2.755 0.07 3.487 -0.642 3.487 -1.527ZM1.848 -0.411C1.297 -0.411 0.816 -0.816 0.816 -1.604S1.332 -2.741 1.848 -2.741C2.371 -2.741 2.88 -2.378 2.88 -1.604C2.88 -0.809 2.385 -0.411 1.848 -0.411Z' id='g0-111'/>
+<path d='M1.2 -0.328C1.569 0.007 1.967 0.07 2.204 0.07C2.943 0.07 3.571 -0.635 3.571 -1.555C3.571 -2.392 3.11 -3.166 2.42 -3.166C2.106 -3.166 1.583 -3.075 1.179 -2.762V-3.096H0.593V1.353H1.2V-0.328ZM1.2 -2.315C1.36 -2.511 1.632 -2.685 1.967 -2.685C2.525 -2.685 2.964 -2.169 2.964 -1.555C2.964 -0.865 2.441 -0.391 1.897 -0.391C1.792 -0.391 1.618 -0.404 1.437 -0.551C1.227 -0.711 1.2 -0.816 1.2 -0.948V-2.315Z' id='g0-112'/>
+<path d='M1.179 -1.485C1.179 -2.239 1.806 -2.643 2.42 -2.65V-3.166C1.834 -3.159 1.409 -2.873 1.13 -2.504V-3.145H0.593V0H1.179V-1.485Z' id='g0-114'/>
+<path d='M2.545 -2.985C2.071 -3.18 1.723 -3.201 1.471 -3.201C1.297 -3.201 0.244 -3.201 0.244 -2.273C0.244 -1.946 0.425 -1.764 0.516 -1.681C0.76 -1.437 1.053 -1.381 1.423 -1.311C1.75 -1.248 2.127 -1.179 2.127 -0.844C2.127 -0.404 1.548 -0.404 1.451 -0.404C1.004 -0.404 0.586 -0.565 0.307 -0.76L0.209 -0.237C0.446 -0.119 0.872 0.07 1.451 0.07C1.764 0.07 2.071 0.021 2.329 -0.167C2.587 -0.363 2.671 -0.669 2.671 -0.907C2.671 -1.032 2.657 -1.304 2.364 -1.569C2.106 -1.799 1.855 -1.848 1.52 -1.911C1.109 -1.988 0.788 -2.05 0.788 -2.357C0.788 -2.755 1.297 -2.755 1.402 -2.755C1.799 -2.755 2.106 -2.671 2.455 -2.49L2.545 -2.985Z' id='g0-115'/>
+<path d='M1.311 -2.657H2.343V-3.096H1.311V-3.982H0.774V-3.096H0.139V-2.657H0.753V-0.893C0.753 -0.425 0.872 0.07 1.374 0.07S2.26 -0.091 2.469 -0.188L2.35 -0.635C2.12 -0.467 1.876 -0.411 1.681 -0.411C1.388 -0.411 1.311 -0.697 1.311 -1.018V-2.657Z' id='g0-116'/>
+<path d='M3.243 -3.096H2.636V-1.074C2.636 -0.516 2.162 -0.342 1.757 -0.342C1.241 -0.342 1.186 -0.481 1.186 -0.802V-3.096H0.579V-0.76C0.579 -0.139 0.851 0.07 1.339 0.07C1.625 0.07 2.239 0.014 2.657 -0.321V0H3.243V-3.096Z' id='g0-117'/>
+<path d='M4.951 -3.096H4.407C4.345 -2.901 3.954 -1.723 3.738 -0.997C3.682 -0.795 3.612 -0.572 3.592 -0.411H3.585C3.543 -0.697 3.299 -1.451 3.285 -1.499L2.769 -3.096H2.239C2.036 -2.497 1.513 -0.934 1.458 -0.425H1.451C1.395 -0.921 0.879 -2.462 0.767 -2.797C0.711 -2.964 0.711 -2.978 0.676 -3.096H0.105L1.123 0H1.709C1.716 -0.028 1.904 -0.579 2.148 -1.353C2.253 -1.695 2.462 -2.364 2.497 -2.671L2.504 -2.678C2.518 -2.532 2.559 -2.378 2.608 -2.204S2.706 -1.841 2.755 -1.681L3.292 0H3.933L4.951 -3.096Z' id='g0-119'/>
+<path d='M1.932 -1.597L3.285 -3.096H2.671L1.681 -1.953L0.669 -3.096H0.042L1.437 -1.597L0 0H0.621L1.681 -1.311L2.783 0H3.41L1.932 -1.597Z' id='g0-120'/>
+<path d='M2.957 -2.803V-3.096H0.307V-2.65H1.332C1.416 -2.65 1.499 -2.657 1.583 -2.657H2.127L0.209 -0.307V0H2.978V-0.467H1.897C1.813 -0.467 1.73 -0.46 1.646 -0.46H1.039L2.957 -2.803Z' id='g0-122'/>
+</defs>
+<g id='page6'>
+<path d='M194.617 260.785V251.93M253.285 260.785V251.93M311.953 260.785V251.93M370.625 260.785V251.93M429.293 260.785V251.93M194.617 75.308V84.164M253.285 75.308V84.164M311.953 75.308V84.164M370.625 75.308V84.164M429.293 75.308V84.164' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M165.281 256.18V251.93M223.953 256.18V251.93M282.621 256.18V251.93M341.289 256.18V251.93M399.957 256.18V251.93M458.629 256.18V251.93M165.281 79.91V84.164M223.953 79.91V84.164M282.621 79.91V84.164M341.289 79.91V84.164M399.957 79.91V84.164M458.629 79.91V84.164' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 251.93H140.199M135.949 218.375H140.199M135.949 184.824H140.199M135.949 151.269H140.199M135.949 117.719H140.199M135.949 84.164H140.199M487.961 251.93H483.711M487.961 218.375H483.711M487.961 184.824H483.711M487.961 151.269H483.711M487.961 117.719H483.711M487.961 84.164H483.711' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 251.93V84.164H487.961V251.93H135.949Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -21.265 74.992)'>
+<use x='168.285' xlink:href='#g3-97' y='191.334'/>
+<use x='172.353' xlink:href='#g3-108' y='191.334'/>
+<use x='174.373' xlink:href='#g3-108' y='191.334'/>
+<use x='176.393' xlink:href='#g3-111' y='191.334'/>
+<use x='180.863' xlink:href='#g3-99' y='191.334'/>
+<use x='184.627' xlink:href='#g3-45' y='191.334'/>
+<use x='187.449' xlink:href='#g3-116' y='191.334'/>
+<use x='190.507' xlink:href='#g3-101' y='191.334'/>
+<use x='194.271' xlink:href='#g3-115' y='191.334'/>
+<use x='197.517' xlink:href='#g3-116' y='191.334'/>
+<use x='200.575' xlink:href='#g3-49' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 36.532 74.992)'>
+<use x='168.285' xlink:href='#g3-97' y='191.334'/>
+<use x='172.353' xlink:href='#g3-108' y='191.334'/>
+<use x='174.373' xlink:href='#g3-108' y='191.334'/>
+<use x='176.393' xlink:href='#g3-111' y='191.334'/>
+<use x='180.863' xlink:href='#g3-99' y='191.334'/>
+<use x='184.627' xlink:href='#g3-45' y='191.334'/>
+<use x='187.449' xlink:href='#g3-116' y='191.334'/>
+<use x='190.507' xlink:href='#g3-101' y='191.334'/>
+<use x='194.271' xlink:href='#g3-115' y='191.334'/>
+<use x='197.517' xlink:href='#g3-116' y='191.334'/>
+<use x='200.575' xlink:href='#g3-78' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 94.98 74.992)'>
+<use x='168.285' xlink:href='#g3-115' y='191.334'/>
+<use x='171.531' xlink:href='#g3-104' y='191.334'/>
+<use x='175.904' xlink:href='#g3-54' y='191.334'/>
+<use x='180.138' xlink:href='#g3-98' y='191.334'/>
+<use x='184.746' xlink:href='#g3-101' y='191.334'/>
+<use x='188.509' xlink:href='#g3-110' y='191.334'/>
+<use x='192.882' xlink:href='#g3-99' y='191.334'/>
+<use x='196.646' xlink:href='#g3-104' y='191.334'/>
+<use x='201.018' xlink:href='#g3-78' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 153.649 74.992)'>
+<use x='168.285' xlink:href='#g3-115' y='191.334'/>
+<use x='171.531' xlink:href='#g3-104' y='191.334'/>
+<use x='175.904' xlink:href='#g3-56' y='191.334'/>
+<use x='180.138' xlink:href='#g3-98' y='191.334'/>
+<use x='184.746' xlink:href='#g3-101' y='191.334'/>
+<use x='188.509' xlink:href='#g3-110' y='191.334'/>
+<use x='192.882' xlink:href='#g3-99' y='191.334'/>
+<use x='196.646' xlink:href='#g3-104' y='191.334'/>
+<use x='201.018' xlink:href='#g3-78' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 207.225 74.992)'>
+<use x='168.285' xlink:href='#g3-120' y='191.334'/>
+<use x='172.187' xlink:href='#g3-109' y='191.334'/>
+<use x='178.912' xlink:href='#g3-97' y='191.334'/>
+<use x='182.98' xlink:href='#g3-108' y='191.334'/>
+<use x='185' xlink:href='#g3-108' y='191.334'/>
+<use x='187.02' xlink:href='#g3-111' y='191.334'/>
+<use x='191.49' xlink:href='#g3-99' y='191.334'/>
+<use x='195.254' xlink:href='#g3-45' y='191.334'/>
+<use x='198.076' xlink:href='#g3-116' y='191.334'/>
+<use x='201.134' xlink:href='#g3-101' y='191.334'/>
+<use x='204.898' xlink:href='#g3-115' y='191.334'/>
+<use x='208.144' xlink:href='#g3-116' y='191.334'/>
+<use x='211.202' xlink:href='#g3-78' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 263.494 74.992)'>
+<use x='168.285' xlink:href='#g3-99' y='191.334'/>
+<use x='172.049' xlink:href='#g3-97' y='191.334'/>
+<use x='176.117' xlink:href='#g3-99' y='191.334'/>
+<use x='179.88' xlink:href='#g3-104' y='191.334'/>
+<use x='184.253' xlink:href='#g3-101' y='191.334'/>
+<use x='188.017' xlink:href='#g3-45' y='191.334'/>
+<use x='190.839' xlink:href='#g3-115' y='191.334'/>
+<use x='194.086' xlink:href='#g3-99' y='191.334'/>
+<use x='197.849' xlink:href='#g3-114' y='191.334'/>
+<use x='200.741' xlink:href='#g3-97' y='191.334'/>
+<use x='204.81' xlink:href='#g3-116' y='191.334'/>
+<use x='207.868' xlink:href='#g3-99' y='191.334'/>
+<use x='211.631' xlink:href='#g3-104' y='191.334'/>
+<use x='216.004' xlink:href='#g3-78' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 62.23)'>
+<use x='168.285' xlink:href='#g2-48' y='191.334'/>
+<use x='170.931' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 28.677)'>
+<use x='168.285' xlink:href='#g2-48' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-53' y='191.334'/>
+<use x='175.048' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -4.876)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -38.43)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-53' y='191.334'/>
+<use x='175.048' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -71.983)'>
+<use x='168.285' xlink:href='#g2-50' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -105.536)'>
+<use x='168.285' xlink:href='#g2-50' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-53' y='191.334'/>
+<use x='175.048' xlink:href='#g2-120' y='191.334'/>
+</g>
+<path clip-path='url(#clip6)' d='M135.949 184.824H487.961' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M142.742 251.93H145.981V184.824H142.742ZM201.41 251.93H204.649V184.824H201.41ZM260.078 251.93H263.317V184.824H260.078ZM318.75 251.93H321.988V184.824H318.75ZM377.418 251.93H380.656V184.824H377.418ZM436.086 251.93H439.324V184.824H436.086Z' fill='#993333'/>
+<path clip-path='url(#clip6)' d='M142.742 251.93H145.981V184.824H142.742ZM201.41 251.93H204.649V184.824H201.41ZM260.078 251.93H263.317V184.824H260.078ZM318.75 251.93H321.988V184.824H318.75ZM377.418 251.93H380.656V184.824H377.418ZM436.086 251.93H439.324V184.824H436.086Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M144.359 184.824V184.488' fill='#993333'/>
+<path clip-path='url(#clip6)' d='M144.359 184.824V184.488' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M142.367 184.489H146.355' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M144.359 184.824V185.16' fill='#993333'/>
+<path clip-path='url(#clip6)' d='M144.359 184.824V185.16' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M146.352 185.161H142.367' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M203.031 184.824V184.488' fill='#993333'/>
+<path clip-path='url(#clip6)' d='M203.031 184.824V184.488' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M201.039 184.489H205.023' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M203.031 184.824V185.16' fill='#993333'/>
+<path clip-path='url(#clip6)' d='M203.031 184.824V185.16' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M205.023 185.161H201.035' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M261.699 184.824V182.742' fill='#993333'/>
+<path clip-path='url(#clip6)' d='M261.699 184.824V182.742' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M259.707 182.742H263.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M261.699 184.824V186.902' fill='#993333'/>
+<path clip-path='url(#clip6)' d='M261.699 184.824V186.902' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M263.691 186.902H259.707' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M320.367 184.824V183.012' fill='#993333'/>
+<path clip-path='url(#clip6)' d='M320.367 184.824V183.012' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M318.375 183.012H322.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M320.367 184.824V186.637' fill='#993333'/>
+<path clip-path='url(#clip6)' d='M320.367 184.824V186.637' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M322.359 186.637H318.375' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M379.035 184.824V184.352' fill='#993333'/>
+<path clip-path='url(#clip6)' d='M379.035 184.824V184.352' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M377.043 184.351H381.031' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M379.035 184.824V185.293' fill='#993333'/>
+<path clip-path='url(#clip6)' d='M379.035 184.824V185.293' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M381.027 185.293H377.043' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M437.707 184.824V183.949' fill='#993333'/>
+<path clip-path='url(#clip6)' d='M437.707 184.824V183.949' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M435.715 183.95H439.699' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M437.707 184.824V185.695' fill='#993333'/>
+<path clip-path='url(#clip6)' d='M437.707 184.824V185.695' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M439.699 185.695H435.711' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M147.973 251.93H151.211V184.285H147.973ZM206.641 251.93H209.879V179.926H206.641ZM265.309 251.93H268.547V98.523H265.309ZM323.981 251.93H327.219V84.164H323.981ZM382.649 251.93H385.887V84.164H382.649ZM441.317 251.93H444.555V84.164H441.317Z' fill='#8080bf'/>
+<path clip-path='url(#clip6)' d='M147.973 251.93H151.211V184.285H147.973ZM206.641 251.93H209.879V179.926H206.641ZM265.309 251.93H268.547V98.523H265.309ZM323.981 251.93H327.219V84.164H323.981ZM382.649 251.93H385.887V84.164H382.649ZM441.317 251.93H444.555V84.164H441.317Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M149.59 184.285V183.949' fill='#8080bf'/>
+<path clip-path='url(#clip6)' d='M149.59 184.285V183.949' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M147.598 183.95H151.586' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M149.59 184.285V184.621' fill='#8080bf'/>
+<path clip-path='url(#clip6)' d='M149.59 184.285V184.621' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M151.582 184.621H147.597' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M208.262 179.926V179.523' fill='#8080bf'/>
+<path clip-path='url(#clip6)' d='M208.262 179.926V179.523' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M206.27 179.524H210.254' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M208.262 179.926V180.328' fill='#8080bf'/>
+<path clip-path='url(#clip6)' d='M208.262 179.926V180.328' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M210.253 180.328H206.265' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M266.93 98.523V94.902' fill='#8080bf'/>
+<path clip-path='url(#clip6)' d='M266.93 98.523V94.902' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M264.938 94.902H268.922' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M266.93 98.523V102.148' fill='#8080bf'/>
+<path clip-path='url(#clip6)' d='M266.93 98.523V102.148' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M268.921 102.148H264.937' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M325.598 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip6)' d='M323.606 84.164H327.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M325.598 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip6)' d='M323.606 84.164H327.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M384.266 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip6)' d='M382.274 84.164H386.262' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M384.266 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip6)' d='M382.274 84.164H386.262' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M442.938 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip6)' d='M440.946 84.164H444.93' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M442.938 84.164V84.164' fill='#8080bf'/>
+<path clip-path='url(#clip6)' d='M440.946 84.164H444.93' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M153.203 251.93H156.442V178.715H153.203ZM211.871 251.93H215.11V178.047H211.871ZM270.539 251.93H273.777V85.773H270.539ZM329.211 251.93H332.449V88.996H329.211ZM387.879 251.93H391.117V84.164H387.879ZM446.547 251.93H449.785V84.164H446.547Z' fill='#ffb733'/>
+<path clip-path='url(#clip6)' d='M153.203 251.93H156.442V178.715H153.203ZM211.871 251.93H215.11V178.047H211.871ZM270.539 251.93H273.777V85.773H270.539ZM329.211 251.93H332.449V88.996H329.211ZM387.879 251.93H391.117V84.164H387.879ZM446.547 251.93H449.785V84.164H446.547Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M154.82 178.715V178.047' fill='#ffb733'/>
+<path clip-path='url(#clip6)' d='M154.82 178.715V178.047' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M152.828 178.047H156.816' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M154.82 178.715V179.387' fill='#ffb733'/>
+<path clip-path='url(#clip6)' d='M154.82 178.715V179.387' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M156.813 179.386H152.828' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M213.492 178.047V177.574' fill='#ffb733'/>
+<path clip-path='url(#clip6)' d='M213.492 178.047V177.574' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M211.5 177.575H215.484' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M213.492 178.047V178.516' fill='#ffb733'/>
+<path clip-path='url(#clip6)' d='M213.492 178.047V178.516' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M215.484 178.516H211.496' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M272.16 85.773V83.558' fill='#ffb733'/>
+<path clip-path='url(#clip6)' d='M272.16 85.773V83.558' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M270.168 83.559H274.152' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M272.16 85.773V87.988' fill='#ffb733'/>
+<path clip-path='url(#clip6)' d='M272.16 85.773V87.988' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M274.152 87.988H270.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M330.828 88.996V86.715' fill='#ffb733'/>
+<path clip-path='url(#clip6)' d='M330.828 88.996V86.715' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M328.836 86.715H332.82' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M330.828 88.996V91.277' fill='#ffb733'/>
+<path clip-path='url(#clip6)' d='M330.828 88.996V91.277' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M332.82 91.277H328.836' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M389.496 84.164V84.164' fill='#ffb733'/>
+<path clip-path='url(#clip6)' d='M387.504 84.164H391.492' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M389.496 84.164V84.164' fill='#ffb733'/>
+<path clip-path='url(#clip6)' d='M387.504 84.164H391.492' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M448.168 84.164V84.164' fill='#ffb733'/>
+<path clip-path='url(#clip6)' d='M446.176 84.164H450.16' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M448.168 84.164V84.164' fill='#ffb733'/>
+<path clip-path='url(#clip6)' d='M446.176 84.164H450.16' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M158.434 251.93H161.672V155.164H158.434ZM217.102 251.93H220.34V156.305H217.102ZM275.77 251.93H279.008V84.164H275.77ZM334.442 251.93H337.68V84.164H334.442ZM393.109 251.93H396.348V131.609H393.109ZM451.777 251.93H455.016V183.949H451.777Z' fill='#bf80bf'/>
+<path clip-path='url(#clip6)' d='M158.434 251.93H161.672V155.164H158.434ZM217.102 251.93H220.34V156.305H217.102ZM275.77 251.93H279.008V84.164H275.77ZM334.442 251.93H337.68V84.164H334.442ZM393.109 251.93H396.348V131.609H393.109ZM451.777 251.93H455.016V183.949H451.777Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M160.051 155.164V154.758' fill='#bf80bf'/>
+<path clip-path='url(#clip6)' d='M160.051 155.164V154.758' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M158.058 154.758H162.046' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M160.051 155.164V155.566' fill='#bf80bf'/>
+<path clip-path='url(#clip6)' d='M160.051 155.164V155.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M162.043 155.567H158.058' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M218.723 156.305V156.102' fill='#bf80bf'/>
+<path clip-path='url(#clip6)' d='M218.723 156.305V156.102' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M216.73 156.102H220.714' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M218.723 156.305V156.504' fill='#bf80bf'/>
+<path clip-path='url(#clip6)' d='M218.723 156.305V156.504' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M220.714 156.504H216.726' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M277.391 84.164V84.164' fill='#bf80bf'/>
+<path clip-path='url(#clip6)' d='M275.398 84.164H279.382' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M277.391 84.164V84.164' fill='#bf80bf'/>
+<path clip-path='url(#clip6)' d='M275.398 84.164H279.382' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M336.059 84.164V84.164' fill='#bf80bf'/>
+<path clip-path='url(#clip6)' d='M334.066 84.164H338.05' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M336.059 84.164V84.164' fill='#bf80bf'/>
+<path clip-path='url(#clip6)' d='M334.066 84.164H338.05' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M394.727 131.609V131.406' fill='#bf80bf'/>
+<path clip-path='url(#clip6)' d='M394.727 131.609V131.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M392.734 131.407H396.722' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M394.727 131.609V131.808' fill='#bf80bf'/>
+<path clip-path='url(#clip6)' d='M394.727 131.609V131.808' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M396.718 131.808H392.734' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M453.399 183.949V182.809' fill='#bf80bf'/>
+<path clip-path='url(#clip6)' d='M453.399 183.949V182.809' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M451.406 182.809H455.39' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M453.399 183.949V185.09' fill='#bf80bf'/>
+<path clip-path='url(#clip6)' d='M453.399 183.949V185.09' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M455.39 185.089H451.402' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M163.664 251.93H166.903V180.664H163.664ZM222.332 251.93H225.57V183.078H222.332ZM281 251.93H284.238V174.355H281ZM339.672 251.93H342.91V183.144H339.672ZM398.34 251.93H401.578V198.914H398.34ZM457.008 251.93H460.246V184.555H457.008Z' fill='#dfbf9f'/>
+<path clip-path='url(#clip6)' d='M163.664 251.93H166.903V180.664H163.664ZM222.332 251.93H225.57V183.078H222.332ZM281 251.93H284.238V174.355H281ZM339.672 251.93H342.91V183.144H339.672ZM398.34 251.93H401.578V198.914H398.34ZM457.008 251.93H460.246V184.555H457.008Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M165.281 180.664V180.461' fill='#dfbf9f'/>
+<path clip-path='url(#clip6)' d='M165.281 180.664V180.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M163.289 180.461H167.277' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M165.281 180.664V180.863' fill='#dfbf9f'/>
+<path clip-path='url(#clip6)' d='M165.281 180.664V180.863' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M167.274 180.864H163.289' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M223.953 183.078V182.809' fill='#dfbf9f'/>
+<path clip-path='url(#clip6)' d='M223.953 183.078V182.809' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M221.961 182.809H225.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M223.953 183.078V183.348' fill='#dfbf9f'/>
+<path clip-path='url(#clip6)' d='M223.953 183.078V183.348' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M225.945 183.348H221.957' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M282.621 174.355V174.355' fill='#dfbf9f'/>
+<path clip-path='url(#clip6)' d='M280.629 174.355H284.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M282.621 174.355V174.355' fill='#dfbf9f'/>
+<path clip-path='url(#clip6)' d='M280.629 174.355H284.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M341.289 183.144V182.473' fill='#dfbf9f'/>
+<path clip-path='url(#clip6)' d='M341.289 183.144V182.473' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M339.297 182.472H343.281' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M341.289 183.144V183.816' fill='#dfbf9f'/>
+<path clip-path='url(#clip6)' d='M341.289 183.144V183.816' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M343.281 183.816H339.297' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M399.957 198.914V197.977' fill='#dfbf9f'/>
+<path clip-path='url(#clip6)' d='M399.957 198.914V197.977' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M397.965 197.977H401.953' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M399.957 198.914V199.855' fill='#dfbf9f'/>
+<path clip-path='url(#clip6)' d='M399.957 198.914V199.855' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M401.949 199.856H397.965' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M458.629 184.555V184.555' fill='#dfbf9f'/>
+<path clip-path='url(#clip6)' d='M456.637 184.555H460.621' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M458.629 184.555V184.555' fill='#dfbf9f'/>
+<path clip-path='url(#clip6)' d='M456.637 184.555H460.621' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M168.895 251.93H172.133V173.684H168.895ZM227.563 251.93H230.801V84.164H227.563ZM286.231 251.93H289.469V84.164H286.231ZM344.902 251.93H348.141V84.164H344.902ZM403.57 251.93H406.809V84.164H403.57ZM462.238 251.93H465.477V84.164H462.238Z' fill='#80bf80'/>
+<path clip-path='url(#clip6)' d='M168.895 251.93H172.133V173.684H168.895ZM227.563 251.93H230.801V84.164H227.563ZM286.231 251.93H289.469V84.164H286.231ZM344.902 251.93H348.141V84.164H344.902ZM403.57 251.93H406.809V84.164H403.57ZM462.238 251.93H465.477V84.164H462.238Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M170.512 173.684V173.348' fill='#80bf80'/>
+<path clip-path='url(#clip6)' d='M170.512 173.684V173.348' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M168.519 173.348H172.507' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M170.512 173.684V174.019' fill='#80bf80'/>
+<path clip-path='url(#clip6)' d='M170.512 173.684V174.019' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M172.504 174.02H168.519' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M229.184 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip6)' d='M227.191 84.164H231.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M229.184 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip6)' d='M227.191 84.164H231.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M287.852 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip6)' d='M285.859 84.164H289.843' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M287.852 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip6)' d='M285.859 84.164H289.843' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M346.52 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip6)' d='M344.527 84.164H348.511' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M346.52 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip6)' d='M344.527 84.164H348.511' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M405.188 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip6)' d='M403.195 84.164H407.183' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M405.188 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip6)' d='M403.195 84.164H407.183' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M463.859 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip6)' d='M461.867 84.164H465.851' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M463.859 84.164V84.164' fill='#80bf80'/>
+<path clip-path='url(#clip6)' d='M461.867 84.164H465.851' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M174.125 251.93H177.363V147.109H174.125ZM232.793 251.93H236.031V139.93H232.793ZM291.461 251.93H294.699V84.164H291.461ZM350.133 251.93H353.371V245.219H350.133ZM408.801 251.93H412.039V245.219H408.801ZM467.469 251.93H470.707V183.144H467.469Z' fill='#bfbf80'/>
+<path clip-path='url(#clip6)' d='M174.125 251.93H177.363V147.109H174.125ZM232.793 251.93H236.031V139.93H232.793ZM291.461 251.93H294.699V84.164H291.461ZM350.133 251.93H353.371V245.219H350.133ZM408.801 251.93H412.039V245.219H408.801ZM467.469 251.93H470.707V183.144H467.469Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M175.742 147.109V144.289' fill='#bfbf80'/>
+<path clip-path='url(#clip6)' d='M175.742 147.109V144.289' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M173.75 144.289H177.738' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M175.742 147.109V149.93' fill='#bfbf80'/>
+<path clip-path='url(#clip6)' d='M175.742 147.109V149.93' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M177.735 149.93H173.75' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M234.414 139.93V130.937' fill='#bfbf80'/>
+<path clip-path='url(#clip6)' d='M234.414 139.93V130.937' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M232.422 130.938H236.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M234.414 139.93V148.922' fill='#bfbf80'/>
+<path clip-path='url(#clip6)' d='M234.414 139.93V148.922' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M236.403 148.922H232.418' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M293.082 84.164V84.164' fill='#bfbf80'/>
+<path clip-path='url(#clip6)' d='M291.09 84.164H295.074' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M293.082 84.164V84.164' fill='#bfbf80'/>
+<path clip-path='url(#clip6)' d='M291.09 84.164H295.074' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M351.75 245.219V245.219' fill='#bfbf80'/>
+<path clip-path='url(#clip6)' d='M349.758 245.219H353.742' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M351.75 245.219V245.219' fill='#bfbf80'/>
+<path clip-path='url(#clip6)' d='M349.758 245.219H353.742' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M410.418 245.219V245.219' fill='#bfbf80'/>
+<path clip-path='url(#clip6)' d='M408.426 245.219H412.414' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M410.418 245.219V245.219' fill='#bfbf80'/>
+<path clip-path='url(#clip6)' d='M408.426 245.219H412.414' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M469.09 183.144V181.734' fill='#bfbf80'/>
+<path clip-path='url(#clip6)' d='M469.09 183.144V181.734' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M467.098 181.734H471.082' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M469.09 183.144V184.555' fill='#bfbf80'/>
+<path clip-path='url(#clip6)' d='M469.09 183.144V184.555' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M471.082 184.555H467.094' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M179.356 251.93H182.594V169.254H179.356ZM238.024 251.93H241.262V169.387H238.024ZM296.692 251.93H299.93V84.164H296.692ZM355.363 251.93H358.602V84.164H355.363ZM414.031 251.93H417.27V84.164H414.031ZM472.699 251.93H475.938V84.164H472.699Z' fill='#339999'/>
+<path clip-path='url(#clip6)' d='M179.356 251.93H182.594V169.254H179.356ZM238.024 251.93H241.262V169.387H238.024ZM296.692 251.93H299.93V84.164H296.692ZM355.363 251.93H358.602V84.164H355.363ZM414.031 251.93H417.27V84.164H414.031ZM472.699 251.93H475.938V84.164H472.699Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M180.973 169.254V168.719' fill='#339999'/>
+<path clip-path='url(#clip6)' d='M180.973 169.254V168.719' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M178.98 168.719H182.968' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M180.973 169.254V169.793' fill='#339999'/>
+<path clip-path='url(#clip6)' d='M180.973 169.254V169.793' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M182.965 169.793H178.98' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M239.645 169.387V169.187' fill='#339999'/>
+<path clip-path='url(#clip6)' d='M239.645 169.387V169.187' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M237.652 169.187H241.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M239.645 169.387V169.59' fill='#339999'/>
+<path clip-path='url(#clip6)' d='M239.645 169.387V169.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M241.633 169.59H237.648' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M298.313 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip6)' d='M296.32 84.164H300.304' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M298.313 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip6)' d='M296.32 84.164H300.304' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M356.981 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip6)' d='M354.988 84.164H358.972' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M356.981 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip6)' d='M354.988 84.164H358.972' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M415.649 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip6)' d='M413.656 84.164H417.644' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M415.649 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip6)' d='M413.656 84.164H417.644' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M474.32 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip6)' d='M472.328 84.164H476.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M474.32 84.164V84.164' fill='#339999'/>
+<path clip-path='url(#clip6)' d='M472.328 84.164H476.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M184.586 251.93H187.824V180.328H184.586ZM243.254 251.93H246.492V179.59H243.254ZM301.922 251.93H305.16V84.164H301.922ZM360.594 251.93H363.832V107.516H360.594ZM419.262 251.93H422.5V175.43H419.262ZM477.93 251.93H481.168V182.406H477.93Z' fill='#bf8080'/>
+<path clip-path='url(#clip6)' d='M184.586 251.93H187.824V180.328H184.586ZM243.254 251.93H246.492V179.59H243.254ZM301.922 251.93H305.16V84.164H301.922ZM360.594 251.93H363.832V107.516H360.594ZM419.262 251.93H422.5V175.43H419.262ZM477.93 251.93H481.168V182.406H477.93Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M186.203 180.328V179.992' fill='#bf8080'/>
+<path clip-path='url(#clip6)' d='M186.203 180.328V179.992' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M184.211 179.992H188.199' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M186.203 180.328V180.664' fill='#bf8080'/>
+<path clip-path='url(#clip6)' d='M186.203 180.328V180.664' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M188.196 180.664H184.211' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M244.875 179.59V179.187' fill='#bf8080'/>
+<path clip-path='url(#clip6)' d='M244.875 179.59V179.187' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M242.883 179.187H246.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M244.875 179.59V179.992' fill='#bf8080'/>
+<path clip-path='url(#clip6)' d='M244.875 179.59V179.992' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M246.864 179.992H242.879' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M303.543 84.164V84.164' fill='#bf8080'/>
+<path clip-path='url(#clip6)' d='M301.551 84.164H305.535' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M303.543 84.164V84.164' fill='#bf8080'/>
+<path clip-path='url(#clip6)' d='M301.551 84.164H305.535' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M362.211 107.516V104.094' fill='#bf8080'/>
+<path clip-path='url(#clip6)' d='M362.211 107.516V104.094' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M360.219 104.093H364.203' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M362.211 107.516V110.937' fill='#bf8080'/>
+<path clip-path='url(#clip6)' d='M362.211 107.516V110.937' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M364.203 110.937H360.219' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M420.879 175.43V175.16' fill='#bf8080'/>
+<path clip-path='url(#clip6)' d='M420.879 175.43V175.16' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M418.887 175.16H422.875' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M420.879 175.43V175.695' fill='#bf8080'/>
+<path clip-path='url(#clip6)' d='M420.879 175.43V175.695' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M422.871 175.696H418.887' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M479.551 182.406V181.199' fill='#bf8080'/>
+<path clip-path='url(#clip6)' d='M479.551 182.406V181.199' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M477.559 181.199H481.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M479.551 182.406V183.613' fill='#bf8080'/>
+<path clip-path='url(#clip6)' d='M479.551 182.406V183.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M481.543 183.613H477.555' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M420.539 312.98H487.762V278.973H420.539Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 255.575 118.327)'>
+<use x='168.285' xlink:href='#g2-114' y='167.424'/>
+<use x='170.092' xlink:href='#g2-53' y='167.424'/>
+<use x='172.739' xlink:href='#g2-97' y='167.424'/>
+<use x='175.281' xlink:href='#g2-45' y='167.424'/>
+<use x='177.046' xlink:href='#g2-49' y='167.424'/>
+<use x='179.692' xlink:href='#g2-50' y='167.424'/>
+<use x='182.338' xlink:href='#g2-120' y='167.424'/>
+<use x='184.777' xlink:href='#g2-108' y='167.424'/>
+<use x='186.04' xlink:href='#g2-97' y='167.424'/>
+<use x='188.435' xlink:href='#g2-114' y='167.424'/>
+<use x='190.243' xlink:href='#g2-103' y='167.424'/>
+<use x='192.889' xlink:href='#g2-101' y='167.424'/>
+<use x='195.241' xlink:href='#g2-44' y='167.424'/>
+<use x='198.476' xlink:href='#g2-51' y='167.424'/>
+<use x='201.122' xlink:href='#g2-56' y='167.424'/>
+<use x='203.769' xlink:href='#g2-52' y='167.424'/>
+<use x='206.415' xlink:href='#g2-71' y='167.424'/>
+<use x='209.943' xlink:href='#g2-98' y='167.424'/>
+<use x='168.285' xlink:href='#g2-52' y='173.401'/>
+<use x='170.931' xlink:href='#g2-56' y='173.401'/>
+<use x='175.342' xlink:href='#g2-112' y='173.401'/>
+<use x='177.928' xlink:href='#g2-114' y='173.401'/>
+<use x='179.735' xlink:href='#g2-111' y='173.401'/>
+<use x='182.529' xlink:href='#g2-99' y='173.401'/>
+<use x='186.645' xlink:href='#g2-65' y='173.401'/>
+<use x='190.174' xlink:href='#g2-77' y='173.401'/>
+<use x='194.792' xlink:href='#g2-68' y='173.401'/>
+<use x='200.378' xlink:href='#g2-64' y='173.401'/>
+<use x='203.907' xlink:href='#g2-50' y='173.401'/>
+<use x='206.553' xlink:href='#g2-46' y='173.401'/>
+<use x='208.024' xlink:href='#g2-53' y='173.401'/>
+<use x='210.67' xlink:href='#g2-71' y='173.401'/>
+<use x='214.198' xlink:href='#g2-104' y='173.401'/>
+<use x='216.931' xlink:href='#g2-122' y='173.401'/>
+<use x='168.285' xlink:href='#g2-85' y='179.379'/>
+<use x='171.917' xlink:href='#g2-98' y='179.379'/>
+<use x='174.65' xlink:href='#g2-117' y='179.379'/>
+<use x='177.383' xlink:href='#g2-110' y='179.379'/>
+<use x='180.116' xlink:href='#g2-116' y='179.379'/>
+<use x='182.027' xlink:href='#g2-117' y='179.379'/>
+<use x='186.524' xlink:href='#g2-49' y='179.379'/>
+<use x='189.17' xlink:href='#g2-56' y='179.379'/>
+<use x='191.817' xlink:href='#g2-46' y='179.379'/>
+<use x='193.287' xlink:href='#g2-48' y='179.379'/>
+<use x='195.933' xlink:href='#g2-52' y='179.379'/>
+<use x='198.58' xlink:href='#g2-46' y='179.379'/>
+<use x='200.05' xlink:href='#g2-49' y='179.379'/>
+<use x='202.696' xlink:href='#g2-44' y='179.379'/>
+<use x='205.931' xlink:href='#g2-71' y='179.379'/>
+<use x='209.459' xlink:href='#g2-67' y='179.379'/>
+<use x='212.841' xlink:href='#g2-67' y='179.379'/>
+<use x='217.986' xlink:href='#g2-55' y='179.379'/>
+<use x='220.633' xlink:href='#g2-46' y='179.379'/>
+<use x='222.103' xlink:href='#g2-52' y='179.379'/>
+<use x='224.749' xlink:href='#g2-46' y='179.379'/>
+<use x='226.219' xlink:href='#g2-48' y='179.379'/>
+<use x='168.285' xlink:href='#g2-50' y='185.357'/>
+<use x='170.931' xlink:href='#g2-52' y='185.357'/>
+<use x='175.342' xlink:href='#g2-99' y='185.357'/>
+<use x='177.694' xlink:href='#g2-111' y='185.357'/>
+<use x='180.193' xlink:href='#g2-114' y='185.357'/>
+<use x='182.001' xlink:href='#g2-101' y='185.357'/>
+<use x='184.353' xlink:href='#g2-115' y='185.357'/>
+<use x='188.146' xlink:href='#g2-111' y='185.357'/>
+<use x='190.793' xlink:href='#g2-110' y='185.357'/>
+<use x='195.29' xlink:href='#g2-51' y='185.357'/>
+<use x='199.7' xlink:href='#g2-110' y='185.357'/>
+<use x='202.433' xlink:href='#g2-117' y='185.357'/>
+<use x='205.166' xlink:href='#g2-109' y='185.357'/>
+<use x='209.369' xlink:href='#g2-97' y='185.357'/>
+<use x='213.676' xlink:href='#g2-110' y='185.357'/>
+<use x='216.409' xlink:href='#g2-111' y='185.357'/>
+<use x='219.202' xlink:href='#g2-100' y='185.357'/>
+<use x='221.935' xlink:href='#g2-101' y='185.357'/>
+<use x='224.287' xlink:href='#g2-115' y='185.357'/>
+<use x='168.285' xlink:href='#g2-50' y='191.334'/>
+<use x='170.931' xlink:href='#g2-48' y='191.334'/>
+<use x='173.578' xlink:href='#g2-50' y='191.334'/>
+<use x='176.224' xlink:href='#g2-48' y='191.334'/>
+<use x='178.87' xlink:href='#g2-45' y='191.334'/>
+<use x='180.635' xlink:href='#g2-48' y='191.334'/>
+<use x='183.281' xlink:href='#g2-49' y='191.334'/>
+<use x='185.927' xlink:href='#g2-45' y='191.334'/>
+<use x='187.692' xlink:href='#g2-49' y='191.334'/>
+<use x='190.338' xlink:href='#g2-54' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -45.339 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 13.33 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 71.999 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 130.668 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 189.337 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 248.006 346.904)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -40.108 346.367)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-49' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 18.561 342.005)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-55' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 77.23 260.606)'>
+<use x='168.285' xlink:href='#g2-50' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-50' y='191.334'/>
+<use x='175.048' xlink:href='#g2-57' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 135.899 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-53' y='191.334'/>
+<use x='176.558' xlink:href='#g2-54' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-54' y='191.334'/>
+<use x='183.321' xlink:href='#g2-52' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 194.568 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-49' y='191.334'/>
+<use x='176.558' xlink:href='#g2-55' y='191.334'/>
+<use x='179.205' xlink:href='#g2-50' y='191.334'/>
+<use x='181.851' xlink:href='#g2-46' y='191.334'/>
+<use x='183.321' xlink:href='#g2-48' y='191.334'/>
+<use x='185.968' xlink:href='#g2-51' y='191.334'/>
+<use x='188.614' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 253.236 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-49' y='191.334'/>
+<use x='176.558' xlink:href='#g2-55' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-50' y='191.334'/>
+<use x='183.321' xlink:href='#g2-57' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -34.878 340.797)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-57' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 23.791 340.126)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-49' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 82.46 247.855)'>
+<use x='168.285' xlink:href='#g2-50' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-52' y='191.334'/>
+<use x='175.048' xlink:href='#g2-56' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 141.129 251.077)'>
+<use x='168.285' xlink:href='#g2-50' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-52' y='191.334'/>
+<use x='175.048' xlink:href='#g2-51' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 199.798 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-52' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-55' y='191.334'/>
+<use x='180.675' xlink:href='#g2-55' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 258.467 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-49' y='191.334'/>
+<use x='176.558' xlink:href='#g2-55' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-49' y='191.334'/>
+<use x='183.321' xlink:href='#g2-48' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -29.648 317.243)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-52' y='191.334'/>
+<use x='175.048' xlink:href='#g2-52' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 29.021 318.384)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-52' y='191.334'/>
+<use x='175.048' xlink:href='#g2-50' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 87.69 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-51' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-52' y='191.334'/>
+<use x='180.675' xlink:href='#g2-56' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 146.359 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-51' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-57' y='191.334'/>
+<use x='180.675' xlink:href='#g2-51' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 205.028 293.689)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-55' y='191.334'/>
+<use x='175.048' xlink:href='#g2-57' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 263.697 346.032)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-49' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -24.417 342.743)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-54' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 34.252 345.159)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-51' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 92.921 336.435)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-49' y='191.334'/>
+<use x='175.048' xlink:href='#g2-54' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 151.59 345.226)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-50' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 210.259 360.996)'>
+<use x='168.285' xlink:href='#g2-48' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-55' y='191.334'/>
+<use x='175.048' xlink:href='#g2-57' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 268.928 346.636)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-48' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -19.187 335.764)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-49' y='191.334'/>
+<use x='175.048' xlink:href='#g2-55' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 39.482 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-53' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-51' y='191.334'/>
+<use x='180.675' xlink:href='#g2-53' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 98.151 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-49' y='191.334'/>
+<use x='176.558' xlink:href='#g2-51' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-57' y='191.334'/>
+<use x='183.321' xlink:href='#g2-50' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 156.82 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-53' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-54' y='191.334'/>
+<use x='180.675' xlink:href='#g2-48' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 215.489 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-55' y='191.334'/>
+<use x='176.558' xlink:href='#g2-49' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-54' y='191.334'/>
+<use x='183.321' xlink:href='#g2-57' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 274.158 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-51' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-53' y='191.334'/>
+<use x='180.675' xlink:href='#g2-54' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -13.956 309.19)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-53' y='191.334'/>
+<use x='175.048' xlink:href='#g2-54' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 44.713 302.01)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-54' y='191.334'/>
+<use x='175.048' xlink:href='#g2-55' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 103.382 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-49' y='191.334'/>
+<use x='176.558' xlink:href='#g2-51' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-51' y='191.334'/>
+<use x='183.321' xlink:href='#g2-55' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 161.523 407.299)'>
+<use x='168.285' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 220.192 407.299)'>
+<use x='168.285' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 279.389 345.226)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-51' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -8.726 331.335)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-50' y='191.334'/>
+<use x='175.048' xlink:href='#g2-51' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 49.943 331.47)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-50' y='191.334'/>
+<use x='175.048' xlink:href='#g2-51' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 108.612 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-49' y='191.334'/>
+<use x='176.558' xlink:href='#g2-55' y='191.334'/>
+<use x='179.205' xlink:href='#g2-46' y='191.334'/>
+<use x='180.675' xlink:href='#g2-55' y='191.334'/>
+<use x='183.321' xlink:href='#g2-52' y='191.334'/>
+<use x='185.968' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 167.281 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-52' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-57' y='191.334'/>
+<use x='180.675' xlink:href='#g2-57' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 225.95 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-51' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-53' y='191.334'/>
+<use x='180.675' xlink:href='#g2-53' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 284.619 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-51' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-53' y='191.334'/>
+<use x='180.675' xlink:href='#g2-57' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -3.495 342.408)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-55' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 55.174 341.67)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-56' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 113.843 246.245)'>
+<use x='163.396' xlink:href='#g4-1' y='191.334'/>
+<use x='166.901' xlink:href='#g4-1' y='191.334'/>
+<use x='170.407' xlink:href='#g4-1' y='191.334'/>
+<use x='173.912' xlink:href='#g2-51' y='191.334'/>
+<use x='176.558' xlink:href='#g2-46' y='191.334'/>
+<use x='178.029' xlink:href='#g2-50' y='191.334'/>
+<use x='180.675' xlink:href='#g2-48' y='191.334'/>
+<use x='183.321' xlink:href='#g2-120' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 172.512 269.598)'>
+<use x='168.285' xlink:href='#g2-50' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-49' y='191.334'/>
+<use x='175.048' xlink:href='#g2-53' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 231.181 337.509)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-49' y='191.334'/>
+<use x='175.048' xlink:href='#g2-52' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 289.85 344.488)'>
+<use x='168.285' xlink:href='#g2-49' y='191.334'/>
+<use x='170.931' xlink:href='#g2-46' y='191.334'/>
+<use x='172.401' xlink:href='#g2-48' y='191.334'/>
+<use x='175.048' xlink:href='#g2-52' y='191.334'/>
+</g>
+<g transform='matrix(0 -1 1 0 -78.176 391.591)'>
+<use x='168.285' xlink:href='#g1-82' y='191.334'/>
+<use x='174.255' xlink:href='#g1-101' y='191.334'/>
+<use x='178.351' xlink:href='#g1-108' y='191.334'/>
+<use x='180.551' xlink:href='#g1-97' y='191.334'/>
+<use x='184.979' xlink:href='#g1-116' y='191.334'/>
+<use x='188.307' xlink:href='#g1-105' y='191.334'/>
+<use x='190.507' xlink:href='#g1-118' y='191.334'/>
+<use x='194.755' xlink:href='#g1-101' y='191.334'/>
+<use x='201.922' xlink:href='#g1-116' y='191.334'/>
+<use x='205.25' xlink:href='#g1-105' y='191.334'/>
+<use x='207.45' xlink:href='#g1-109' y='191.334'/>
+<use x='214.77' xlink:href='#g1-101' y='191.334'/>
+<use x='221.937' xlink:href='#g3-40' y='191.334'/>
+<use x='225.231' xlink:href='#g3-108' y='191.334'/>
+<use x='227.251' xlink:href='#g3-111' y='191.334'/>
+<use x='231.25' xlink:href='#g3-119' y='191.334'/>
+<use x='236.799' xlink:href='#g3-101' y='191.334'/>
+<use x='240.562' xlink:href='#g3-114' y='191.334'/>
+<use x='246.277' xlink:href='#g3-105' y='191.334'/>
+<use x='248.297' xlink:href='#g3-115' y='191.334'/>
+<use x='254.366' xlink:href='#g3-98' y='191.334'/>
+<use x='258.974' xlink:href='#g3-101' y='191.334'/>
+<use x='262.738' xlink:href='#g3-116' y='191.334'/>
+<use x='265.796' xlink:href='#g3-116' y='191.334'/>
+<use x='268.854' xlink:href='#g3-101' y='191.334'/>
+<use x='272.618' xlink:href='#g3-114' y='191.334'/>
+<use x='275.51' xlink:href='#g3-41' y='191.334'/>
+</g>
+<path d='M136.149 312.324H341.363V282.324H136.149Z' fill='#ffffff'/>
+<path d='M136.149 312.324H341.363V282.324H136.149Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path d='M139.336 292.816H142.324V284.844H139.336ZM145.313 292.816H148.301V286.836H145.313Z' fill='#993333'/>
+<path d='M139.336 292.816H142.324V284.844H139.336ZM145.313 292.816H148.301V286.836H145.313Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -40.354 114.479)'>
+<use x='195.372' xlink:href='#g3-120' y='178.327'/>
+<use x='199.274' xlink:href='#g3-109' y='178.327'/>
+<use x='205.999' xlink:href='#g3-105' y='178.327'/>
+<use x='208.019' xlink:href='#g3-58' y='178.327'/>
+<use x='210.371' xlink:href='#g0-57' y='178.327'/>
+<use x='214.076' xlink:href='#g0-57' y='178.327'/>
+</g>
+<path d='M184.145 292.816H187.133V284.844H184.145ZM190.121 292.816H193.109V286.836H190.121Z' fill='#8080bf'/>
+<path d='M184.145 292.816H187.133V284.844H184.145ZM190.121 292.816H193.109V286.836H190.121Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.441 114.036)'>
+<use x='239.382' xlink:href='#g3-116' y='178.327'/>
+<use x='242.44' xlink:href='#g3-99' y='178.327'/>
+<use x='246.204' xlink:href='#g3-58' y='178.327'/>
+<use x='248.556' xlink:href='#g0-53' y='178.327'/>
+<use x='252.261' xlink:href='#g0-54' y='178.327'/>
+</g>
+<path d='M227.356 292.816H230.344V284.844H227.356ZM233.332 292.816H236.32V286.836H233.332Z' fill='#ffb733'/>
+<path d='M227.356 292.816H230.344V284.844H227.356ZM233.332 292.816H236.32V286.836H233.332Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.04 114.479)'>
+<use x='281.863' xlink:href='#g3-106' y='178.327'/>
+<use x='284.119' xlink:href='#g3-101' y='178.327'/>
+<use x='287.882' xlink:href='#g3-58' y='178.327'/>
+<use x='290.235' xlink:href='#g0-53' y='178.327'/>
+<use x='293.94' xlink:href='#g0-50' y='178.327'/>
+</g>
+<path d='M269.106 292.816H272.094V284.844H269.106ZM275.086 292.816H278.074V286.836H275.086Z' fill='#bf80bf'/>
+<path d='M269.106 292.816H272.094V284.844H269.106ZM275.086 292.816H278.074V286.836H275.086Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -39.932 114.515)'>
+<use x='321.287' xlink:href='#g3-116' y='178.327'/>
+<use x='324.345' xlink:href='#g3-98' y='178.327'/>
+<use x='328.717' xlink:href='#g3-98' y='178.327'/>
+<use x='333.09' xlink:href='#g3-58' y='178.327'/>
+<use x='335.442' xlink:href='#g0-53' y='178.327'/>
+<use x='339.147' xlink:href='#g0-51' y='178.327'/>
+</g>
+<path d='M306.203 292.816H309.191V284.844H306.203ZM312.18 292.816H315.168V286.836H312.18Z' fill='#dfbf9f'/>
+<path d='M306.203 292.816H309.191V284.844H306.203ZM312.18 292.816H315.168V286.836H312.18Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.662 114.036)'>
+<use x='356.019' xlink:href='#g3-114' y='178.327'/>
+<use x='358.911' xlink:href='#g3-112' y='178.327'/>
+<use x='363.284' xlink:href='#g3-58' y='178.327'/>
+<use x='365.636' xlink:href='#g0-56' y='178.327'/>
+<use x='369.341' xlink:href='#g0-54' y='178.327'/>
+</g>
+<path d='M139.336 305.82H142.324V297.852H139.336ZM145.313 305.82H148.301V299.844H145.313Z' fill='#80bf80'/>
+<path d='M139.336 305.82H142.324V297.852H139.336ZM145.313 305.82H148.301V299.844H145.313Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -43.882 114.515)'>
+<use x='195.372' xlink:href='#g3-104' y='191.334'/>
+<use x='199.744' xlink:href='#g3-111' y='191.334'/>
+<use x='203.978' xlink:href='#g3-97' y='191.334'/>
+<use x='207.811' xlink:href='#g3-114' y='191.334'/>
+<use x='210.703' xlink:href='#g3-100' y='191.334'/>
+<use x='215.076' xlink:href='#g3-58' y='191.334'/>
+<use x='217.428' xlink:href='#g0-51' y='191.334'/>
+<use x='221.133' xlink:href='#g0-51' y='191.334'/>
+</g>
+<path d='M184.145 305.82H187.133V297.852H184.145ZM190.121 305.82H193.109V299.844H190.121Z' fill='#bfbf80'/>
+<path d='M184.145 305.82H187.133V297.852H184.145ZM190.121 305.82H193.109V299.844H190.121Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -43.084 114.515)'>
+<use x='239.382' xlink:href='#g3-109' y='191.334'/>
+<use x='246.107' xlink:href='#g3-101' y='191.334'/>
+<use x='249.871' xlink:href='#g3-115' y='191.334'/>
+<use x='253.117' xlink:href='#g3-104' y='191.334'/>
+<use x='257.489' xlink:href='#g3-58' y='191.334'/>
+<use x='259.842' xlink:href='#g0-51' y='191.334'/>
+<use x='263.547' xlink:href='#g0-52' y='191.334'/>
+</g>
+<path d='M227.356 305.82H230.344V297.852H227.356ZM233.332 305.82H236.32V299.844H233.332Z' fill='#339999'/>
+<path d='M227.356 305.82H230.344V297.852H227.356ZM233.332 305.82H236.32V299.844H233.332Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -42.353 114.515)'>
+<use x='281.863' xlink:href='#g3-103' y='191.334'/>
+<use x='286.097' xlink:href='#g3-108' y='191.334'/>
+<use x='288.118' xlink:href='#g3-105' y='191.334'/>
+<use x='290.138' xlink:href='#g3-98' y='191.334'/>
+<use x='294.746' xlink:href='#g3-99' y='191.334'/>
+<use x='298.509' xlink:href='#g3-58' y='191.334'/>
+<use x='300.862' xlink:href='#g0-52' y='191.334'/>
+<use x='304.567' xlink:href='#g0-51' y='191.334'/>
+</g>
+<path d='M269.106 305.82H272.094V297.852H269.106ZM275.086 305.82H278.074V299.844H275.086Z' fill='#bf8080'/>
+<path d='M269.106 305.82H272.094V297.852H269.106ZM275.086 305.82H278.074V299.844H275.086Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -40.026 114.479)'>
+<use x='321.287' xlink:href='#g3-115' y='191.334'/>
+<use x='324.533' xlink:href='#g3-109' y='191.334'/>
+<use x='331.258' xlink:href='#g3-105' y='191.334'/>
+<use x='333.278' xlink:href='#g3-58' y='191.334'/>
+<use x='335.63' xlink:href='#g0-54' y='191.334'/>
+<use x='339.335' xlink:href='#g0-57' y='191.334'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='243.704pt' version='1.1' viewBox='106.737 54.995 381.623 243.704' width='381.623pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip2'>
+<path d='M135.949 249.281H487.961V81.515H135.949Z'/>
+</clipPath>
+<use id='g2-40' transform='scale(1.6)' xlink:href='#g1-40'/>
+<use id='g2-41' transform='scale(1.6)' xlink:href='#g1-41'/>
+<use id='g2-45' transform='scale(1.6)' xlink:href='#g1-45'/>
+<use id='g2-49' transform='scale(1.6)' xlink:href='#g1-49'/>
+<use id='g2-54' transform='scale(1.6)' xlink:href='#g1-54'/>
+<use id='g2-56' transform='scale(1.6)' xlink:href='#g1-56'/>
+<use id='g2-78' transform='scale(1.6)' xlink:href='#g1-78'/>
+<use id='g2-97' transform='scale(1.6)' xlink:href='#g1-97'/>
+<use id='g2-98' transform='scale(1.6)' xlink:href='#g1-98'/>
+<use id='g2-99' transform='scale(1.6)' xlink:href='#g1-99'/>
+<use id='g2-100' transform='scale(1.6)' xlink:href='#g1-100'/>
+<use id='g2-101' transform='scale(1.6)' xlink:href='#g1-101'/>
+<use id='g2-103' transform='scale(1.6)' xlink:href='#g1-103'/>
+<use id='g2-104' transform='scale(1.6)' xlink:href='#g1-104'/>
+<use id='g2-105' transform='scale(1.6)' xlink:href='#g1-105'/>
+<use id='g2-106' transform='scale(1.6)' xlink:href='#g1-106'/>
+<use id='g2-108' transform='scale(1.6)' xlink:href='#g1-108'/>
+<use id='g2-109' transform='scale(1.6)' xlink:href='#g1-109'/>
+<use id='g2-110' transform='scale(1.6)' xlink:href='#g1-110'/>
+<use id='g2-111' transform='scale(1.6)' xlink:href='#g1-111'/>
+<use id='g2-112' transform='scale(1.6)' xlink:href='#g1-112'/>
+<use id='g2-114' transform='scale(1.6)' xlink:href='#g1-114'/>
+<use id='g2-115' transform='scale(1.6)' xlink:href='#g1-115'/>
+<use id='g2-116' transform='scale(1.6)' xlink:href='#g1-116'/>
+<use id='g2-119' transform='scale(1.6)' xlink:href='#g1-119'/>
+<use id='g2-120' transform='scale(1.6)' xlink:href='#g1-120'/>
+<path d='M1.519 -3.736C1.435 -3.736 1.425 -3.736 1.365 -3.681C0.737 -3.133 0.418 -2.247 0.418 -1.245C0.418 -0.304 0.702 0.603 1.36 1.181C1.425 1.245 1.435 1.245 1.519 1.245H1.758C1.743 1.235 1.26 0.822 1.031 0.045C0.912 -0.344 0.852 -0.752 0.852 -1.245C0.852 -2.969 1.659 -3.651 1.758 -3.736H1.519Z' id='g1-40'/>
+<path d='M0.533 1.245C0.618 1.245 0.628 1.245 0.687 1.191C1.315 0.643 1.634 -0.244 1.634 -1.245C1.634 -2.187 1.35 -3.093 0.692 -3.671C0.628 -3.736 0.618 -3.736 0.533 -3.736H0.294C0.309 -3.726 0.792 -3.313 1.021 -2.535C1.141 -2.147 1.2 -1.738 1.2 -1.245C1.2 0.478 0.394 1.161 0.294 1.245H0.533Z' id='g1-41'/>
+<path d='M0.956 -0.005V-0.448H0.508V0H0.648L0.503 0.638H0.727L0.956 -0.005Z' id='g1-44'/>
+<path d='M1.465 -0.951V-1.265H0.06V-0.951H1.465Z' id='g1-45'/>
+<path d='M0.956 -0.448H0.508V0H0.956V-0.448Z' id='g1-46'/>
+<path d='M2.431 -1.619C2.431 -1.858 2.431 -2.441 2.197 -2.849C1.943 -3.298 1.559 -3.372 1.32 -3.372C1.096 -3.372 0.707 -3.303 0.458 -2.874C0.219 -2.476 0.209 -1.933 0.209 -1.619C0.209 -1.25 0.229 -0.797 0.438 -0.418C0.658 -0.015 1.026 0.105 1.32 0.105C1.818 0.105 2.092 -0.184 2.242 -0.498C2.416 -0.852 2.431 -1.31 2.431 -1.619ZM1.32 -0.224C1.111 -0.224 0.872 -0.344 0.747 -0.702C0.648 -1.006 0.643 -1.32 0.643 -1.684C0.643 -2.142 0.643 -3.044 1.32 -3.044S1.998 -2.142 1.998 -1.684C1.998 -1.355 1.998 -0.981 1.878 -0.663C1.738 -0.304 1.484 -0.224 1.32 -0.224Z' id='g1-48'/>
+<path d='M1.599 -3.372H1.489C1.166 -3.073 0.757 -3.054 0.458 -3.044V-2.73C0.653 -2.735 0.902 -2.745 1.151 -2.844V-0.314H0.488V0H2.262V-0.314H1.599V-3.372Z' id='g1-49'/>
+<path d='M1.41 -0.384C1.35 -0.384 1.29 -0.379 1.23 -0.379H0.663L1.435 -1.061C1.524 -1.141 1.768 -1.325 1.863 -1.405C2.082 -1.604 2.376 -1.863 2.376 -2.296C2.376 -2.859 1.958 -3.372 1.245 -3.372C0.717 -3.372 0.389 -3.088 0.219 -2.58L0.453 -2.286C0.568 -2.705 0.742 -3.029 1.176 -3.029C1.594 -3.029 1.913 -2.735 1.913 -2.286C1.913 -1.873 1.669 -1.639 1.37 -1.355C1.27 -1.255 1.001 -1.031 0.897 -0.932C0.752 -0.802 0.408 -0.468 0.264 -0.344V0H2.376V-0.384H1.41Z' id='g1-50'/>
+<path d='M0.498 -2.555C0.702 -2.954 1.061 -3.064 1.3 -3.064C1.594 -3.064 1.813 -2.894 1.813 -2.61C1.813 -2.346 1.634 -2.022 1.255 -1.958C1.23 -1.953 1.21 -1.953 0.882 -1.928V-1.599H1.27C1.743 -1.599 1.918 -1.225 1.918 -0.912C1.918 -0.523 1.679 -0.224 1.29 -0.224C0.936 -0.224 0.533 -0.394 0.284 -0.712L0.219 -0.389C0.508 -0.065 0.912 0.105 1.3 0.105C1.953 0.105 2.421 -0.384 2.421 -0.907C2.421 -1.315 2.092 -1.644 1.699 -1.758C2.077 -1.953 2.271 -2.286 2.271 -2.61C2.271 -3.034 1.838 -3.372 1.305 -3.372C0.867 -3.372 0.503 -3.143 0.294 -2.844L0.498 -2.555Z' id='g1-51'/>
+<path d='M1.973 -0.832H2.491V-1.161H1.973V-3.268H1.479L0.149 -1.161V-0.832H1.544V0H1.973V-0.832ZM0.573 -1.161C0.722 -1.395 1.579 -2.725 1.579 -3.024V-1.161H0.573Z' id='g1-52'/>
+<path d='M0.817 -2.924H2.197V-3.268H0.418V-1.405H0.782C0.902 -1.674 1.136 -1.793 1.36 -1.793C1.564 -1.793 1.873 -1.654 1.873 -1.021C1.873 -0.369 1.46 -0.224 1.205 -0.224C0.877 -0.224 0.558 -0.399 0.389 -0.682L0.199 -0.384C0.443 -0.08 0.812 0.105 1.205 0.105C1.863 0.105 2.376 -0.403 2.376 -1.011C2.376 -1.629 1.918 -2.122 1.37 -2.122C1.156 -2.122 0.966 -2.047 0.817 -1.923V-2.924Z' id='g1-53'/>
+<path d='M2.187 -3.273C1.918 -3.372 1.729 -3.372 1.634 -3.372C0.877 -3.372 0.219 -2.66 0.219 -1.609C0.219 -0.259 0.827 0.105 1.33 0.105C1.733 0.105 1.943 -0.085 2.097 -0.244C2.416 -0.583 2.421 -0.936 2.421 -1.111C2.421 -1.763 2.067 -2.306 1.584 -2.306C1.096 -2.306 0.832 -2.052 0.687 -1.908C0.752 -2.59 1.101 -3.064 1.639 -3.064C1.738 -3.064 1.938 -3.054 2.187 -2.959V-3.273ZM0.692 -1.096C0.692 -1.126 0.692 -1.2 0.697 -1.225C0.697 -1.564 0.912 -1.978 1.355 -1.978C1.963 -1.978 1.963 -1.28 1.963 -1.111C1.963 -0.922 1.963 -0.712 1.828 -0.503C1.709 -0.324 1.559 -0.224 1.33 -0.224C0.802 -0.224 0.717 -0.877 0.692 -1.096Z' id='g1-54'/>
+<path d='M1.23 -2.884C1.29 -2.884 1.35 -2.889 1.41 -2.889H2.037C1.28 -2.147 0.797 -1.106 0.797 0.05H1.265C1.265 -1.405 1.973 -2.451 2.421 -2.919V-3.268H0.219V-2.884H1.23Z' id='g1-55'/>
+<path d='M1.704 -1.763C2.032 -1.868 2.346 -2.132 2.346 -2.501C2.346 -2.954 1.913 -3.372 1.32 -3.372S0.294 -2.954 0.294 -2.501C0.294 -2.127 0.618 -1.863 0.936 -1.763C0.498 -1.629 0.219 -1.29 0.219 -0.907C0.219 -0.374 0.692 0.105 1.32 0.105S2.421 -0.374 2.421 -0.907C2.421 -1.29 2.137 -1.629 1.704 -1.763ZM1.32 -1.928C0.966 -1.928 0.677 -2.132 0.677 -2.496C0.677 -2.814 0.902 -3.064 1.32 -3.064C1.733 -3.064 1.963 -2.814 1.963 -2.496C1.963 -2.142 1.684 -1.928 1.32 -1.928ZM1.32 -0.224C0.976 -0.224 0.672 -0.443 0.672 -0.912C0.672 -1.36 0.961 -1.599 1.32 -1.599S1.968 -1.355 1.968 -0.912C1.968 -0.443 1.659 -0.224 1.32 -0.224Z' id='g1-56'/>
+<path d='M0.384 -0.125C0.628 0.055 0.852 0.105 1.086 0.105C1.783 0.105 2.421 -0.598 2.421 -1.669C2.421 -3.029 1.818 -3.372 1.34 -3.372C0.897 -3.372 0.692 -3.163 0.548 -3.019C0.229 -2.695 0.219 -2.351 0.219 -2.157C0.219 -1.514 0.568 -0.961 1.056 -0.961C1.619 -0.961 1.928 -1.335 1.953 -1.365C1.883 -0.573 1.494 -0.224 1.086 -0.224C0.827 -0.224 0.667 -0.319 0.553 -0.413L0.384 -0.125ZM1.938 -2.162C1.943 -2.132 1.943 -2.082 1.943 -2.052C1.943 -1.684 1.719 -1.29 1.285 -1.29C1.096 -1.29 0.946 -1.345 0.817 -1.549C0.687 -1.743 0.677 -1.933 0.677 -2.157C0.677 -2.351 0.677 -2.575 0.832 -2.795C0.936 -2.944 1.086 -3.064 1.335 -3.064C1.818 -3.064 1.923 -2.481 1.938 -2.162Z' id='g1-57'/>
+<path d='M2.959 -0.438C2.884 -0.438 2.874 -0.438 2.834 -0.418C2.59 -0.334 2.336 -0.279 2.072 -0.279C1.27 -0.279 0.697 -0.956 0.697 -1.729C0.697 -2.565 1.345 -3.178 2.042 -3.178C2.182 -3.178 2.511 -3.143 2.675 -2.745C2.535 -2.824 2.381 -2.859 2.252 -2.859C1.719 -2.859 1.27 -2.361 1.27 -1.729C1.27 -1.081 1.733 -0.598 2.247 -0.598C2.635 -0.598 3.228 -0.912 3.228 -1.798C3.228 -2.301 3.193 -3.507 2.047 -3.507C1.101 -3.507 0.294 -2.725 0.294 -1.729C0.294 -0.742 1.091 0.05 2.052 0.05C2.511 0.05 2.939 -0.139 3.228 -0.438H2.959ZM2.252 -0.927C1.943 -0.927 1.674 -1.27 1.674 -1.729C1.674 -2.202 1.953 -2.531 2.247 -2.531C2.555 -2.531 2.824 -2.187 2.824 -1.729C2.824 -1.255 2.545 -0.927 2.252 -0.927Z' id='g1-64'/>
+<path d='M2.002 -3.457H1.519L0.149 0H0.558L0.946 -0.986H2.461L2.849 0H3.372L2.002 -3.457ZM1.709 -3.078L2.336 -1.28H1.071L1.709 -3.078Z' id='g1-65'/>
+<path d='M3.083 -0.608C2.735 -0.394 2.575 -0.299 2.077 -0.299C1.305 -0.299 0.837 -1.021 0.837 -1.738C0.837 -2.476 1.35 -3.168 2.077 -3.168C2.406 -3.168 2.745 -3.064 2.974 -2.889L3.054 -3.342C2.705 -3.472 2.426 -3.512 2.062 -3.512C1.076 -3.512 0.339 -2.695 0.339 -1.733C0.339 -0.707 1.121 0.05 2.092 0.05C2.58 0.05 2.785 -0.05 3.113 -0.229L3.083 -0.608Z' id='g1-67'/>
+<path d='M0.488 -3.457V0H1.903C2.8 0 3.522 -0.757 3.522 -1.699C3.522 -2.675 2.795 -3.457 1.903 -3.457H0.488ZM0.976 -0.294V-3.163H1.768C2.451 -3.163 3.034 -2.62 3.034 -1.704C3.034 -0.777 2.426 -0.294 1.768 -0.294H0.976Z' id='g1-68'/>
+<path d='M2.725 -1.624V-1.953H0.986V-3.098H1.714C1.773 -3.098 1.833 -3.093 1.893 -3.093H2.874V-3.442H0.483V0H2.949V-0.389H2.501C2.082 -0.389 1.664 -0.379 1.245 -0.379H0.986V-1.624H2.725Z' id='g1-69'/>
+<path d='M3.173 -1.489H2.057V-1.161H2.735V-0.399C2.516 -0.344 2.301 -0.299 2.077 -0.299C1.31 -0.299 0.837 -1.021 0.837 -1.733C0.837 -2.416 1.3 -3.168 2.052 -3.168C2.511 -3.168 2.8 -3.029 3.049 -2.819L3.128 -3.273C2.785 -3.437 2.481 -3.517 2.102 -3.517C1.096 -3.517 0.339 -2.73 0.339 -1.733C0.339 -0.762 1.091 0.05 2.072 0.05C2.431 0.05 2.854 -0.03 3.173 -0.194V-1.489Z' id='g1-71'/>
+<path d='M2.775 -1.729C2.715 -1.579 2.356 -0.702 2.306 -0.493H2.301C2.267 -0.638 2.037 -1.21 1.988 -1.34L1.131 -3.457H0.523V0H0.941V-3.059H0.946C0.986 -2.884 1.245 -2.227 1.27 -2.172L2.102 -0.105H2.496L3.238 -1.938C3.238 -1.943 3.582 -2.79 3.666 -3.064H3.671V0H4.09V-3.457H3.477L2.775 -1.729Z' id='g1-77'/>
+<path d='M1.176 -3.457H0.498V0H0.917V-3.064H0.922L2.555 0H3.233V-3.457H2.814V-0.394H2.809L1.176 -3.457Z' id='g1-78'/>
+<path d='M3.143 -3.457H2.71V-1.161C2.71 -0.493 2.262 -0.189 1.833 -0.189S0.986 -0.498 0.986 -1.156V-3.457H0.483V-1.166C0.483 -0.433 1.111 0.105 1.828 0.105C2.54 0.105 3.143 -0.438 3.143 -1.166V-3.457Z' id='g1-85'/>
+<path d='M2.122 -1.435C2.122 -1.943 1.733 -2.286 1.24 -2.286C0.927 -2.286 0.687 -2.222 0.408 -2.072L0.438 -1.709C0.603 -1.818 0.847 -1.968 1.24 -1.968C1.46 -1.968 1.689 -1.803 1.689 -1.43V-1.23C0.951 -1.205 0.224 -1.051 0.224 -0.588C0.224 -0.339 0.394 0.05 0.832 0.05C1.046 0.05 1.44 0.005 1.704 -0.189V0H2.122V-1.435ZM1.689 -0.707C1.689 -0.608 1.689 -0.478 1.514 -0.374C1.355 -0.284 1.161 -0.279 1.106 -0.279C0.832 -0.279 0.623 -0.403 0.623 -0.593C0.623 -0.912 1.465 -0.941 1.689 -0.951V-0.707Z' id='g1-97'/>
+<path d='M0.842 -3.457H0.423V0H0.857V-0.234C0.966 -0.139 1.205 0.05 1.569 0.05C2.112 0.05 2.55 -0.458 2.55 -1.111C2.55 -1.714 2.207 -2.262 1.709 -2.262C1.395 -2.262 1.091 -2.162 0.842 -1.978V-3.457ZM0.857 -1.569C0.857 -1.649 0.857 -1.709 1.031 -1.823C1.106 -1.868 1.24 -1.933 1.41 -1.933C1.743 -1.933 2.117 -1.709 2.117 -1.111C2.117 -0.503 1.704 -0.279 1.355 -0.279C1.171 -0.279 0.996 -0.364 0.857 -0.588V-1.569Z' id='g1-98'/>
+<path d='M2.167 -0.543C1.918 -0.384 1.649 -0.294 1.34 -0.294C0.882 -0.294 0.613 -0.663 0.613 -1.111C0.613 -1.494 0.812 -1.943 1.355 -1.943C1.694 -1.943 1.853 -1.873 2.107 -1.714L2.172 -2.072C1.873 -2.222 1.743 -2.286 1.355 -2.286C0.608 -2.286 0.179 -1.684 0.179 -1.106C0.179 -0.498 0.658 0.05 1.335 0.05C1.684 0.05 1.983 -0.055 2.197 -0.179L2.167 -0.543Z' id='g1-99'/>
+<path d='M2.306 -3.457H1.888V-1.998C1.569 -2.232 1.245 -2.262 1.101 -2.262C0.578 -2.262 0.179 -1.738 0.179 -1.106S0.573 0.05 1.086 0.05C1.395 0.05 1.684 -0.09 1.873 -0.259V0H2.306V-3.457ZM1.873 -0.618C1.748 -0.413 1.559 -0.279 1.32 -0.279C0.971 -0.279 0.613 -0.523 0.613 -1.101C0.613 -1.724 1.036 -1.933 1.375 -1.933C1.574 -1.933 1.743 -1.848 1.873 -1.679V-0.618Z' id='g1-100'/>
+<path d='M2.142 -0.543C1.863 -0.344 1.549 -0.279 1.335 -0.279C0.902 -0.279 0.573 -0.633 0.558 -1.091H2.192C2.192 -1.32 2.167 -1.654 1.973 -1.938C1.793 -2.192 1.494 -2.286 1.25 -2.286C0.643 -2.286 0.174 -1.753 0.174 -1.121C0.174 -0.483 0.672 0.05 1.33 0.05C1.619 0.05 1.918 -0.035 2.172 -0.189L2.142 -0.543ZM0.593 -1.39C0.707 -1.788 1.001 -1.958 1.25 -1.958C1.469 -1.958 1.793 -1.853 1.888 -1.39H0.593Z' id='g1-101'/>
+<path d='M2.506 -2.262C2.396 -2.262 2.062 -2.257 1.684 -2.112L1.674 -2.107C1.494 -2.227 1.32 -2.262 1.176 -2.262C0.687 -2.262 0.324 -1.878 0.324 -1.45C0.324 -1.275 0.384 -1.096 0.498 -0.956C0.428 -0.872 0.354 -0.732 0.354 -0.543C0.354 -0.349 0.433 -0.224 0.478 -0.164C0.204 -0.005 0.149 0.224 0.149 0.344C0.149 0.722 0.672 1.021 1.32 1.021C1.973 1.021 2.491 0.722 2.491 0.344C2.491 -0.359 1.619 -0.359 1.405 -0.359H0.941C0.862 -0.359 0.648 -0.359 0.648 -0.618C0.648 -0.717 0.682 -0.767 0.687 -0.777C0.862 -0.667 1.036 -0.633 1.171 -0.633C1.659 -0.633 2.022 -1.016 2.022 -1.445C2.022 -1.604 1.978 -1.748 1.888 -1.883C1.868 -1.913 1.868 -1.918 1.868 -1.923C1.868 -1.943 2.167 -1.943 2.192 -1.943C2.197 -1.943 2.386 -1.943 2.565 -1.923L2.506 -2.262ZM1.176 -0.941C0.907 -0.941 0.707 -1.111 0.707 -1.445C0.707 -1.833 0.956 -1.953 1.171 -1.953C1.44 -1.953 1.639 -1.783 1.639 -1.45C1.639 -1.061 1.39 -0.941 1.176 -0.941ZM1.41 0.03C1.524 0.03 2.112 0.03 2.112 0.349C2.112 0.563 1.738 0.712 1.32 0.712S0.528 0.563 0.528 0.349C0.528 0.324 0.528 0.03 0.932 0.03H1.41Z' id='g1-103'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.161 -2.262 0.932 -2.012 0.832 -1.908V-3.457H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-104'/>
+<path d='M0.877 -3.417H0.374V-2.914H0.877V-3.417ZM0.837 -2.212H0.418V0H0.837V-2.212Z' id='g1-105'/>
+<path d='M0.986 -3.417H0.483V-2.914H0.986V-3.417ZM-0.324 0.847C-0.095 0.971 0.13 1.016 0.319 1.016C0.663 1.016 0.986 0.752 0.986 0.294V-2.212H0.568V0.329C0.568 0.418 0.568 0.498 0.463 0.583C0.349 0.667 0.209 0.667 0.164 0.667C-0.045 0.667 -0.174 0.573 -0.234 0.518L-0.324 0.847Z' id='g1-106'/>
+<path d='M0.837 -3.457H0.418V0H0.837V-3.457Z' id='g1-108'/>
+<path d='M3.786 -1.474C3.786 -1.863 3.671 -2.262 3.059 -2.262C2.64 -2.262 2.381 -2.017 2.262 -1.858C2.212 -1.993 2.087 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.21C2.316 -1.539 2.456 -1.933 2.839 -1.933C3.352 -1.933 3.352 -1.584 3.352 -1.44V0H3.786V-1.474Z' id='g1-109'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-110'/>
+<path d='M2.491 -1.091C2.491 -1.748 1.968 -2.286 1.32 -2.286S0.149 -1.743 0.149 -1.091C0.149 -0.458 0.677 0.05 1.32 0.05C1.968 0.05 2.491 -0.458 2.491 -1.091ZM1.32 -0.294C0.927 -0.294 0.583 -0.583 0.583 -1.146S0.951 -1.958 1.32 -1.958C1.694 -1.958 2.057 -1.699 2.057 -1.146C2.057 -0.578 1.704 -0.294 1.32 -0.294Z' id='g1-111'/>
+<path d='M0.857 -0.234C1.121 0.005 1.405 0.05 1.574 0.05C2.102 0.05 2.55 -0.453 2.55 -1.111C2.55 -1.709 2.222 -2.262 1.729 -2.262C1.504 -2.262 1.131 -2.197 0.842 -1.973V-2.212H0.423V0.966H0.857V-0.234ZM0.857 -1.654C0.971 -1.793 1.166 -1.918 1.405 -1.918C1.803 -1.918 2.117 -1.549 2.117 -1.111C2.117 -0.618 1.743 -0.279 1.355 -0.279C1.28 -0.279 1.156 -0.289 1.026 -0.394C0.877 -0.508 0.857 -0.583 0.857 -0.677V-1.654Z' id='g1-112'/>
+<path d='M0.842 -1.061C0.842 -1.599 1.29 -1.888 1.729 -1.893V-2.262C1.31 -2.257 1.006 -2.052 0.807 -1.788V-2.247H0.423V0H0.842V-1.061Z' id='g1-114'/>
+<path d='M1.818 -2.132C1.479 -2.271 1.23 -2.286 1.051 -2.286C0.927 -2.286 0.174 -2.286 0.174 -1.624C0.174 -1.39 0.304 -1.26 0.369 -1.2C0.543 -1.026 0.752 -0.986 1.016 -0.936C1.25 -0.892 1.519 -0.842 1.519 -0.603C1.519 -0.289 1.106 -0.289 1.036 -0.289C0.717 -0.289 0.418 -0.403 0.219 -0.543L0.149 -0.169C0.319 -0.085 0.623 0.05 1.036 0.05C1.26 0.05 1.479 0.015 1.664 -0.12C1.848 -0.259 1.908 -0.478 1.908 -0.648C1.908 -0.737 1.898 -0.932 1.689 -1.121C1.504 -1.285 1.325 -1.32 1.086 -1.365C0.792 -1.42 0.563 -1.465 0.563 -1.684C0.563 -1.968 0.927 -1.968 1.001 -1.968C1.285 -1.968 1.504 -1.908 1.753 -1.778L1.818 -2.132Z' id='g1-115'/>
+<path d='M0.936 -1.898H1.674V-2.212H0.936V-2.844H0.553V-2.212H0.1V-1.898H0.538V-0.638C0.538 -0.304 0.623 0.05 0.981 0.05S1.614 -0.065 1.763 -0.134L1.679 -0.453C1.514 -0.334 1.34 -0.294 1.2 -0.294C0.991 -0.294 0.936 -0.498 0.936 -0.727V-1.898Z' id='g1-116'/>
+<path d='M2.316 -2.212H1.883V-0.767C1.883 -0.369 1.544 -0.244 1.255 -0.244C0.887 -0.244 0.847 -0.344 0.847 -0.573V-2.212H0.413V-0.543C0.413 -0.1 0.608 0.05 0.956 0.05C1.161 0.05 1.599 0.01 1.898 -0.229V0H2.316V-2.212Z' id='g1-117'/>
+<path d='M3.537 -2.212H3.148C3.103 -2.072 2.824 -1.23 2.67 -0.712C2.63 -0.568 2.58 -0.408 2.565 -0.294H2.56C2.531 -0.498 2.356 -1.036 2.346 -1.071L1.978 -2.212H1.599C1.455 -1.783 1.081 -0.667 1.041 -0.304H1.036C0.996 -0.658 0.628 -1.758 0.548 -1.998C0.508 -2.117 0.508 -2.127 0.483 -2.212H0.075L0.802 0H1.22C1.225 -0.02 1.36 -0.413 1.534 -0.966C1.609 -1.21 1.758 -1.689 1.783 -1.908L1.788 -1.913C1.798 -1.808 1.828 -1.699 1.863 -1.574S1.933 -1.315 1.968 -1.2L2.351 0H2.809L3.537 -2.212Z' id='g1-119'/>
+<path d='M1.38 -1.141L2.346 -2.212H1.908L1.2 -1.395L0.478 -2.212H0.03L1.026 -1.141L0 0H0.443L1.2 -0.936L1.988 0H2.436L1.38 -1.141Z' id='g1-120'/>
+<path d='M2.361 -2.212H1.958C1.554 -1.26 1.275 -0.618 1.255 -0.314C1.245 -0.453 1.156 -0.702 1.066 -0.936C0.986 -1.136 0.907 -1.335 0.817 -1.529L0.503 -2.212H0.075L1.106 0C1.046 0.144 0.946 0.374 0.917 0.438C0.812 0.648 0.742 0.717 0.608 0.717C0.588 0.717 0.403 0.717 0.189 0.628L0.219 0.976C0.264 0.986 0.448 1.016 0.603 1.016C0.802 1.016 0.981 0.941 1.191 0.463L2.361 -2.212Z' id='g1-121'/>
+<path d='M2.112 -2.002V-2.212H0.219V-1.893H0.951C1.011 -1.893 1.071 -1.898 1.131 -1.898H1.519L0.149 -0.219V0H2.127V-0.334H1.355C1.295 -0.334 1.235 -0.329 1.176 -0.329H0.742L2.112 -2.002Z' id='g1-122'/>
+<path d='M1.445 -1.245C1.445 -1.41 1.305 -1.549 1.141 -1.549S0.837 -1.41 0.837 -1.245S0.976 -0.941 1.141 -0.941S1.445 -1.081 1.445 -1.245Z' id='g3-1'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g0-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g0-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g0-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g0-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g0-108'/>
+<path d='M6.581 -2.663C6.581 -3.327 6.402 -4.08 5.317 -4.08C4.564 -4.08 4.142 -3.622 3.927 -3.344C3.865 -3.524 3.676 -4.08 2.762 -4.08C2.053 -4.08 1.623 -3.667 1.417 -3.398V-4.035H0.726V0H1.47V-2.188C1.47 -2.78 1.704 -3.497 2.385 -3.497C3.282 -3.497 3.282 -2.86 3.282 -2.6V0H4.026V-2.188C4.026 -2.78 4.259 -3.497 4.94 -3.497C5.837 -3.497 5.837 -2.86 5.837 -2.6V0H6.581V-2.663Z' id='g0-109'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g0-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g0-118'/>
+</defs>
+<g id='page2'>
+<path d='M194.617 258.136V249.281M253.285 258.136V249.281M311.953 258.136V249.281M370.625 258.136V249.281M429.293 258.136V249.281M194.617 72.66V81.515M253.285 72.66V81.515M311.953 72.66V81.515M370.625 72.66V81.515M429.293 72.66V81.515' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M165.281 253.535V249.281M223.953 253.535V249.281M282.621 253.535V249.281M341.289 253.535V249.281M399.957 253.535V249.281M458.629 253.535V249.281M165.281 77.265V81.515M223.953 77.265V81.515M282.621 77.265V81.515M341.289 77.265V81.515M399.957 77.265V81.515M458.629 77.265V81.515' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 249.281H140.199M135.949 215.73H140.199M135.949 182.176H140.199M135.949 148.625H140.199M135.949 115.07H140.199M135.949 81.515H140.199M487.961 249.281H483.711M487.961 215.73H483.711M487.961 182.176H483.711M487.961 148.625H483.711M487.961 115.07H483.711M487.961 81.515H483.711' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 249.281V81.515H487.961V249.281H135.949Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -21.265 61.588)'>
+<use x='168.285' xlink:href='#g2-97' y='201.694'/>
+<use x='172.353' xlink:href='#g2-108' y='201.694'/>
+<use x='174.373' xlink:href='#g2-108' y='201.694'/>
+<use x='176.393' xlink:href='#g2-111' y='201.694'/>
+<use x='180.863' xlink:href='#g2-99' y='201.694'/>
+<use x='184.627' xlink:href='#g2-45' y='201.694'/>
+<use x='187.449' xlink:href='#g2-116' y='201.694'/>
+<use x='190.507' xlink:href='#g2-101' y='201.694'/>
+<use x='194.271' xlink:href='#g2-115' y='201.694'/>
+<use x='197.517' xlink:href='#g2-116' y='201.694'/>
+<use x='200.575' xlink:href='#g2-49' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 36.532 61.588)'>
+<use x='168.285' xlink:href='#g2-97' y='201.694'/>
+<use x='172.353' xlink:href='#g2-108' y='201.694'/>
+<use x='174.373' xlink:href='#g2-108' y='201.694'/>
+<use x='176.393' xlink:href='#g2-111' y='201.694'/>
+<use x='180.863' xlink:href='#g2-99' y='201.694'/>
+<use x='184.627' xlink:href='#g2-45' y='201.694'/>
+<use x='187.449' xlink:href='#g2-116' y='201.694'/>
+<use x='190.507' xlink:href='#g2-101' y='201.694'/>
+<use x='194.271' xlink:href='#g2-115' y='201.694'/>
+<use x='197.517' xlink:href='#g2-116' y='201.694'/>
+<use x='200.575' xlink:href='#g2-78' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 94.98 61.588)'>
+<use x='168.285' xlink:href='#g2-115' y='201.694'/>
+<use x='171.531' xlink:href='#g2-104' y='201.694'/>
+<use x='175.904' xlink:href='#g2-54' y='201.694'/>
+<use x='180.138' xlink:href='#g2-98' y='201.694'/>
+<use x='184.746' xlink:href='#g2-101' y='201.694'/>
+<use x='188.509' xlink:href='#g2-110' y='201.694'/>
+<use x='192.882' xlink:href='#g2-99' y='201.694'/>
+<use x='196.646' xlink:href='#g2-104' y='201.694'/>
+<use x='201.018' xlink:href='#g2-78' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 153.649 61.588)'>
+<use x='168.285' xlink:href='#g2-115' y='201.694'/>
+<use x='171.531' xlink:href='#g2-104' y='201.694'/>
+<use x='175.904' xlink:href='#g2-56' y='201.694'/>
+<use x='180.138' xlink:href='#g2-98' y='201.694'/>
+<use x='184.746' xlink:href='#g2-101' y='201.694'/>
+<use x='188.509' xlink:href='#g2-110' y='201.694'/>
+<use x='192.882' xlink:href='#g2-99' y='201.694'/>
+<use x='196.646' xlink:href='#g2-104' y='201.694'/>
+<use x='201.018' xlink:href='#g2-78' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 207.225 61.588)'>
+<use x='168.285' xlink:href='#g2-120' y='201.694'/>
+<use x='172.187' xlink:href='#g2-109' y='201.694'/>
+<use x='178.912' xlink:href='#g2-97' y='201.694'/>
+<use x='182.98' xlink:href='#g2-108' y='201.694'/>
+<use x='185' xlink:href='#g2-108' y='201.694'/>
+<use x='187.02' xlink:href='#g2-111' y='201.694'/>
+<use x='191.49' xlink:href='#g2-99' y='201.694'/>
+<use x='195.254' xlink:href='#g2-45' y='201.694'/>
+<use x='198.076' xlink:href='#g2-116' y='201.694'/>
+<use x='201.134' xlink:href='#g2-101' y='201.694'/>
+<use x='204.898' xlink:href='#g2-115' y='201.694'/>
+<use x='208.144' xlink:href='#g2-116' y='201.694'/>
+<use x='211.202' xlink:href='#g2-78' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 263.494 61.588)'>
+<use x='168.285' xlink:href='#g2-99' y='201.694'/>
+<use x='172.049' xlink:href='#g2-97' y='201.694'/>
+<use x='176.117' xlink:href='#g2-99' y='201.694'/>
+<use x='179.88' xlink:href='#g2-104' y='201.694'/>
+<use x='184.253' xlink:href='#g2-101' y='201.694'/>
+<use x='188.017' xlink:href='#g2-45' y='201.694'/>
+<use x='190.839' xlink:href='#g2-115' y='201.694'/>
+<use x='194.086' xlink:href='#g2-99' y='201.694'/>
+<use x='197.849' xlink:href='#g2-114' y='201.694'/>
+<use x='200.741' xlink:href='#g2-97' y='201.694'/>
+<use x='204.81' xlink:href='#g2-116' y='201.694'/>
+<use x='207.868' xlink:href='#g2-99' y='201.694'/>
+<use x='211.631' xlink:href='#g2-104' y='201.694'/>
+<use x='216.004' xlink:href='#g2-78' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 49.223)'>
+<use x='168.285' xlink:href='#g1-48' y='201.694'/>
+<use x='170.931' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 15.67)'>
+<use x='168.285' xlink:href='#g1-48' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-53' y='201.694'/>
+<use x='175.048' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -17.883)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -51.436)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-53' y='201.694'/>
+<use x='175.048' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -84.989)'>
+<use x='168.285' xlink:href='#g1-50' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -118.543)'>
+<use x='168.285' xlink:href='#g1-50' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-53' y='201.694'/>
+<use x='175.048' xlink:href='#g1-120' y='201.694'/>
+</g>
+<path clip-path='url(#clip2)' d='M135.949 182.176H487.961' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M145.356 249.281H148.594V182.176H145.356ZM204.028 249.281H207.266V182.176H204.028ZM262.695 249.281H265.934V182.176H262.695ZM321.363 249.281H324.602V182.176H321.363ZM380.031 249.281H383.27V182.176H380.031ZM438.703 249.281H441.942V182.176H438.703Z' fill='#e0e0f0'/>
+<path clip-path='url(#clip2)' d='M145.356 249.281H148.594V182.176H145.356ZM204.028 249.281H207.266V182.176H204.028ZM262.695 249.281H265.934V182.176H262.695ZM321.363 249.281H324.602V182.176H321.363ZM380.031 249.281H383.27V182.176H380.031ZM438.703 249.281H441.942V182.176H438.703Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M146.977 182.176V182.043' fill='#e0e0f0'/>
+<path clip-path='url(#clip2)' d='M146.977 182.176V182.043' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M144.984 182.043H148.968' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M146.977 182.176V182.308' fill='#e0e0f0'/>
+<path clip-path='url(#clip2)' d='M146.977 182.176V182.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M148.969 182.308H144.984' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M205.645 182.176V182.043' fill='#e0e0f0'/>
+<path clip-path='url(#clip2)' d='M205.645 182.176V182.043' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M203.652 182.043H207.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M205.645 182.176V182.308' fill='#e0e0f0'/>
+<path clip-path='url(#clip2)' d='M205.645 182.176V182.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M207.636 182.308H203.652' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M264.313 182.176V180.429' fill='#e0e0f0'/>
+<path clip-path='url(#clip2)' d='M264.313 182.176V180.429' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M262.32 180.429H266.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M264.313 182.176V183.922' fill='#e0e0f0'/>
+<path clip-path='url(#clip2)' d='M264.313 182.176V183.922' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M266.304 183.922H262.32' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M322.984 182.176V181.504' fill='#e0e0f0'/>
+<path clip-path='url(#clip2)' d='M322.984 182.176V181.504' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M320.992 181.504H324.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M322.984 182.176V182.847' fill='#e0e0f0'/>
+<path clip-path='url(#clip2)' d='M322.984 182.176V182.847' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M324.976 182.847H320.988' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M381.652 182.176V181.84' fill='#e0e0f0'/>
+<path clip-path='url(#clip2)' d='M381.652 182.176V181.84' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M379.66 181.84H383.644' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M381.652 182.176V182.512' fill='#e0e0f0'/>
+<path clip-path='url(#clip2)' d='M381.652 182.176V182.512' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M383.644 182.512H379.66' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M440.32 182.176V181.371' fill='#e0e0f0'/>
+<path clip-path='url(#clip2)' d='M440.32 182.176V181.371' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M438.328 181.371H442.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M440.32 182.176V182.98' fill='#e0e0f0'/>
+<path clip-path='url(#clip2)' d='M440.32 182.176V182.98' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M442.312 182.98H438.328' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M150.586 249.281H153.824V180.765H150.586ZM209.258 249.281H212.496V173.117H209.258ZM267.926 249.281H271.164V158.086H267.926ZM326.594 249.281H329.832V81.515H326.594ZM385.262 249.281H388.5V81.515H385.262ZM443.934 249.281H447.172V81.515H443.934Z' fill='#c2c2e1'/>
+<path clip-path='url(#clip2)' d='M150.586 249.281H153.824V180.765H150.586ZM209.258 249.281H212.496V173.117H209.258ZM267.926 249.281H271.164V158.086H267.926ZM326.594 249.281H329.832V81.515H326.594ZM385.262 249.281H388.5V81.515H385.262ZM443.934 249.281H447.172V81.515H443.934Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M152.207 180.765V180.633' fill='#c2c2e1'/>
+<path clip-path='url(#clip2)' d='M152.207 180.765V180.633' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M150.215 180.633H154.199' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M152.207 180.765V180.902' fill='#c2c2e1'/>
+<path clip-path='url(#clip2)' d='M152.207 180.765V180.902' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M154.2 180.902H150.215' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M210.875 173.117V172.914' fill='#c2c2e1'/>
+<path clip-path='url(#clip2)' d='M210.875 173.117V172.914' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M208.883 172.915H212.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M210.875 173.117V173.316' fill='#c2c2e1'/>
+<path clip-path='url(#clip2)' d='M210.875 173.117V173.316' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M212.867 173.316H208.883' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M269.543 158.086V157.48' fill='#c2c2e1'/>
+<path clip-path='url(#clip2)' d='M269.543 158.086V157.48' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M267.551 157.48H271.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M269.543 158.086V158.687' fill='#c2c2e1'/>
+<path clip-path='url(#clip2)' d='M269.543 158.086V158.687' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M271.535 158.687H267.551' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M328.215 81.515V81.515' fill='#c2c2e1'/>
+<path clip-path='url(#clip2)' d='M326.223 81.516H330.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M328.215 81.515V81.515' fill='#c2c2e1'/>
+<path clip-path='url(#clip2)' d='M326.223 81.516H330.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M386.883 81.515V81.515' fill='#c2c2e1'/>
+<path clip-path='url(#clip2)' d='M384.891 81.516H388.875' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M386.883 81.515V81.515' fill='#c2c2e1'/>
+<path clip-path='url(#clip2)' d='M384.891 81.516H388.875' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M445.551 81.515V81.515' fill='#c2c2e1'/>
+<path clip-path='url(#clip2)' d='M443.559 81.516H447.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M445.551 81.515V81.515' fill='#c2c2e1'/>
+<path clip-path='url(#clip2)' d='M443.559 81.516H447.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M155.817 249.281H159.055V175.668H155.817ZM214.488 249.281H217.727V172.648H214.488ZM273.156 249.281H276.395V81.515H273.156ZM331.824 249.281H335.063V148.152H331.824ZM390.492 249.281H393.731V130.57H390.492ZM449.164 249.281H452.402V81.515H449.164Z' fill='#a3a3d1'/>
+<path clip-path='url(#clip2)' d='M155.817 249.281H159.055V175.668H155.817ZM214.488 249.281H217.727V172.648H214.488ZM273.156 249.281H276.395V81.515H273.156ZM331.824 249.281H335.063V148.152H331.824ZM390.492 249.281H393.731V130.57H390.492ZM449.164 249.281H452.402V81.515H449.164Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M157.438 175.668V174.39' fill='#a3a3d1'/>
+<path clip-path='url(#clip2)' d='M157.438 175.668V174.39' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M155.445 174.391H159.429' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M157.438 175.668V176.941' fill='#a3a3d1'/>
+<path clip-path='url(#clip2)' d='M157.438 175.668V176.941' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M159.43 176.942H155.445' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M216.106 172.648V172.648' fill='#a3a3d1'/>
+<path clip-path='url(#clip2)' d='M214.113 172.648H218.097' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M216.106 172.648V172.648' fill='#a3a3d1'/>
+<path clip-path='url(#clip2)' d='M214.113 172.648H218.097' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M274.774 81.515V81.515' fill='#a3a3d1'/>
+<path clip-path='url(#clip2)' d='M272.781 81.516H276.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M274.774 81.515V81.515' fill='#a3a3d1'/>
+<path clip-path='url(#clip2)' d='M272.781 81.516H276.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M333.445 148.152V146.945' fill='#a3a3d1'/>
+<path clip-path='url(#clip2)' d='M333.445 148.152V146.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M331.453 146.945H335.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M333.445 148.152V149.359' fill='#a3a3d1'/>
+<path clip-path='url(#clip2)' d='M333.445 148.152V149.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M335.437 149.359H331.449' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M392.113 130.57V129.566' fill='#a3a3d1'/>
+<path clip-path='url(#clip2)' d='M392.113 130.57V129.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M390.121 129.566H394.105' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M392.113 130.57V131.578' fill='#a3a3d1'/>
+<path clip-path='url(#clip2)' d='M392.113 130.57V131.578' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M394.105 131.578H390.121' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M450.781 81.515V81.515' fill='#a3a3d1'/>
+<path clip-path='url(#clip2)' d='M448.789 81.516H452.773' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M450.781 81.515V81.515' fill='#a3a3d1'/>
+<path clip-path='url(#clip2)' d='M448.789 81.516H452.773' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M161.047 249.281H164.285V157.949H161.047ZM219.719 249.281H222.957V144.664H219.719ZM278.387 249.281H281.625V81.515H278.387ZM337.055 249.281H340.293V156.879H337.055ZM395.723 249.281H398.961V162.984H395.723ZM454.395 249.281H457.633V179.426H454.395Z' fill='#8585c2'/>
+<path clip-path='url(#clip2)' d='M161.047 249.281H164.285V157.949H161.047ZM219.719 249.281H222.957V144.664H219.719ZM278.387 249.281H281.625V81.515H278.387ZM337.055 249.281H340.293V156.879H337.055ZM395.723 249.281H398.961V162.984H395.723ZM454.395 249.281H457.633V179.426H454.395Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M162.668 157.949V157.883' fill='#8585c2'/>
+<path clip-path='url(#clip2)' d='M162.668 157.949V157.883' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M160.676 157.883H164.66' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M162.668 157.949V158.019' fill='#8585c2'/>
+<path clip-path='url(#clip2)' d='M162.668 157.949V158.019' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M164.661 158.02H160.676' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M221.336 144.664V144.261' fill='#8585c2'/>
+<path clip-path='url(#clip2)' d='M221.336 144.664V144.261' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M219.344 144.261H223.328' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M221.336 144.664V145.066' fill='#8585c2'/>
+<path clip-path='url(#clip2)' d='M221.336 144.664V145.066' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M223.329 145.066H219.344' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M280.004 81.515V81.515' fill='#8585c2'/>
+<path clip-path='url(#clip2)' d='M278.012 81.516H282' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M280.004 81.515V81.515' fill='#8585c2'/>
+<path clip-path='url(#clip2)' d='M278.012 81.516H282' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M338.676 156.879V156.004' fill='#8585c2'/>
+<path clip-path='url(#clip2)' d='M338.676 156.879V156.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M336.684 156.004H340.668' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M338.676 156.879V157.75' fill='#8585c2'/>
+<path clip-path='url(#clip2)' d='M338.676 156.879V157.75' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M340.668 157.75H336.68' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M397.344 162.984V162.582' fill='#8585c2'/>
+<path clip-path='url(#clip2)' d='M397.344 162.984V162.582' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M395.352 162.582H399.336' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M397.344 162.984V163.387' fill='#8585c2'/>
+<path clip-path='url(#clip2)' d='M397.344 162.984V163.387' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M399.336 163.386H395.352' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M456.012 179.426V178.621' fill='#8585c2'/>
+<path clip-path='url(#clip2)' d='M456.012 179.426V178.621' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M454.02 178.622H458.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M456.012 179.426V180.23' fill='#8585c2'/>
+<path clip-path='url(#clip2)' d='M456.012 179.426V180.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M458.004 180.23H454.02' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M166.278 249.281H169.516V163.992H166.278ZM224.949 249.281H228.188V159.426H224.949ZM283.617 249.281H286.856V91.047H283.617ZM342.285 249.281H345.524V81.515H342.285ZM400.953 249.281H404.192V81.515H400.953ZM459.625 249.281H462.863V81.515H459.625Z' fill='#6666b3'/>
+<path clip-path='url(#clip2)' d='M166.278 249.281H169.516V163.992H166.278ZM224.949 249.281H228.188V159.426H224.949ZM283.617 249.281H286.856V91.047H283.617ZM342.285 249.281H345.524V81.515H342.285ZM400.953 249.281H404.192V81.515H400.953ZM459.625 249.281H462.863V81.515H459.625Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M167.899 163.992V163.722' fill='#6666b3'/>
+<path clip-path='url(#clip2)' d='M167.899 163.992V163.722' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M165.906 163.723H169.89' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M167.899 163.992V164.258' fill='#6666b3'/>
+<path clip-path='url(#clip2)' d='M167.899 163.992V164.258' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M169.891 164.258H165.906' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M226.567 159.426V159.359' fill='#6666b3'/>
+<path clip-path='url(#clip2)' d='M226.567 159.426V159.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M224.574 159.359H228.558' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M226.567 159.426V159.496' fill='#6666b3'/>
+<path clip-path='url(#clip2)' d='M226.567 159.426V159.496' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M228.559 159.496H224.574' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M285.234 91.047V90.777' fill='#6666b3'/>
+<path clip-path='url(#clip2)' d='M285.234 91.047V90.777' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M283.242 90.777H287.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M285.234 91.047V91.316' fill='#6666b3'/>
+<path clip-path='url(#clip2)' d='M285.234 91.047V91.316' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M287.227 91.316H283.242' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M343.906 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip2)' d='M341.914 81.516H345.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M343.906 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip2)' d='M341.914 81.516H345.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M402.574 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip2)' d='M400.582 81.516H404.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M402.574 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip2)' d='M400.582 81.516H404.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M461.242 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip2)' d='M459.25 81.516H463.234' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M461.242 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip2)' d='M459.25 81.516H463.234' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M171.508 249.281H174.746V172.648H171.508ZM230.18 249.281H233.414V132.519H230.18ZM288.848 249.281H292.086V124.867H288.848ZM347.516 249.281H350.754V81.515H347.516ZM406.184 249.281H409.422V81.515H406.184ZM464.856 249.281H468.094V81.515H464.856Z' fill='#4747a4'/>
+<path clip-path='url(#clip2)' d='M171.508 249.281H174.746V172.648H171.508ZM230.18 249.281H233.414V132.519H230.18ZM288.848 249.281H292.086V124.867H288.848ZM347.516 249.281H350.754V81.515H347.516ZM406.184 249.281H409.422V81.515H406.184ZM464.856 249.281H468.094V81.515H464.856Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M173.129 172.648V172.312' fill='#4747a4'/>
+<path clip-path='url(#clip2)' d='M173.129 172.648V172.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M171.137 172.313H175.121' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M173.129 172.648V172.984' fill='#4747a4'/>
+<path clip-path='url(#clip2)' d='M173.129 172.648V172.984' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M175.122 172.985H171.137' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M231.797 132.519V131.847' fill='#4747a4'/>
+<path clip-path='url(#clip2)' d='M231.797 132.519V131.847' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M229.805 131.848H233.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M231.797 132.519V133.187' fill='#4747a4'/>
+<path clip-path='url(#clip2)' d='M231.797 132.519V133.187' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M233.79 133.188H229.805' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M290.465 124.867V124.062' fill='#4747a4'/>
+<path clip-path='url(#clip2)' d='M290.465 124.867V124.062' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M288.473 124.062H292.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M290.465 124.867V125.672' fill='#4747a4'/>
+<path clip-path='url(#clip2)' d='M290.465 124.867V125.672' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M292.458 125.672H288.473' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M349.137 81.515V81.515' fill='#4747a4'/>
+<path clip-path='url(#clip2)' d='M347.145 81.516H351.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M349.137 81.515V81.515' fill='#4747a4'/>
+<path clip-path='url(#clip2)' d='M347.145 81.516H351.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M407.805 81.515V81.515' fill='#4747a4'/>
+<path clip-path='url(#clip2)' d='M405.813 81.516H409.797' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M407.805 81.515V81.515' fill='#4747a4'/>
+<path clip-path='url(#clip2)' d='M405.813 81.516H409.797' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M466.473 81.515V81.515' fill='#4747a4'/>
+<path clip-path='url(#clip2)' d='M464.481 81.516H468.465' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M466.473 81.515V81.515' fill='#4747a4'/>
+<path clip-path='url(#clip2)' d='M464.481 81.516H468.465' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M176.738 249.281H179.977V163.656H176.738ZM235.41 249.281H238.645V159.359H235.41ZM294.078 249.281H297.317V90.578H294.078ZM352.746 249.281H355.984V81.515H352.746ZM411.414 249.281H414.652V81.515H411.414ZM470.086 249.281H473.324V81.515H470.086Z' fill='#292994'/>
+<path clip-path='url(#clip2)' d='M176.738 249.281H179.977V163.656H176.738ZM235.41 249.281H238.645V159.359H235.41ZM294.078 249.281H297.317V90.578H294.078ZM352.746 249.281H355.984V81.515H352.746ZM411.414 249.281H414.652V81.515H411.414ZM470.086 249.281H473.324V81.515H470.086Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M178.36 163.656V163.32' fill='#292994'/>
+<path clip-path='url(#clip2)' d='M178.36 163.656V163.32' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M176.367 163.32H180.351' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M178.36 163.656V163.992' fill='#292994'/>
+<path clip-path='url(#clip2)' d='M178.36 163.656V163.992' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M180.352 163.992H176.367' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M237.027 159.359V159.359' fill='#292994'/>
+<path clip-path='url(#clip2)' d='M235.035 159.359H239.019' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M237.027 159.359V159.359' fill='#292994'/>
+<path clip-path='url(#clip2)' d='M235.035 159.359H239.019' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M295.695 90.578V89.234' fill='#292994'/>
+<path clip-path='url(#clip2)' d='M295.695 90.578V89.234' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M293.703 89.234H297.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M295.695 90.578V91.918' fill='#292994'/>
+<path clip-path='url(#clip2)' d='M295.695 90.578V91.918' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M297.688 91.918H293.703' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M354.367 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip2)' d='M352.375 81.516H356.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M354.367 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip2)' d='M352.375 81.516H356.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M413.035 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip2)' d='M411.043 81.516H415.027' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M413.035 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip2)' d='M411.043 81.516H415.027' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M471.703 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip2)' d='M469.711 81.516H473.695' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M471.703 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip2)' d='M469.711 81.516H473.695' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M416.645 249.281H419.883V131.445H416.645ZM181.969 249.281H185.207V153.523H181.969ZM240.641 249.281H243.875V149.226H240.641ZM299.309 249.281H302.547V81.515H299.309ZM357.977 249.281H361.215V129.297H357.977ZM475.317 249.281H478.555V179.894H475.317Z' fill='#0a0a85'/>
+<path clip-path='url(#clip2)' d='M416.645 249.281H419.883V131.445H416.645ZM181.969 249.281H185.207V153.523H181.969ZM240.641 249.281H243.875V149.226H240.641ZM299.309 249.281H302.547V81.515H299.309ZM357.977 249.281H361.215V129.297H357.977ZM475.317 249.281H478.555V179.894H475.317Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M418.266 131.445V128.894' fill='#0a0a85'/>
+<path clip-path='url(#clip2)' d='M418.266 131.445V128.894' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M416.274 128.895H420.258' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M418.266 131.445V133.992' fill='#0a0a85'/>
+<path clip-path='url(#clip2)' d='M418.266 131.445V133.992' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M420.257 133.992H416.273' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M183.59 153.523V152.851' fill='#0a0a85'/>
+<path clip-path='url(#clip2)' d='M183.59 153.523V152.851' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M181.598 152.852H185.583' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M183.59 153.523V154.191' fill='#0a0a85'/>
+<path clip-path='url(#clip2)' d='M183.59 153.523V154.191' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M185.582 154.191H181.597' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M242.258 149.226V149.16' fill='#0a0a85'/>
+<path clip-path='url(#clip2)' d='M242.258 149.226V149.16' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M240.266 149.16H244.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M242.258 149.226V149.293' fill='#0a0a85'/>
+<path clip-path='url(#clip2)' d='M242.258 149.226V149.293' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M244.25 149.293H240.265' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M300.926 81.515V81.515' fill='#0a0a85'/>
+<path clip-path='url(#clip2)' d='M298.933 81.516H302.921' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M300.926 81.515V81.515' fill='#0a0a85'/>
+<path clip-path='url(#clip2)' d='M298.933 81.516H302.921' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M359.598 129.297V128.426' fill='#0a0a85'/>
+<path clip-path='url(#clip2)' d='M359.598 129.297V128.426' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M357.606 128.426H361.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M359.598 129.297V130.168' fill='#0a0a85'/>
+<path clip-path='url(#clip2)' d='M359.598 129.297V130.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M361.589 130.168H357.601' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M476.934 179.894V179.894' fill='#0a0a85'/>
+<path clip-path='url(#clip2)' d='M474.942 179.895H478.926' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M476.934 179.894V179.894' fill='#0a0a85'/>
+<path clip-path='url(#clip2)' d='M474.942 179.895H478.926' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip2)' d='M418.387 298.301H487.762V276.324H418.387Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 253.422 92.648)'>
+<use x='168.285' xlink:href='#g1-114' y='190.457'/>
+<use x='170.092' xlink:href='#g1-53' y='190.457'/>
+<use x='172.739' xlink:href='#g1-97' y='190.457'/>
+<use x='175.281' xlink:href='#g1-45' y='190.457'/>
+<use x='177.046' xlink:href='#g1-52' y='190.457'/>
+<use x='179.692' xlink:href='#g1-120' y='190.457'/>
+<use x='182.131' xlink:href='#g1-108' y='190.457'/>
+<use x='183.393' xlink:href='#g1-97' y='190.457'/>
+<use x='185.789' xlink:href='#g1-114' y='190.457'/>
+<use x='187.596' xlink:href='#g1-103' y='190.457'/>
+<use x='190.243' xlink:href='#g1-101' y='190.457'/>
+<use x='192.595' xlink:href='#g1-44' y='190.457'/>
+<use x='195.83' xlink:href='#g1-49' y='190.457'/>
+<use x='198.476' xlink:href='#g1-50' y='190.457'/>
+<use x='201.122' xlink:href='#g1-56' y='190.457'/>
+<use x='203.769' xlink:href='#g1-71' y='190.457'/>
+<use x='207.297' xlink:href='#g1-98' y='190.457'/>
+<use x='168.285' xlink:href='#g1-49' y='196.075'/>
+<use x='170.931' xlink:href='#g1-54' y='196.075'/>
+<use x='173.578' xlink:href='#g1-45' y='196.075'/>
+<use x='175.342' xlink:href='#g1-99' y='196.075'/>
+<use x='177.694' xlink:href='#g1-111' y='196.075'/>
+<use x='180.193' xlink:href='#g1-114' y='196.075'/>
+<use x='182.001' xlink:href='#g1-101' y='196.075'/>
+<use x='186.118' xlink:href='#g1-65' y='196.075'/>
+<use x='189.646' xlink:href='#g1-77' y='196.075'/>
+<use x='194.264' xlink:href='#g1-68' y='196.075'/>
+<use x='199.851' xlink:href='#g1-69' y='196.075'/>
+<use x='203.025' xlink:href='#g1-112' y='196.075'/>
+<use x='205.611' xlink:href='#g1-121' y='196.075'/>
+<use x='208.049' xlink:href='#g1-99' y='196.075'/>
+<use x='212.166' xlink:href='#g1-64' y='196.075'/>
+<use x='215.695' xlink:href='#g1-50' y='196.075'/>
+<use x='218.341' xlink:href='#g1-46' y='196.075'/>
+<use x='219.811' xlink:href='#g1-53' y='196.075'/>
+<use x='222.457' xlink:href='#g1-71' y='196.075'/>
+<use x='225.986' xlink:href='#g1-104' y='196.075'/>
+<use x='228.719' xlink:href='#g1-122' y='196.075'/>
+<use x='168.285' xlink:href='#g1-85' y='201.694'/>
+<use x='171.917' xlink:href='#g1-98' y='201.694'/>
+<use x='174.65' xlink:href='#g1-117' y='201.694'/>
+<use x='177.383' xlink:href='#g1-110' y='201.694'/>
+<use x='180.116' xlink:href='#g1-116' y='201.694'/>
+<use x='182.027' xlink:href='#g1-117' y='201.694'/>
+<use x='186.524' xlink:href='#g1-49' y='201.694'/>
+<use x='189.17' xlink:href='#g1-56' y='201.694'/>
+<use x='191.817' xlink:href='#g1-46' y='201.694'/>
+<use x='193.287' xlink:href='#g1-48' y='201.694'/>
+<use x='195.933' xlink:href='#g1-52' y='201.694'/>
+<use x='198.58' xlink:href='#g1-46' y='201.694'/>
+<use x='200.05' xlink:href='#g1-49' y='201.694'/>
+<use x='202.696' xlink:href='#g1-44' y='201.694'/>
+<use x='205.931' xlink:href='#g1-71' y='201.694'/>
+<use x='209.459' xlink:href='#g1-67' y='201.694'/>
+<use x='212.841' xlink:href='#g1-67' y='201.694'/>
+<use x='217.986' xlink:href='#g1-55' y='201.694'/>
+<use x='220.633' xlink:href='#g1-46' y='201.694'/>
+<use x='222.103' xlink:href='#g1-51' y='201.694'/>
+<use x='224.749' xlink:href='#g1-46' y='201.694'/>
+<use x='226.219' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -53.084 344.257)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 5.585 344.257)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 64.254 344.257)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 122.923 344.257)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 181.592 344.257)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 240.261 344.257)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -47.853 342.848)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-50' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 10.816 335.198)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-49' y='201.694'/>
+<use x='175.048' xlink:href='#g1-52' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 69.485 320.166)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-51' y='201.694'/>
+<use x='175.048' xlink:href='#g1-54' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 128.154 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-51' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-56' y='201.694'/>
+<use x='180.675' xlink:href='#g1-50' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 186.823 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-56' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-50' y='201.694'/>
+<use x='180.675' xlink:href='#g1-50' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 245.492 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-49' y='201.694'/>
+<use x='176.558' xlink:href='#g1-57' y='201.694'/>
+<use x='179.205' xlink:href='#g1-46' y='201.694'/>
+<use x='180.675' xlink:href='#g1-50' y='201.694'/>
+<use x='183.321' xlink:href='#g1-51' y='201.694'/>
+<use x='185.968' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -42.623 337.748)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-49' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 16.046 334.728)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-49' y='201.694'/>
+<use x='175.048' xlink:href='#g1-52' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 74.715 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-50' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-56' y='201.694'/>
+<use x='180.675' xlink:href='#g1-51' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 133.384 310.234)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-53' y='201.694'/>
+<use x='175.048' xlink:href='#g1-49' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 192.053 292.653)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-55' y='201.694'/>
+<use x='175.048' xlink:href='#g1-55' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 250.722 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-49' y='201.694'/>
+<use x='176.558' xlink:href='#g1-56' y='201.694'/>
+<use x='179.205' xlink:href='#g1-46' y='201.694'/>
+<use x='180.675' xlink:href='#g1-48' y='201.694'/>
+<use x='183.321' xlink:href='#g1-57' y='201.694'/>
+<use x='185.968' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -37.392 320.032)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-51' y='201.694'/>
+<use x='175.048' xlink:href='#g1-54' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 21.277 306.745)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-53' y='201.694'/>
+<use x='175.048' xlink:href='#g1-54' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 79.946 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-50' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-55' y='201.694'/>
+<use x='180.675' xlink:href='#g1-52' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 138.615 318.958)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-51' y='201.694'/>
+<use x='175.048' xlink:href='#g1-56' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 197.284 325.065)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-50' y='201.694'/>
+<use x='175.048' xlink:href='#g1-57' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 255.953 341.506)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-52' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -32.162 326.071)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-50' y='201.694'/>
+<use x='175.048' xlink:href='#g1-55' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 26.507 321.508)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-51' y='201.694'/>
+<use x='175.048' xlink:href='#g1-52' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 85.176 253.127)'>
+<use x='168.285' xlink:href='#g1-50' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-51' y='201.694'/>
+<use x='175.048' xlink:href='#g1-54' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 143.845 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-50' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-57' y='201.694'/>
+<use x='180.675' xlink:href='#g1-52' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 202.514 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-49' y='201.694'/>
+<use x='176.558' xlink:href='#g1-50' y='201.694'/>
+<use x='179.205' xlink:href='#g1-46' y='201.694'/>
+<use x='180.675' xlink:href='#g1-53' y='201.694'/>
+<use x='183.321' xlink:href='#g1-50' y='201.694'/>
+<use x='185.968' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 261.183 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-52' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-50' y='201.694'/>
+<use x='180.675' xlink:href='#g1-49' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -26.931 334.728)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-49' y='201.694'/>
+<use x='175.048' xlink:href='#g1-52' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 31.738 294.599)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-55' y='201.694'/>
+<use x='175.048' xlink:href='#g1-52' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 90.407 286.949)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-56' y='201.694'/>
+<use x='175.048' xlink:href='#g1-53' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 149.076 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-52' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-55' y='201.694'/>
+<use x='180.675' xlink:href='#g1-51' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 207.745 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-51' y='201.694'/>
+<use x='176.558' xlink:href='#g1-51' y='201.694'/>
+<use x='179.205' xlink:href='#g1-46' y='201.694'/>
+<use x='180.675' xlink:href='#g1-56' y='201.694'/>
+<use x='183.321' xlink:href='#g1-53' y='201.694'/>
+<use x='185.968' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 266.414 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-51' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-57' y='201.694'/>
+<use x='180.675' xlink:href='#g1-53' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -21.701 325.736)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-50' y='201.694'/>
+<use x='175.048' xlink:href='#g1-56' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 36.968 321.441)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-51' y='201.694'/>
+<use x='175.048' xlink:href='#g1-52' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 95.637 252.657)'>
+<use x='168.285' xlink:href='#g1-50' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-51' y='201.694'/>
+<use x='175.048' xlink:href='#g1-55' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 154.306 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-50' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-57' y='201.694'/>
+<use x='180.675' xlink:href='#g1-55' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 212.975 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-49' y='201.694'/>
+<use x='176.558' xlink:href='#g1-49' y='201.694'/>
+<use x='179.205' xlink:href='#g1-46' y='201.694'/>
+<use x='180.675' xlink:href='#g1-55' y='201.694'/>
+<use x='183.321' xlink:href='#g1-56' y='201.694'/>
+<use x='185.968' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 271.644 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-52' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-49' y='201.694'/>
+<use x='180.675' xlink:href='#g1-56' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 218.205 293.525)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-55' y='201.694'/>
+<use x='175.048' xlink:href='#g1-54' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -16.471 315.603)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-52' y='201.694'/>
+<use x='175.048' xlink:href='#g1-51' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 42.198 311.308)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-52' y='201.694'/>
+<use x='175.048' xlink:href='#g1-57' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 100.867 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-51' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-52' y='201.694'/>
+<use x='180.675' xlink:href='#g1-56' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 159.536 291.377)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-55' y='201.694'/>
+<use x='175.048' xlink:href='#g1-57' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 276.874 341.975)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-51' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -88.536 388.944)'>
+<use x='168.285' xlink:href='#g0-82' y='201.694'/>
+<use x='174.255' xlink:href='#g0-101' y='201.694'/>
+<use x='178.351' xlink:href='#g0-108' y='201.694'/>
+<use x='180.551' xlink:href='#g0-97' y='201.694'/>
+<use x='184.979' xlink:href='#g0-116' y='201.694'/>
+<use x='188.307' xlink:href='#g0-105' y='201.694'/>
+<use x='190.507' xlink:href='#g0-118' y='201.694'/>
+<use x='194.755' xlink:href='#g0-101' y='201.694'/>
+<use x='201.922' xlink:href='#g0-116' y='201.694'/>
+<use x='205.25' xlink:href='#g0-105' y='201.694'/>
+<use x='207.45' xlink:href='#g0-109' y='201.694'/>
+<use x='214.77' xlink:href='#g0-101' y='201.694'/>
+<use x='221.937' xlink:href='#g2-40' y='201.694'/>
+<use x='225.231' xlink:href='#g2-108' y='201.694'/>
+<use x='227.251' xlink:href='#g2-111' y='201.694'/>
+<use x='231.25' xlink:href='#g2-119' y='201.694'/>
+<use x='236.799' xlink:href='#g2-101' y='201.694'/>
+<use x='240.562' xlink:href='#g2-114' y='201.694'/>
+<use x='246.277' xlink:href='#g2-105' y='201.694'/>
+<use x='248.297' xlink:href='#g2-115' y='201.694'/>
+<use x='254.366' xlink:href='#g2-98' y='201.694'/>
+<use x='258.974' xlink:href='#g2-101' y='201.694'/>
+<use x='262.738' xlink:href='#g2-116' y='201.694'/>
+<use x='265.796' xlink:href='#g2-116' y='201.694'/>
+<use x='268.854' xlink:href='#g2-101' y='201.694'/>
+<use x='272.618' xlink:href='#g2-114' y='201.694'/>
+<use x='275.51' xlink:href='#g2-41' y='201.694'/>
+</g>
+<path d='M136.149 296.672H349.488V279.679H136.149Z' fill='#ffffff'/>
+<path d='M136.149 296.672H349.488V279.679H136.149Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path d='M139.336 290.168H142.324V282.199H139.336ZM145.313 290.168H148.301V284.191H145.313Z' fill='#e0e0f0'/>
+<path d='M139.336 290.168H142.324V282.199H139.336ZM145.313 290.168H148.301V284.191H145.313Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -33.522 88.465)'>
+<use x='185.011' xlink:href='#g2-109' y='201.694'/>
+<use x='191.736' xlink:href='#g2-105' y='201.694'/>
+</g>
+<path d='M163.422 290.168H166.41V282.199H163.422ZM169.398 290.168H172.391V284.191H169.398Z' fill='#c2c2e1'/>
+<path d='M163.422 290.168H166.41V282.199H163.422ZM169.398 290.168H172.391V284.191H169.398Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.56 88.011)'>
+<use x='208.137' xlink:href='#g2-116' y='201.694'/>
+<use x='211.195' xlink:href='#g2-99' y='201.694'/>
+</g>
+<path d='M185.586 290.168H188.574V282.199H185.586ZM191.563 290.168H194.555V284.191H191.563Z' fill='#a3a3d1'/>
+<path d='M185.586 290.168H188.574V282.199H185.586ZM191.563 290.168H194.555V284.191H191.563Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.159 88.465)'>
+<use x='229.9' xlink:href='#g2-106' y='201.694'/>
+<use x='232.155' xlink:href='#g2-101' y='201.694'/>
+</g>
+<path d='M206.949 290.168H209.938V282.199H206.949ZM212.926 290.168H215.914V284.191H212.926Z' fill='#8585c2'/>
+<path d='M206.949 290.168H209.938V282.199H206.949ZM212.926 290.168H215.914V284.191H212.926Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.958 87.505)'>
+<use x='252.061' xlink:href='#g2-115' y='201.694'/>
+<use x='255.307' xlink:href='#g2-110' y='201.694'/>
+</g>
+<path d='M229.91 290.168H232.898V282.199H229.91ZM235.887 290.168H238.875V284.191H235.887Z' fill='#6666b3'/>
+<path d='M229.91 290.168H232.898V282.199H229.91ZM235.887 290.168H238.875V284.191H235.887Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.781 87.505)'>
+<use x='274.845' xlink:href='#g2-114' y='201.694'/>
+<use x='277.737' xlink:href='#g2-112' y='201.694'/>
+</g>
+<path d='M252.516 290.168H255.504V282.199H252.516ZM258.496 290.168H261.484V284.191H258.496Z' fill='#4747a4'/>
+<path d='M252.516 290.168H255.504V282.199H252.516ZM258.496 290.168H261.484V284.191H258.496Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -39.001 88.501)'>
+<use x='303.672' xlink:href='#g2-104' y='201.694'/>
+<use x='308.045' xlink:href='#g2-111' y='201.694'/>
+<use x='312.279' xlink:href='#g2-97' y='201.694'/>
+<use x='316.112' xlink:href='#g2-114' y='201.694'/>
+<use x='319.004' xlink:href='#g2-100' y='201.694'/>
+</g>
+<path d='M287.563 290.168H290.551V282.199H287.563ZM293.543 290.168H296.531V284.191H293.543Z' fill='#292994'/>
+<path d='M287.563 290.168H290.551V282.199H287.563ZM293.543 290.168H296.531V284.191H293.543Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.472 88.501)'>
+<use x='337.19' xlink:href='#g2-103' y='201.694'/>
+<use x='341.424' xlink:href='#g2-108' y='201.694'/>
+<use x='343.444' xlink:href='#g2-105' y='201.694'/>
+<use x='345.464' xlink:href='#g2-98' y='201.694'/>
+<use x='350.072' xlink:href='#g2-99' y='201.694'/>
+</g>
+<path d='M319.551 290.168H322.539V282.199H319.551ZM325.527 290.168H328.52V284.191H325.527Z' fill='#0a0a85'/>
+<path d='M319.551 290.168H322.539V282.199H319.551ZM325.527 290.168H328.52V284.191H325.527Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -35.051 88.501)'>
+<use x='366.757' xlink:href='#g2-116' y='201.694'/>
+<use x='369.815' xlink:href='#g2-98' y='201.694'/>
+<use x='374.187' xlink:href='#g2-98' y='201.694'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='164.687pt' version='1.1' viewBox='52.938 54.996 381.625 164.687' width='381.625pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip7'>
+<path d='M82.148 203.937H434.164V78.691H82.148Z'/>
+</clipPath>
+<use id='g2-40' transform='scale(1.6)' xlink:href='#g1-40'/>
+<use id='g2-41' transform='scale(1.6)' xlink:href='#g1-41'/>
+<use id='g2-78' transform='scale(1.6)' xlink:href='#g1-78'/>
+<use id='g2-97' transform='scale(1.6)' xlink:href='#g1-97'/>
+<use id='g2-98' transform='scale(1.6)' xlink:href='#g1-98'/>
+<use id='g2-99' transform='scale(1.6)' xlink:href='#g1-99'/>
+<use id='g2-100' transform='scale(1.6)' xlink:href='#g1-100'/>
+<use id='g2-101' transform='scale(1.6)' xlink:href='#g1-101'/>
+<use id='g2-102' transform='scale(1.6)' xlink:href='#g1-102'/>
+<use id='g2-105' transform='scale(1.6)' xlink:href='#g1-105'/>
+<use id='g2-108' transform='scale(1.6)' xlink:href='#g1-108'/>
+<use id='g2-110' transform='scale(1.6)' xlink:href='#g1-110'/>
+<use id='g2-111' transform='scale(1.6)' xlink:href='#g1-111'/>
+<use id='g2-112' transform='scale(1.6)' xlink:href='#g1-112'/>
+<use id='g2-114' transform='scale(1.6)' xlink:href='#g1-114'/>
+<use id='g2-115' transform='scale(1.6)' xlink:href='#g1-115'/>
+<use id='g2-116' transform='scale(1.6)' xlink:href='#g1-116'/>
+<use id='g2-119' transform='scale(1.6)' xlink:href='#g1-119'/>
+<path d='M1.519 -3.736C1.435 -3.736 1.425 -3.736 1.365 -3.681C0.737 -3.133 0.418 -2.247 0.418 -1.245C0.418 -0.304 0.702 0.603 1.36 1.181C1.425 1.245 1.435 1.245 1.519 1.245H1.758C1.743 1.235 1.26 0.822 1.031 0.045C0.912 -0.344 0.852 -0.752 0.852 -1.245C0.852 -2.969 1.659 -3.651 1.758 -3.736H1.519Z' id='g1-40'/>
+<path d='M0.533 1.245C0.618 1.245 0.628 1.245 0.687 1.191C1.315 0.643 1.634 -0.244 1.634 -1.245C1.634 -2.187 1.35 -3.093 0.692 -3.671C0.628 -3.736 0.618 -3.736 0.533 -3.736H0.294C0.309 -3.726 0.792 -3.313 1.021 -2.535C1.141 -2.147 1.2 -1.738 1.2 -1.245C1.2 0.478 0.394 1.161 0.294 1.245H0.533Z' id='g1-41'/>
+<path d='M0.956 -0.448H0.508V0H0.956V-0.448Z' id='g1-46'/>
+<path d='M2.431 -1.619C2.431 -1.858 2.431 -2.441 2.197 -2.849C1.943 -3.298 1.559 -3.372 1.32 -3.372C1.096 -3.372 0.707 -3.303 0.458 -2.874C0.219 -2.476 0.209 -1.933 0.209 -1.619C0.209 -1.25 0.229 -0.797 0.438 -0.418C0.658 -0.015 1.026 0.105 1.32 0.105C1.818 0.105 2.092 -0.184 2.242 -0.498C2.416 -0.852 2.431 -1.31 2.431 -1.619ZM1.32 -0.224C1.111 -0.224 0.872 -0.344 0.747 -0.702C0.648 -1.006 0.643 -1.32 0.643 -1.684C0.643 -2.142 0.643 -3.044 1.32 -3.044S1.998 -2.142 1.998 -1.684C1.998 -1.355 1.998 -0.981 1.878 -0.663C1.738 -0.304 1.484 -0.224 1.32 -0.224Z' id='g1-48'/>
+<path d='M1.599 -3.372H1.489C1.166 -3.073 0.757 -3.054 0.458 -3.044V-2.73C0.653 -2.735 0.902 -2.745 1.151 -2.844V-0.314H0.488V0H2.262V-0.314H1.599V-3.372Z' id='g1-49'/>
+<path d='M1.41 -0.384C1.35 -0.384 1.29 -0.379 1.23 -0.379H0.663L1.435 -1.061C1.524 -1.141 1.768 -1.325 1.863 -1.405C2.082 -1.604 2.376 -1.863 2.376 -2.296C2.376 -2.859 1.958 -3.372 1.245 -3.372C0.717 -3.372 0.389 -3.088 0.219 -2.58L0.453 -2.286C0.568 -2.705 0.742 -3.029 1.176 -3.029C1.594 -3.029 1.913 -2.735 1.913 -2.286C1.913 -1.873 1.669 -1.639 1.37 -1.355C1.27 -1.255 1.001 -1.031 0.897 -0.932C0.752 -0.802 0.408 -0.468 0.264 -0.344V0H2.376V-0.384H1.41Z' id='g1-50'/>
+<path d='M0.498 -2.555C0.702 -2.954 1.061 -3.064 1.3 -3.064C1.594 -3.064 1.813 -2.894 1.813 -2.61C1.813 -2.346 1.634 -2.022 1.255 -1.958C1.23 -1.953 1.21 -1.953 0.882 -1.928V-1.599H1.27C1.743 -1.599 1.918 -1.225 1.918 -0.912C1.918 -0.523 1.679 -0.224 1.29 -0.224C0.936 -0.224 0.533 -0.394 0.284 -0.712L0.219 -0.389C0.508 -0.065 0.912 0.105 1.3 0.105C1.953 0.105 2.421 -0.384 2.421 -0.907C2.421 -1.315 2.092 -1.644 1.699 -1.758C2.077 -1.953 2.271 -2.286 2.271 -2.61C2.271 -3.034 1.838 -3.372 1.305 -3.372C0.867 -3.372 0.503 -3.143 0.294 -2.844L0.498 -2.555Z' id='g1-51'/>
+<path d='M1.973 -0.832H2.491V-1.161H1.973V-3.268H1.479L0.149 -1.161V-0.832H1.544V0H1.973V-0.832ZM0.573 -1.161C0.722 -1.395 1.579 -2.725 1.579 -3.024V-1.161H0.573Z' id='g1-52'/>
+<path d='M0.817 -2.924H2.197V-3.268H0.418V-1.405H0.782C0.902 -1.674 1.136 -1.793 1.36 -1.793C1.564 -1.793 1.873 -1.654 1.873 -1.021C1.873 -0.369 1.46 -0.224 1.205 -0.224C0.877 -0.224 0.558 -0.399 0.389 -0.682L0.199 -0.384C0.443 -0.08 0.812 0.105 1.205 0.105C1.863 0.105 2.376 -0.403 2.376 -1.011C2.376 -1.629 1.918 -2.122 1.37 -2.122C1.156 -2.122 0.966 -2.047 0.817 -1.923V-2.924Z' id='g1-53'/>
+<path d='M2.187 -3.273C1.918 -3.372 1.729 -3.372 1.634 -3.372C0.877 -3.372 0.219 -2.66 0.219 -1.609C0.219 -0.259 0.827 0.105 1.33 0.105C1.733 0.105 1.943 -0.085 2.097 -0.244C2.416 -0.583 2.421 -0.936 2.421 -1.111C2.421 -1.763 2.067 -2.306 1.584 -2.306C1.096 -2.306 0.832 -2.052 0.687 -1.908C0.752 -2.59 1.101 -3.064 1.639 -3.064C1.738 -3.064 1.938 -3.054 2.187 -2.959V-3.273ZM0.692 -1.096C0.692 -1.126 0.692 -1.2 0.697 -1.225C0.697 -1.564 0.912 -1.978 1.355 -1.978C1.963 -1.978 1.963 -1.28 1.963 -1.111C1.963 -0.922 1.963 -0.712 1.828 -0.503C1.709 -0.324 1.559 -0.224 1.33 -0.224C0.802 -0.224 0.717 -0.877 0.692 -1.096Z' id='g1-54'/>
+<path d='M1.23 -2.884C1.29 -2.884 1.35 -2.889 1.41 -2.889H2.037C1.28 -2.147 0.797 -1.106 0.797 0.05H1.265C1.265 -1.405 1.973 -2.451 2.421 -2.919V-3.268H0.219V-2.884H1.23Z' id='g1-55'/>
+<path d='M1.704 -1.763C2.032 -1.868 2.346 -2.132 2.346 -2.501C2.346 -2.954 1.913 -3.372 1.32 -3.372S0.294 -2.954 0.294 -2.501C0.294 -2.127 0.618 -1.863 0.936 -1.763C0.498 -1.629 0.219 -1.29 0.219 -0.907C0.219 -0.374 0.692 0.105 1.32 0.105S2.421 -0.374 2.421 -0.907C2.421 -1.29 2.137 -1.629 1.704 -1.763ZM1.32 -1.928C0.966 -1.928 0.677 -2.132 0.677 -2.496C0.677 -2.814 0.902 -3.064 1.32 -3.064C1.733 -3.064 1.963 -2.814 1.963 -2.496C1.963 -2.142 1.684 -1.928 1.32 -1.928ZM1.32 -0.224C0.976 -0.224 0.672 -0.443 0.672 -0.912C0.672 -1.36 0.961 -1.599 1.32 -1.599S1.968 -1.355 1.968 -0.912C1.968 -0.443 1.659 -0.224 1.32 -0.224Z' id='g1-56'/>
+<path d='M0.384 -0.125C0.628 0.055 0.852 0.105 1.086 0.105C1.783 0.105 2.421 -0.598 2.421 -1.669C2.421 -3.029 1.818 -3.372 1.34 -3.372C0.897 -3.372 0.692 -3.163 0.548 -3.019C0.229 -2.695 0.219 -2.351 0.219 -2.157C0.219 -1.514 0.568 -0.961 1.056 -0.961C1.619 -0.961 1.928 -1.335 1.953 -1.365C1.883 -0.573 1.494 -0.224 1.086 -0.224C0.827 -0.224 0.667 -0.319 0.553 -0.413L0.384 -0.125ZM1.938 -2.162C1.943 -2.132 1.943 -2.082 1.943 -2.052C1.943 -1.684 1.719 -1.29 1.285 -1.29C1.096 -1.29 0.946 -1.345 0.817 -1.549C0.687 -1.743 0.677 -1.933 0.677 -2.157C0.677 -2.351 0.677 -2.575 0.832 -2.795C0.936 -2.944 1.086 -3.064 1.335 -3.064C1.818 -3.064 1.923 -2.481 1.938 -2.162Z' id='g1-57'/>
+<path d='M1.176 -3.457H0.498V0H0.917V-3.064H0.922L2.555 0H3.233V-3.457H2.814V-0.394H2.809L1.176 -3.457Z' id='g1-78'/>
+<path d='M2.122 -1.435C2.122 -1.943 1.733 -2.286 1.24 -2.286C0.927 -2.286 0.687 -2.222 0.408 -2.072L0.438 -1.709C0.603 -1.818 0.847 -1.968 1.24 -1.968C1.46 -1.968 1.689 -1.803 1.689 -1.43V-1.23C0.951 -1.205 0.224 -1.051 0.224 -0.588C0.224 -0.339 0.394 0.05 0.832 0.05C1.046 0.05 1.44 0.005 1.704 -0.189V0H2.122V-1.435ZM1.689 -0.707C1.689 -0.608 1.689 -0.478 1.514 -0.374C1.355 -0.284 1.161 -0.279 1.106 -0.279C0.832 -0.279 0.623 -0.403 0.623 -0.593C0.623 -0.912 1.465 -0.941 1.689 -0.951V-0.707Z' id='g1-97'/>
+<path d='M0.842 -3.457H0.423V0H0.857V-0.234C0.966 -0.139 1.205 0.05 1.569 0.05C2.112 0.05 2.55 -0.458 2.55 -1.111C2.55 -1.714 2.207 -2.262 1.709 -2.262C1.395 -2.262 1.091 -2.162 0.842 -1.978V-3.457ZM0.857 -1.569C0.857 -1.649 0.857 -1.709 1.031 -1.823C1.106 -1.868 1.24 -1.933 1.41 -1.933C1.743 -1.933 2.117 -1.709 2.117 -1.111C2.117 -0.503 1.704 -0.279 1.355 -0.279C1.171 -0.279 0.996 -0.364 0.857 -0.588V-1.569Z' id='g1-98'/>
+<path d='M2.167 -0.543C1.918 -0.384 1.649 -0.294 1.34 -0.294C0.882 -0.294 0.613 -0.663 0.613 -1.111C0.613 -1.494 0.812 -1.943 1.355 -1.943C1.694 -1.943 1.853 -1.873 2.107 -1.714L2.172 -2.072C1.873 -2.222 1.743 -2.286 1.355 -2.286C0.608 -2.286 0.179 -1.684 0.179 -1.106C0.179 -0.498 0.658 0.05 1.335 0.05C1.684 0.05 1.983 -0.055 2.197 -0.179L2.167 -0.543Z' id='g1-99'/>
+<path d='M2.306 -3.457H1.888V-1.998C1.569 -2.232 1.245 -2.262 1.101 -2.262C0.578 -2.262 0.179 -1.738 0.179 -1.106S0.573 0.05 1.086 0.05C1.395 0.05 1.684 -0.09 1.873 -0.259V0H2.306V-3.457ZM1.873 -0.618C1.748 -0.413 1.559 -0.279 1.32 -0.279C0.971 -0.279 0.613 -0.523 0.613 -1.101C0.613 -1.724 1.036 -1.933 1.375 -1.933C1.574 -1.933 1.743 -1.848 1.873 -1.679V-0.618Z' id='g1-100'/>
+<path d='M2.142 -0.543C1.863 -0.344 1.549 -0.279 1.335 -0.279C0.902 -0.279 0.573 -0.633 0.558 -1.091H2.192C2.192 -1.32 2.167 -1.654 1.973 -1.938C1.793 -2.192 1.494 -2.286 1.25 -2.286C0.643 -2.286 0.174 -1.753 0.174 -1.121C0.174 -0.483 0.672 0.05 1.33 0.05C1.619 0.05 1.918 -0.035 2.172 -0.189L2.142 -0.543ZM0.593 -1.39C0.707 -1.788 1.001 -1.958 1.25 -1.958C1.469 -1.958 1.793 -1.853 1.888 -1.39H0.593Z' id='g1-101'/>
+<path d='M0.946 -1.898H1.514V-2.212H0.932V-2.785C0.932 -3.128 1.245 -3.178 1.41 -3.178C1.514 -3.178 1.649 -3.163 1.833 -3.093V-3.457C1.704 -3.487 1.549 -3.507 1.415 -3.507C0.902 -3.507 0.528 -3.138 0.528 -2.645V-2.212H0.144V-1.898H0.528V0H0.946V-1.898Z' id='g1-102'/>
+<path d='M0.877 -3.417H0.374V-2.914H0.877V-3.417ZM0.837 -2.212H0.418V0H0.837V-2.212Z' id='g1-105'/>
+<path d='M0.837 -3.457H0.418V0H0.837V-3.457Z' id='g1-108'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-110'/>
+<path d='M2.491 -1.091C2.491 -1.748 1.968 -2.286 1.32 -2.286S0.149 -1.743 0.149 -1.091C0.149 -0.458 0.677 0.05 1.32 0.05C1.968 0.05 2.491 -0.458 2.491 -1.091ZM1.32 -0.294C0.927 -0.294 0.583 -0.583 0.583 -1.146S0.951 -1.958 1.32 -1.958C1.694 -1.958 2.057 -1.699 2.057 -1.146C2.057 -0.578 1.704 -0.294 1.32 -0.294Z' id='g1-111'/>
+<path d='M0.857 -0.234C1.121 0.005 1.405 0.05 1.574 0.05C2.102 0.05 2.55 -0.453 2.55 -1.111C2.55 -1.709 2.222 -2.262 1.729 -2.262C1.504 -2.262 1.131 -2.197 0.842 -1.973V-2.212H0.423V0.966H0.857V-0.234ZM0.857 -1.654C0.971 -1.793 1.166 -1.918 1.405 -1.918C1.803 -1.918 2.117 -1.549 2.117 -1.111C2.117 -0.618 1.743 -0.279 1.355 -0.279C1.28 -0.279 1.156 -0.289 1.026 -0.394C0.877 -0.508 0.857 -0.583 0.857 -0.677V-1.654Z' id='g1-112'/>
+<path d='M0.842 -1.061C0.842 -1.599 1.29 -1.888 1.729 -1.893V-2.262C1.31 -2.257 1.006 -2.052 0.807 -1.788V-2.247H0.423V0H0.842V-1.061Z' id='g1-114'/>
+<path d='M1.818 -2.132C1.479 -2.271 1.23 -2.286 1.051 -2.286C0.927 -2.286 0.174 -2.286 0.174 -1.624C0.174 -1.39 0.304 -1.26 0.369 -1.2C0.543 -1.026 0.752 -0.986 1.016 -0.936C1.25 -0.892 1.519 -0.842 1.519 -0.603C1.519 -0.289 1.106 -0.289 1.036 -0.289C0.717 -0.289 0.418 -0.403 0.219 -0.543L0.149 -0.169C0.319 -0.085 0.623 0.05 1.036 0.05C1.26 0.05 1.479 0.015 1.664 -0.12C1.848 -0.259 1.908 -0.478 1.908 -0.648C1.908 -0.737 1.898 -0.932 1.689 -1.121C1.504 -1.285 1.325 -1.32 1.086 -1.365C0.792 -1.42 0.563 -1.465 0.563 -1.684C0.563 -1.968 0.927 -1.968 1.001 -1.968C1.285 -1.968 1.504 -1.908 1.753 -1.778L1.818 -2.132Z' id='g1-115'/>
+<path d='M0.936 -1.898H1.674V-2.212H0.936V-2.844H0.553V-2.212H0.1V-1.898H0.538V-0.638C0.538 -0.304 0.623 0.05 0.981 0.05S1.614 -0.065 1.763 -0.134L1.679 -0.453C1.514 -0.334 1.34 -0.294 1.2 -0.294C0.991 -0.294 0.936 -0.498 0.936 -0.727V-1.898Z' id='g1-116'/>
+<path d='M3.537 -2.212H3.148C3.103 -2.072 2.824 -1.23 2.67 -0.712C2.63 -0.568 2.58 -0.408 2.565 -0.294H2.56C2.531 -0.498 2.356 -1.036 2.346 -1.071L1.978 -2.212H1.599C1.455 -1.783 1.081 -0.667 1.041 -0.304H1.036C0.996 -0.658 0.628 -1.758 0.548 -1.998C0.508 -2.117 0.508 -2.127 0.483 -2.212H0.075L0.802 0H1.22C1.225 -0.02 1.36 -0.413 1.534 -0.966C1.609 -1.21 1.758 -1.689 1.783 -1.908L1.788 -1.913C1.798 -1.808 1.828 -1.699 1.863 -1.574S1.933 -1.315 1.968 -1.2L2.351 0H2.809L3.537 -2.212Z' id='g1-119'/>
+<path d='M1.38 -1.141L2.346 -2.212H1.908L1.2 -1.395L0.478 -2.212H0.03L1.026 -1.141L0 0H0.443L1.2 -0.936L1.988 0H2.436L1.38 -1.141Z' id='g1-120'/>
+<path d='M1.445 -1.245C1.445 -1.41 1.305 -1.549 1.141 -1.549S0.837 -1.41 0.837 -1.245S0.976 -0.941 1.141 -0.941S1.445 -1.081 1.445 -1.245Z' id='g3-1'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g0-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g0-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g0-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g0-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g0-108'/>
+<path d='M1.462 -1.91C1.462 -2.851 2.197 -3.425 3.013 -3.434V-4.08C2.367 -4.071 1.775 -3.748 1.408 -3.219V-4.035H0.744V0H1.462V-1.91Z' id='g0-114'/>
+<path d='M3.165 -3.847C2.609 -4.098 2.197 -4.133 1.829 -4.133C1.623 -4.133 0.305 -4.133 0.305 -2.95C0.305 -2.52 0.565 -2.251 0.664 -2.152C1.004 -1.856 1.237 -1.811 1.847 -1.695C2.134 -1.641 2.645 -1.542 2.645 -1.085C2.645 -0.502 1.919 -0.502 1.802 -0.502C1.273 -0.502 0.762 -0.681 0.377 -0.95L0.26 -0.296C0.798 -0.009 1.345 0.099 1.802 0.099C2.385 0.099 3.318 -0.09 3.318 -1.157C3.318 -1.47 3.192 -1.784 2.878 -2.053C2.573 -2.313 2.304 -2.367 1.748 -2.475C1.426 -2.537 0.977 -2.618 0.977 -3.04C0.977 -3.569 1.614 -3.569 1.748 -3.569C2.403 -3.569 2.789 -3.362 3.049 -3.219L3.165 -3.847Z' id='g0-115'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g0-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g0-118'/>
+</defs>
+<g id='page7'>
+<path d='M140.82 212.793V203.937M199.488 212.793V203.937M258.156 212.793V203.937M316.824 212.793V203.937M375.496 212.793V203.937M140.82 69.836V78.691M199.488 69.836V78.691M258.156 69.836V78.691M316.824 69.836V78.691M375.496 69.836V78.691' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M111.484 208.191V203.937M170.152 208.191V203.937M228.824 208.191V203.937M287.492 208.191V203.937M346.16 208.191V203.937M404.828 208.191V203.937M111.484 74.441V78.691M170.152 74.441V78.691M228.824 74.441V78.691M287.492 74.441V78.691M346.16 74.441V78.691M404.828 74.441V78.691' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 203.937H86.402M82.148 172.625H86.402M82.148 141.316H86.402M82.148 110.004H86.402M82.148 78.691H86.402M434.164 203.937H429.91M434.164 172.625H429.91M434.164 141.316H429.91M434.164 110.004H429.91M434.164 78.691H429.91' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 203.937V78.691H434.164V203.937H82.148Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -11.54 33.989)'>
+<use x='114.487' xlink:href='#g2-99' y='183.949'/>
+<use x='118.25' xlink:href='#g2-102' y='183.949'/>
+<use x='120.838' xlink:href='#g2-114' y='183.949'/>
+<use x='123.73' xlink:href='#g2-97' y='183.949'/>
+<use x='127.798' xlink:href='#g2-99' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 41.401 33.989)'>
+<use x='114.487' xlink:href='#g2-101' y='183.949'/>
+<use x='118.25' xlink:href='#g2-115' y='183.949'/>
+<use x='121.497' xlink:href='#g2-112' y='183.949'/>
+<use x='125.634' xlink:href='#g2-114' y='183.949'/>
+<use x='128.526' xlink:href='#g2-101' y='183.949'/>
+<use x='132.29' xlink:href='#g2-115' y='183.949'/>
+<use x='135.536' xlink:href='#g2-115' y='183.949'/>
+<use x='138.782' xlink:href='#g2-111' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 103.095 33.989)'>
+<use x='114.487' xlink:href='#g2-98' y='183.949'/>
+<use x='118.859' xlink:href='#g2-97' y='183.949'/>
+<use x='122.692' xlink:href='#g2-114' y='183.949'/>
+<use x='125.584' xlink:href='#g2-110' y='183.949'/>
+<use x='129.957' xlink:href='#g2-101' y='183.949'/>
+<use x='133.72' xlink:href='#g2-115' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 162.903 33.989)'>
+<use x='114.487' xlink:href='#g2-108' y='183.949'/>
+<use x='116.507' xlink:href='#g2-101' y='183.949'/>
+<use x='120.271' xlink:href='#g2-97' y='183.949'/>
+<use x='124.339' xlink:href='#g2-110' y='183.949'/>
+<use x='128.711' xlink:href='#g2-78' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 223.526 33.989)'>
+<use x='114.487' xlink:href='#g2-114' y='183.949'/>
+<use x='117.379' xlink:href='#g2-101' y='183.949'/>
+<use x='121.142' xlink:href='#g2-100' y='183.949'/>
+<use x='125.515' xlink:href='#g2-105' y='183.949'/>
+<use x='127.535' xlink:href='#g2-115' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 277.054 33.989)'>
+<use x='114.487' xlink:href='#g2-108' y='183.949'/>
+<use x='116.507' xlink:href='#g2-97' y='183.949'/>
+<use x='120.34' xlink:href='#g2-114' y='183.949'/>
+<use x='123.232' xlink:href='#g2-115' y='183.949'/>
+<use x='126.478' xlink:href='#g2-111' y='183.949'/>
+<use x='130.712' xlink:href='#g2-110' y='183.949'/>
+<use x='135.085' xlink:href='#g2-78' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 21.624)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -9.688)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -40.999)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -72.311)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -103.622)'>
+<use x='114.487' xlink:href='#g1-50' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<path clip-path='url(#clip7)' d='M82.148 141.316H434.164' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M91.559 203.937H94.797V141.316H91.559ZM150.227 203.937H153.465V141.316H150.227ZM208.899 203.937H212.133V141.316H208.899ZM267.567 203.937H270.805V141.316H267.567ZM326.234 203.937H329.473V141.316H326.234ZM384.902 203.937H388.141V141.316H384.902Z' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M91.559 203.937H94.797V141.316H91.559ZM150.227 203.937H153.465V141.316H150.227ZM208.899 203.937H212.133V141.316H208.899ZM267.567 203.937H270.805V141.316H267.567ZM326.234 203.937H329.473V141.316H326.234ZM384.902 203.937H388.141V141.316H384.902Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M93.18 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M91.184 141.316H95.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M93.18 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M91.184 141.316H95.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M151.848 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M149.855 141.316H153.84' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M151.848 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M149.855 141.316H153.84' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M210.516 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M208.523 141.316H212.507' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M210.516 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M208.523 141.316H212.507' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M269.184 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M267.191 141.316H271.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M269.184 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M267.191 141.316H271.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M327.856 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M325.859 141.316H329.847' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M327.856 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M325.859 141.316H329.847' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M386.524 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M384.531 141.316H388.515' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M386.524 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip7)' d='M384.531 141.316H388.515' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M96.789 203.937H100.027V94.222H96.789ZM155.457 203.937H158.695V78.691H155.457ZM214.129 203.937H217.363V138.246H214.129ZM272.797 203.937H276.035V154.652H272.797ZM331.465 203.937H334.703V129.793H331.465ZM390.133 203.937H393.371V138.808H390.133Z' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M96.789 203.937H100.027V94.222H96.789ZM155.457 203.937H158.695V78.691H155.457ZM214.129 203.937H217.363V138.246H214.129ZM272.797 203.937H276.035V154.652H272.797ZM331.465 203.937H334.703V129.793H331.465ZM390.133 203.937H393.371V138.808H390.133Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M98.41 94.222V94.222' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M96.414 94.223H100.399' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M98.41 94.222V94.222' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M96.414 94.223H100.399' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M157.078 78.691V78.691' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M155.086 78.691H159.071' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M157.078 78.691V78.691' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M155.086 78.691H159.071' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M215.746 138.246V138.246' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M213.754 138.246H217.739' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M215.746 138.246V138.246' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M213.754 138.246H217.739' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M274.414 154.652V154.652' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M272.422 154.652H276.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M274.414 154.652V154.652' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M272.422 154.652H276.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M333.086 129.793V129.793' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M331.09 129.793H335.078' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M333.086 129.793V129.793' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M331.09 129.793H335.078' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M391.754 138.808V138.808' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M389.762 138.808H393.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M391.754 138.808V138.808' fill='#e1c2e1'/>
+<path clip-path='url(#clip7)' d='M389.762 138.808H393.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M102.02 203.937H105.258V115.828H102.02ZM160.688 203.937H163.926V108.187H160.688ZM219.359 203.937H222.594V139.625H219.359ZM278.027 203.937H281.266V141.379H278.027ZM336.695 203.937H339.934V137.246H336.695ZM395.363 203.937H398.602V122.09H395.363Z' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M102.02 203.937H105.258V115.828H102.02ZM160.688 203.937H163.926V108.187H160.688ZM219.359 203.937H222.594V139.625H219.359ZM278.027 203.937H281.266V141.379H278.027ZM336.695 203.937H339.934V137.246H336.695ZM395.363 203.937H398.602V122.09H395.363Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M103.641 115.828V115.828' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M101.644 115.828H105.629' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M103.641 115.828V115.828' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M101.644 115.828H105.629' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M162.309 108.187V108.187' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M160.316 108.188H164.301' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M162.309 108.187V108.187' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M160.316 108.188H164.301' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M220.977 139.625V139.625' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M218.984 139.625H222.969' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M220.977 139.625V139.625' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M218.984 139.625H222.969' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M279.645 141.379V141.379' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M277.652 141.379H281.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M279.645 141.379V141.379' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M277.652 141.379H281.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M338.317 137.246V137.246' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M336.32 137.246H340.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M338.317 137.246V137.246' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M336.32 137.246H340.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M396.984 122.09V122.09' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M394.992 122.09H398.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M396.984 122.09V122.09' fill='#d1a3d1'/>
+<path clip-path='url(#clip7)' d='M394.992 122.09H398.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M107.25 203.937H110.488V123.469H107.25ZM165.918 203.937H169.156V112.133H165.918ZM224.59 203.937H227.824V140.187H224.59ZM283.258 203.937H286.496V140.312H283.258ZM341.926 203.937H345.164V137.933H341.926ZM400.594 203.937H403.832V126.347H400.594Z' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M107.25 203.937H110.488V123.469H107.25ZM165.918 203.937H169.156V112.133H165.918ZM224.59 203.937H227.824V140.187H224.59ZM283.258 203.937H286.496V140.312H283.258ZM341.926 203.937H345.164V137.933H341.926ZM400.594 203.937H403.832V126.347H400.594Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M108.871 123.469V123.469' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M106.875 123.469H110.86' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M108.871 123.469V123.469' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M106.875 123.469H110.86' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M167.539 112.133V112.133' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M165.547 112.133H169.532' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M167.539 112.133V112.133' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M165.547 112.133H169.532' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M226.207 140.187V140.187' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M224.215 140.187H228.2' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M226.207 140.187V140.187' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M224.215 140.187H228.2' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M284.875 140.312V140.312' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M282.883 140.312H286.868' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M284.875 140.312V140.312' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M282.883 140.312H286.868' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M343.547 137.933V137.933' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M341.551 137.934H345.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M343.547 137.933V137.933' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M341.551 137.934H345.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M402.215 126.347V126.347' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M400.223 126.347H404.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M402.215 126.347V126.347' fill='#c285c2'/>
+<path clip-path='url(#clip7)' d='M400.223 126.347H404.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M112.481 203.937H115.719V139H112.481ZM171.149 203.937H174.387V143.82H171.149ZM229.82 203.937H233.055V141.316H229.82ZM288.488 203.937H291.727V133.176H288.488ZM347.156 203.937H350.395V140H347.156ZM405.824 203.937H409.063V133.176H405.824Z' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M112.481 203.937H115.719V139H112.481ZM171.149 203.937H174.387V143.82H171.149ZM229.82 203.937H233.055V141.316H229.82ZM288.488 203.937H291.727V133.176H288.488ZM347.156 203.937H350.395V140H347.156ZM405.824 203.937H409.063V133.176H405.824Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M114.098 139V139' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M112.105 139H116.09' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M114.098 139V139' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M112.105 139H116.09' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M172.77 143.82V143.82' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M170.777 143.82H174.762' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M172.77 143.82V143.82' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M170.777 143.82H174.762' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M231.438 141.316V141.316' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M229.445 141.316H233.43' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M231.438 141.316V141.316' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M229.445 141.316H233.43' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M290.106 133.176V133.176' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M288.113 133.176H292.098' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M290.106 133.176V133.176' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M288.113 133.176H292.098' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M348.777 140V140' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M346.781 140H350.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M348.777 140V140' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M346.781 140H350.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M407.445 133.176V133.176' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M405.453 133.176H409.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M407.445 133.176V133.176' fill='#b366b3'/>
+<path clip-path='url(#clip7)' d='M405.453 133.176H409.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M117.711 203.937H120.949V111.32H117.711ZM176.379 203.937H179.617V78.691H176.379ZM235.051 203.937H238.285V139.75H235.051ZM293.719 203.937H296.957V125.785H293.719ZM352.387 203.937H355.625V124.906H352.387ZM411.055 203.937H414.293V132.359H411.055Z' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M117.711 203.937H120.949V111.32H117.711ZM176.379 203.937H179.617V78.691H176.379ZM235.051 203.937H238.285V139.75H235.051ZM293.719 203.937H296.957V125.785H293.719ZM352.387 203.937H355.625V124.906H352.387ZM411.055 203.937H414.293V132.359H411.055Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M119.328 111.32V111.32' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M117.336 111.32H121.321' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M119.328 111.32V111.32' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M117.336 111.32H121.321' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M178 78.691V78.691' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M176.008 78.691H179.993' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M178 78.691V78.691' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M176.008 78.691H179.993' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M236.668 139.75V139.75' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M234.676 139.75H238.661' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M236.668 139.75V139.75' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M234.676 139.75H238.661' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M295.336 125.785V125.785' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M293.344 125.785H297.329' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M295.336 125.785V125.785' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M293.344 125.785H297.329' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M354.008 124.906V124.906' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M352.012 124.906H356' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M354.008 124.906V124.906' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M352.012 124.906H356' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M412.676 132.359V132.359' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M410.684 132.359H414.668' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M412.676 132.359V132.359' fill='#a447a4'/>
+<path clip-path='url(#clip7)' d='M410.684 132.359H414.668' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M122.941 203.937H126.18V143.57H122.941ZM181.609 203.937H184.848V154.34H181.609ZM240.281 203.937H243.516V141.504H240.281ZM298.949 203.937H302.188V133.363H298.949ZM357.617 203.937H360.856V139.937H357.617ZM416.285 203.937H419.524V133.426H416.285Z' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M122.941 203.937H126.18V143.57H122.941ZM181.609 203.937H184.848V154.34H181.609ZM240.281 203.937H243.516V141.504H240.281ZM298.949 203.937H302.188V133.363H298.949ZM357.617 203.937H360.856V139.937H357.617ZM416.285 203.937H419.524V133.426H416.285Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M124.559 143.57V143.57' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M122.566 143.571H126.551' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M124.559 143.57V143.57' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M122.566 143.571H126.551' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M183.231 154.34V154.34' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M181.238 154.34H185.223' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M183.231 154.34V154.34' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M181.238 154.34H185.223' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M241.899 141.504V141.504' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M239.906 141.504H243.891' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M241.899 141.504V141.504' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M239.906 141.504H243.891' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M300.567 133.363V133.363' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M298.574 133.363H302.559' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M300.567 133.363V133.363' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M298.574 133.363H302.559' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M359.238 139.937V139.937' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M357.242 139.938H361.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M359.238 139.937V139.937' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M357.242 139.938H361.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M417.906 133.426V133.426' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M415.914 133.426H419.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M417.906 133.426V133.426' fill='#942994'/>
+<path clip-path='url(#clip7)' d='M415.914 133.426H419.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M128.172 203.937H131.41V118.457H128.172ZM186.84 203.937H190.078V113.699H186.84ZM245.512 203.937H248.746V140.062H245.512ZM304.18 203.937H307.418V133.613H304.18ZM362.848 203.937H366.086V97.101H362.848ZM421.516 203.937H424.754V137.371H421.516Z' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M128.172 203.937H131.41V118.457H128.172ZM186.84 203.937H190.078V113.699H186.84ZM245.512 203.937H248.746V140.062H245.512ZM304.18 203.937H307.418V133.613H304.18ZM362.848 203.937H366.086V97.101H362.848ZM421.516 203.937H424.754V137.371H421.516Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M129.789 118.457V118.457' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M127.797 118.457H131.782' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M129.789 118.457V118.457' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M127.797 118.457H131.782' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M188.461 113.699V113.699' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M186.469 113.7H190.454' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M188.461 113.699V113.699' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M186.469 113.7H190.454' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M247.129 140.062V140.062' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M245.137 140.063H249.122' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M247.129 140.062V140.062' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M245.137 140.063H249.122' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M305.797 133.613V133.613' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M303.804 133.614H307.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M305.797 133.613V133.613' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M303.804 133.614H307.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M364.469 97.101V97.101' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M362.473 97.102H366.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M364.469 97.101V97.101' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M362.473 97.102H366.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M423.137 137.371V137.371' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M421.144 137.371H425.128' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip7)' d='M423.137 137.371V137.371' fill='#850a85'/>
+<path clip-path='url(#clip7)' d='M421.144 137.371H425.128' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(0 -1 1 0 -89.137 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -30.468 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 28.201 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 86.87 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 145.539 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 204.208 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -83.906 202.685)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-55' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -25.237 187.155)'>
+<use x='109.598' xlink:href='#g3-1' y='183.949'/>
+<use x='113.103' xlink:href='#g3-1' y='183.949'/>
+<use x='116.608' xlink:href='#g3-1' y='183.949'/>
+<use x='120.114' xlink:href='#g1-50' y='183.949'/>
+<use x='122.76' xlink:href='#g1-46' y='183.949'/>
+<use x='124.23' xlink:href='#g1-48' y='183.949'/>
+<use x='126.877' xlink:href='#g1-55' y='183.949'/>
+<use x='129.523' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 33.432 246.709)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 92.101 263.116)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-55' y='183.949'/>
+<use x='121.25' xlink:href='#g1-57' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 150.77 238.255)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 209.439 247.273)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -78.676 224.29)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-52' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -20.007 216.65)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 38.662 248.087)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 97.331 249.84)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 156 245.707)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 214.669 230.552)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-51' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -73.445 231.93)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -14.776 220.595)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-52' y='183.949'/>
+<use x='121.25' xlink:href='#g1-55' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 43.893 248.65)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 102.562 248.776)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 161.231 246.396)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 219.9 234.811)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -68.215 247.46)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -9.546 252.282)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-57' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 49.123 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 107.792 241.637)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 166.461 248.462)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 225.13 241.637)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -62.984 219.781)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-52' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -4.315 187.155)'>
+<use x='109.598' xlink:href='#g3-1' y='183.949'/>
+<use x='113.103' xlink:href='#g3-1' y='183.949'/>
+<use x='116.608' xlink:href='#g3-1' y='183.949'/>
+<use x='120.114' xlink:href='#g1-52' y='183.949'/>
+<use x='122.76' xlink:href='#g1-46' y='183.949'/>
+<use x='124.23' xlink:href='#g1-51' y='183.949'/>
+<use x='126.877' xlink:href='#g1-54' y='183.949'/>
+<use x='129.523' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 54.354 248.212)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 113.023 234.247)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 171.692 233.37)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 230.361 240.822)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -57.754 252.032)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-57' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 0.915 262.803)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-55' y='183.949'/>
+<use x='121.25' xlink:href='#g1-57' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 59.584 249.965)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 118.253 241.824)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 176.922 248.4)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 235.591 241.887)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -52.524 226.92)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-51' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 6.145 222.161)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-52' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 64.814 248.525)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 123.483 242.075)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 182.152 205.566)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-55' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 240.821 245.832)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -124.589 307.697)'>
+<use x='114.487' xlink:href='#g0-82' y='183.949'/>
+<use x='120.457' xlink:href='#g0-101' y='183.949'/>
+<use x='124.553' xlink:href='#g0-108' y='183.949'/>
+<use x='126.753' xlink:href='#g0-97' y='183.949'/>
+<use x='131.181' xlink:href='#g0-116' y='183.949'/>
+<use x='134.509' xlink:href='#g0-105' y='183.949'/>
+<use x='136.709' xlink:href='#g0-118' y='183.949'/>
+<use x='140.957' xlink:href='#g0-101' y='183.949'/>
+<use x='148.124' xlink:href='#g0-114' y='183.949'/>
+<use x='151.272' xlink:href='#g0-115' y='183.949'/>
+<use x='154.805' xlink:href='#g0-115' y='183.949'/>
+<use x='161.409' xlink:href='#g2-40' y='183.949'/>
+<use x='164.702' xlink:href='#g2-108' y='183.949'/>
+<use x='166.722' xlink:href='#g2-111' y='183.949'/>
+<use x='170.721' xlink:href='#g2-119' y='183.949'/>
+<use x='176.27' xlink:href='#g2-101' y='183.949'/>
+<use x='180.034' xlink:href='#g2-114' y='183.949'/>
+<use x='185.749' xlink:href='#g2-105' y='183.949'/>
+<use x='187.769' xlink:href='#g2-115' y='183.949'/>
+<use x='193.838' xlink:href='#g2-98' y='183.949'/>
+<use x='198.446' xlink:href='#g2-101' y='183.949'/>
+<use x='202.209' xlink:href='#g2-116' y='183.949'/>
+<use x='205.267' xlink:href='#g2-116' y='183.949'/>
+<use x='208.325' xlink:href='#g2-101' y='183.949'/>
+<use x='212.089' xlink:href='#g2-114' y='183.949'/>
+<use x='214.981' xlink:href='#g2-41' y='183.949'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='196.567pt' version='1.1' viewBox='106.737 54.996 381.623 196.567' width='381.623pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip8'>
+<path d='M135.949 203.938H487.961V78.692H135.949Z'/>
+</clipPath>
+<use id='g2-40' transform='scale(1.6)' xlink:href='#g1-40'/>
+<use id='g2-41' transform='scale(1.6)' xlink:href='#g1-41'/>
+<use id='g2-45' transform='scale(1.6)' xlink:href='#g1-45'/>
+<use id='g2-49' transform='scale(1.6)' xlink:href='#g1-49'/>
+<use id='g2-54' transform='scale(1.6)' xlink:href='#g1-54'/>
+<use id='g2-56' transform='scale(1.6)' xlink:href='#g1-56'/>
+<use id='g2-78' transform='scale(1.6)' xlink:href='#g1-78'/>
+<use id='g2-97' transform='scale(1.6)' xlink:href='#g1-97'/>
+<use id='g2-98' transform='scale(1.6)' xlink:href='#g1-98'/>
+<use id='g2-99' transform='scale(1.6)' xlink:href='#g1-99'/>
+<use id='g2-100' transform='scale(1.6)' xlink:href='#g1-100'/>
+<use id='g2-101' transform='scale(1.6)' xlink:href='#g1-101'/>
+<use id='g2-103' transform='scale(1.6)' xlink:href='#g1-103'/>
+<use id='g2-104' transform='scale(1.6)' xlink:href='#g1-104'/>
+<use id='g2-105' transform='scale(1.6)' xlink:href='#g1-105'/>
+<use id='g2-106' transform='scale(1.6)' xlink:href='#g1-106'/>
+<use id='g2-108' transform='scale(1.6)' xlink:href='#g1-108'/>
+<use id='g2-109' transform='scale(1.6)' xlink:href='#g1-109'/>
+<use id='g2-110' transform='scale(1.6)' xlink:href='#g1-110'/>
+<use id='g2-111' transform='scale(1.6)' xlink:href='#g1-111'/>
+<use id='g2-112' transform='scale(1.6)' xlink:href='#g1-112'/>
+<use id='g2-114' transform='scale(1.6)' xlink:href='#g1-114'/>
+<use id='g2-115' transform='scale(1.6)' xlink:href='#g1-115'/>
+<use id='g2-116' transform='scale(1.6)' xlink:href='#g1-116'/>
+<use id='g2-119' transform='scale(1.6)' xlink:href='#g1-119'/>
+<use id='g2-120' transform='scale(1.6)' xlink:href='#g1-120'/>
+<path d='M1.519 -3.736C1.435 -3.736 1.425 -3.736 1.365 -3.681C0.737 -3.133 0.418 -2.247 0.418 -1.245C0.418 -0.304 0.702 0.603 1.36 1.181C1.425 1.245 1.435 1.245 1.519 1.245H1.758C1.743 1.235 1.26 0.822 1.031 0.045C0.912 -0.344 0.852 -0.752 0.852 -1.245C0.852 -2.969 1.659 -3.651 1.758 -3.736H1.519Z' id='g1-40'/>
+<path d='M0.533 1.245C0.618 1.245 0.628 1.245 0.687 1.191C1.315 0.643 1.634 -0.244 1.634 -1.245C1.634 -2.187 1.35 -3.093 0.692 -3.671C0.628 -3.736 0.618 -3.736 0.533 -3.736H0.294C0.309 -3.726 0.792 -3.313 1.021 -2.535C1.141 -2.147 1.2 -1.738 1.2 -1.245C1.2 0.478 0.394 1.161 0.294 1.245H0.533Z' id='g1-41'/>
+<path d='M0.956 -0.005V-0.448H0.508V0H0.648L0.503 0.638H0.727L0.956 -0.005Z' id='g1-44'/>
+<path d='M1.465 -0.951V-1.265H0.06V-0.951H1.465Z' id='g1-45'/>
+<path d='M0.956 -0.448H0.508V0H0.956V-0.448Z' id='g1-46'/>
+<path d='M2.431 -1.619C2.431 -1.858 2.431 -2.441 2.197 -2.849C1.943 -3.298 1.559 -3.372 1.32 -3.372C1.096 -3.372 0.707 -3.303 0.458 -2.874C0.219 -2.476 0.209 -1.933 0.209 -1.619C0.209 -1.25 0.229 -0.797 0.438 -0.418C0.658 -0.015 1.026 0.105 1.32 0.105C1.818 0.105 2.092 -0.184 2.242 -0.498C2.416 -0.852 2.431 -1.31 2.431 -1.619ZM1.32 -0.224C1.111 -0.224 0.872 -0.344 0.747 -0.702C0.648 -1.006 0.643 -1.32 0.643 -1.684C0.643 -2.142 0.643 -3.044 1.32 -3.044S1.998 -2.142 1.998 -1.684C1.998 -1.355 1.998 -0.981 1.878 -0.663C1.738 -0.304 1.484 -0.224 1.32 -0.224Z' id='g1-48'/>
+<path d='M1.599 -3.372H1.489C1.166 -3.073 0.757 -3.054 0.458 -3.044V-2.73C0.653 -2.735 0.902 -2.745 1.151 -2.844V-0.314H0.488V0H2.262V-0.314H1.599V-3.372Z' id='g1-49'/>
+<path d='M1.41 -0.384C1.35 -0.384 1.29 -0.379 1.23 -0.379H0.663L1.435 -1.061C1.524 -1.141 1.768 -1.325 1.863 -1.405C2.082 -1.604 2.376 -1.863 2.376 -2.296C2.376 -2.859 1.958 -3.372 1.245 -3.372C0.717 -3.372 0.389 -3.088 0.219 -2.58L0.453 -2.286C0.568 -2.705 0.742 -3.029 1.176 -3.029C1.594 -3.029 1.913 -2.735 1.913 -2.286C1.913 -1.873 1.669 -1.639 1.37 -1.355C1.27 -1.255 1.001 -1.031 0.897 -0.932C0.752 -0.802 0.408 -0.468 0.264 -0.344V0H2.376V-0.384H1.41Z' id='g1-50'/>
+<path d='M0.498 -2.555C0.702 -2.954 1.061 -3.064 1.3 -3.064C1.594 -3.064 1.813 -2.894 1.813 -2.61C1.813 -2.346 1.634 -2.022 1.255 -1.958C1.23 -1.953 1.21 -1.953 0.882 -1.928V-1.599H1.27C1.743 -1.599 1.918 -1.225 1.918 -0.912C1.918 -0.523 1.679 -0.224 1.29 -0.224C0.936 -0.224 0.533 -0.394 0.284 -0.712L0.219 -0.389C0.508 -0.065 0.912 0.105 1.3 0.105C1.953 0.105 2.421 -0.384 2.421 -0.907C2.421 -1.315 2.092 -1.644 1.699 -1.758C2.077 -1.953 2.271 -2.286 2.271 -2.61C2.271 -3.034 1.838 -3.372 1.305 -3.372C0.867 -3.372 0.503 -3.143 0.294 -2.844L0.498 -2.555Z' id='g1-51'/>
+<path d='M1.973 -0.832H2.491V-1.161H1.973V-3.268H1.479L0.149 -1.161V-0.832H1.544V0H1.973V-0.832ZM0.573 -1.161C0.722 -1.395 1.579 -2.725 1.579 -3.024V-1.161H0.573Z' id='g1-52'/>
+<path d='M0.817 -2.924H2.197V-3.268H0.418V-1.405H0.782C0.902 -1.674 1.136 -1.793 1.36 -1.793C1.564 -1.793 1.873 -1.654 1.873 -1.021C1.873 -0.369 1.46 -0.224 1.205 -0.224C0.877 -0.224 0.558 -0.399 0.389 -0.682L0.199 -0.384C0.443 -0.08 0.812 0.105 1.205 0.105C1.863 0.105 2.376 -0.403 2.376 -1.011C2.376 -1.629 1.918 -2.122 1.37 -2.122C1.156 -2.122 0.966 -2.047 0.817 -1.923V-2.924Z' id='g1-53'/>
+<path d='M2.187 -3.273C1.918 -3.372 1.729 -3.372 1.634 -3.372C0.877 -3.372 0.219 -2.66 0.219 -1.609C0.219 -0.259 0.827 0.105 1.33 0.105C1.733 0.105 1.943 -0.085 2.097 -0.244C2.416 -0.583 2.421 -0.936 2.421 -1.111C2.421 -1.763 2.067 -2.306 1.584 -2.306C1.096 -2.306 0.832 -2.052 0.687 -1.908C0.752 -2.59 1.101 -3.064 1.639 -3.064C1.738 -3.064 1.938 -3.054 2.187 -2.959V-3.273ZM0.692 -1.096C0.692 -1.126 0.692 -1.2 0.697 -1.225C0.697 -1.564 0.912 -1.978 1.355 -1.978C1.963 -1.978 1.963 -1.28 1.963 -1.111C1.963 -0.922 1.963 -0.712 1.828 -0.503C1.709 -0.324 1.559 -0.224 1.33 -0.224C0.802 -0.224 0.717 -0.877 0.692 -1.096Z' id='g1-54'/>
+<path d='M1.23 -2.884C1.29 -2.884 1.35 -2.889 1.41 -2.889H2.037C1.28 -2.147 0.797 -1.106 0.797 0.05H1.265C1.265 -1.405 1.973 -2.451 2.421 -2.919V-3.268H0.219V-2.884H1.23Z' id='g1-55'/>
+<path d='M1.704 -1.763C2.032 -1.868 2.346 -2.132 2.346 -2.501C2.346 -2.954 1.913 -3.372 1.32 -3.372S0.294 -2.954 0.294 -2.501C0.294 -2.127 0.618 -1.863 0.936 -1.763C0.498 -1.629 0.219 -1.29 0.219 -0.907C0.219 -0.374 0.692 0.105 1.32 0.105S2.421 -0.374 2.421 -0.907C2.421 -1.29 2.137 -1.629 1.704 -1.763ZM1.32 -1.928C0.966 -1.928 0.677 -2.132 0.677 -2.496C0.677 -2.814 0.902 -3.064 1.32 -3.064C1.733 -3.064 1.963 -2.814 1.963 -2.496C1.963 -2.142 1.684 -1.928 1.32 -1.928ZM1.32 -0.224C0.976 -0.224 0.672 -0.443 0.672 -0.912C0.672 -1.36 0.961 -1.599 1.32 -1.599S1.968 -1.355 1.968 -0.912C1.968 -0.443 1.659 -0.224 1.32 -0.224Z' id='g1-56'/>
+<path d='M0.384 -0.125C0.628 0.055 0.852 0.105 1.086 0.105C1.783 0.105 2.421 -0.598 2.421 -1.669C2.421 -3.029 1.818 -3.372 1.34 -3.372C0.897 -3.372 0.692 -3.163 0.548 -3.019C0.229 -2.695 0.219 -2.351 0.219 -2.157C0.219 -1.514 0.568 -0.961 1.056 -0.961C1.619 -0.961 1.928 -1.335 1.953 -1.365C1.883 -0.573 1.494 -0.224 1.086 -0.224C0.827 -0.224 0.667 -0.319 0.553 -0.413L0.384 -0.125ZM1.938 -2.162C1.943 -2.132 1.943 -2.082 1.943 -2.052C1.943 -1.684 1.719 -1.29 1.285 -1.29C1.096 -1.29 0.946 -1.345 0.817 -1.549C0.687 -1.743 0.677 -1.933 0.677 -2.157C0.677 -2.351 0.677 -2.575 0.832 -2.795C0.936 -2.944 1.086 -3.064 1.335 -3.064C1.818 -3.064 1.923 -2.481 1.938 -2.162Z' id='g1-57'/>
+<path d='M2.959 -0.438C2.884 -0.438 2.874 -0.438 2.834 -0.418C2.59 -0.334 2.336 -0.279 2.072 -0.279C1.27 -0.279 0.697 -0.956 0.697 -1.729C0.697 -2.565 1.345 -3.178 2.042 -3.178C2.182 -3.178 2.511 -3.143 2.675 -2.745C2.535 -2.824 2.381 -2.859 2.252 -2.859C1.719 -2.859 1.27 -2.361 1.27 -1.729C1.27 -1.081 1.733 -0.598 2.247 -0.598C2.635 -0.598 3.228 -0.912 3.228 -1.798C3.228 -2.301 3.193 -3.507 2.047 -3.507C1.101 -3.507 0.294 -2.725 0.294 -1.729C0.294 -0.742 1.091 0.05 2.052 0.05C2.511 0.05 2.939 -0.139 3.228 -0.438H2.959ZM2.252 -0.927C1.943 -0.927 1.674 -1.27 1.674 -1.729C1.674 -2.202 1.953 -2.531 2.247 -2.531C2.555 -2.531 2.824 -2.187 2.824 -1.729C2.824 -1.255 2.545 -0.927 2.252 -0.927Z' id='g1-64'/>
+<path d='M2.002 -3.457H1.519L0.149 0H0.558L0.946 -0.986H2.461L2.849 0H3.372L2.002 -3.457ZM1.709 -3.078L2.336 -1.28H1.071L1.709 -3.078Z' id='g1-65'/>
+<path d='M3.083 -0.608C2.735 -0.394 2.575 -0.299 2.077 -0.299C1.305 -0.299 0.837 -1.021 0.837 -1.738C0.837 -2.476 1.35 -3.168 2.077 -3.168C2.406 -3.168 2.745 -3.064 2.974 -2.889L3.054 -3.342C2.705 -3.472 2.426 -3.512 2.062 -3.512C1.076 -3.512 0.339 -2.695 0.339 -1.733C0.339 -0.707 1.121 0.05 2.092 0.05C2.58 0.05 2.785 -0.05 3.113 -0.229L3.083 -0.608Z' id='g1-67'/>
+<path d='M0.488 -3.457V0H1.903C2.8 0 3.522 -0.757 3.522 -1.699C3.522 -2.675 2.795 -3.457 1.903 -3.457H0.488ZM0.976 -0.294V-3.163H1.768C2.451 -3.163 3.034 -2.62 3.034 -1.704C3.034 -0.777 2.426 -0.294 1.768 -0.294H0.976Z' id='g1-68'/>
+<path d='M2.725 -1.624V-1.953H0.986V-3.098H1.714C1.773 -3.098 1.833 -3.093 1.893 -3.093H2.874V-3.442H0.483V0H2.949V-0.389H2.501C2.082 -0.389 1.664 -0.379 1.245 -0.379H0.986V-1.624H2.725Z' id='g1-69'/>
+<path d='M3.173 -1.489H2.057V-1.161H2.735V-0.399C2.516 -0.344 2.301 -0.299 2.077 -0.299C1.31 -0.299 0.837 -1.021 0.837 -1.733C0.837 -2.416 1.3 -3.168 2.052 -3.168C2.511 -3.168 2.8 -3.029 3.049 -2.819L3.128 -3.273C2.785 -3.437 2.481 -3.517 2.102 -3.517C1.096 -3.517 0.339 -2.73 0.339 -1.733C0.339 -0.762 1.091 0.05 2.072 0.05C2.431 0.05 2.854 -0.03 3.173 -0.194V-1.489Z' id='g1-71'/>
+<path d='M2.775 -1.729C2.715 -1.579 2.356 -0.702 2.306 -0.493H2.301C2.267 -0.638 2.037 -1.21 1.988 -1.34L1.131 -3.457H0.523V0H0.941V-3.059H0.946C0.986 -2.884 1.245 -2.227 1.27 -2.172L2.102 -0.105H2.496L3.238 -1.938C3.238 -1.943 3.582 -2.79 3.666 -3.064H3.671V0H4.09V-3.457H3.477L2.775 -1.729Z' id='g1-77'/>
+<path d='M1.176 -3.457H0.498V0H0.917V-3.064H0.922L2.555 0H3.233V-3.457H2.814V-0.394H2.809L1.176 -3.457Z' id='g1-78'/>
+<path d='M3.143 -3.457H2.71V-1.161C2.71 -0.493 2.262 -0.189 1.833 -0.189S0.986 -0.498 0.986 -1.156V-3.457H0.483V-1.166C0.483 -0.433 1.111 0.105 1.828 0.105C2.54 0.105 3.143 -0.438 3.143 -1.166V-3.457Z' id='g1-85'/>
+<path d='M2.122 -1.435C2.122 -1.943 1.733 -2.286 1.24 -2.286C0.927 -2.286 0.687 -2.222 0.408 -2.072L0.438 -1.709C0.603 -1.818 0.847 -1.968 1.24 -1.968C1.46 -1.968 1.689 -1.803 1.689 -1.43V-1.23C0.951 -1.205 0.224 -1.051 0.224 -0.588C0.224 -0.339 0.394 0.05 0.832 0.05C1.046 0.05 1.44 0.005 1.704 -0.189V0H2.122V-1.435ZM1.689 -0.707C1.689 -0.608 1.689 -0.478 1.514 -0.374C1.355 -0.284 1.161 -0.279 1.106 -0.279C0.832 -0.279 0.623 -0.403 0.623 -0.593C0.623 -0.912 1.465 -0.941 1.689 -0.951V-0.707Z' id='g1-97'/>
+<path d='M0.842 -3.457H0.423V0H0.857V-0.234C0.966 -0.139 1.205 0.05 1.569 0.05C2.112 0.05 2.55 -0.458 2.55 -1.111C2.55 -1.714 2.207 -2.262 1.709 -2.262C1.395 -2.262 1.091 -2.162 0.842 -1.978V-3.457ZM0.857 -1.569C0.857 -1.649 0.857 -1.709 1.031 -1.823C1.106 -1.868 1.24 -1.933 1.41 -1.933C1.743 -1.933 2.117 -1.709 2.117 -1.111C2.117 -0.503 1.704 -0.279 1.355 -0.279C1.171 -0.279 0.996 -0.364 0.857 -0.588V-1.569Z' id='g1-98'/>
+<path d='M2.167 -0.543C1.918 -0.384 1.649 -0.294 1.34 -0.294C0.882 -0.294 0.613 -0.663 0.613 -1.111C0.613 -1.494 0.812 -1.943 1.355 -1.943C1.694 -1.943 1.853 -1.873 2.107 -1.714L2.172 -2.072C1.873 -2.222 1.743 -2.286 1.355 -2.286C0.608 -2.286 0.179 -1.684 0.179 -1.106C0.179 -0.498 0.658 0.05 1.335 0.05C1.684 0.05 1.983 -0.055 2.197 -0.179L2.167 -0.543Z' id='g1-99'/>
+<path d='M2.306 -3.457H1.888V-1.998C1.569 -2.232 1.245 -2.262 1.101 -2.262C0.578 -2.262 0.179 -1.738 0.179 -1.106S0.573 0.05 1.086 0.05C1.395 0.05 1.684 -0.09 1.873 -0.259V0H2.306V-3.457ZM1.873 -0.618C1.748 -0.413 1.559 -0.279 1.32 -0.279C0.971 -0.279 0.613 -0.523 0.613 -1.101C0.613 -1.724 1.036 -1.933 1.375 -1.933C1.574 -1.933 1.743 -1.848 1.873 -1.679V-0.618Z' id='g1-100'/>
+<path d='M2.142 -0.543C1.863 -0.344 1.549 -0.279 1.335 -0.279C0.902 -0.279 0.573 -0.633 0.558 -1.091H2.192C2.192 -1.32 2.167 -1.654 1.973 -1.938C1.793 -2.192 1.494 -2.286 1.25 -2.286C0.643 -2.286 0.174 -1.753 0.174 -1.121C0.174 -0.483 0.672 0.05 1.33 0.05C1.619 0.05 1.918 -0.035 2.172 -0.189L2.142 -0.543ZM0.593 -1.39C0.707 -1.788 1.001 -1.958 1.25 -1.958C1.469 -1.958 1.793 -1.853 1.888 -1.39H0.593Z' id='g1-101'/>
+<path d='M2.506 -2.262C2.396 -2.262 2.062 -2.257 1.684 -2.112L1.674 -2.107C1.494 -2.227 1.32 -2.262 1.176 -2.262C0.687 -2.262 0.324 -1.878 0.324 -1.45C0.324 -1.275 0.384 -1.096 0.498 -0.956C0.428 -0.872 0.354 -0.732 0.354 -0.543C0.354 -0.349 0.433 -0.224 0.478 -0.164C0.204 -0.005 0.149 0.224 0.149 0.344C0.149 0.722 0.672 1.021 1.32 1.021C1.973 1.021 2.491 0.722 2.491 0.344C2.491 -0.359 1.619 -0.359 1.405 -0.359H0.941C0.862 -0.359 0.648 -0.359 0.648 -0.618C0.648 -0.717 0.682 -0.767 0.687 -0.777C0.862 -0.667 1.036 -0.633 1.171 -0.633C1.659 -0.633 2.022 -1.016 2.022 -1.445C2.022 -1.604 1.978 -1.748 1.888 -1.883C1.868 -1.913 1.868 -1.918 1.868 -1.923C1.868 -1.943 2.167 -1.943 2.192 -1.943C2.197 -1.943 2.386 -1.943 2.565 -1.923L2.506 -2.262ZM1.176 -0.941C0.907 -0.941 0.707 -1.111 0.707 -1.445C0.707 -1.833 0.956 -1.953 1.171 -1.953C1.44 -1.953 1.639 -1.783 1.639 -1.45C1.639 -1.061 1.39 -0.941 1.176 -0.941ZM1.41 0.03C1.524 0.03 2.112 0.03 2.112 0.349C2.112 0.563 1.738 0.712 1.32 0.712S0.528 0.563 0.528 0.349C0.528 0.324 0.528 0.03 0.932 0.03H1.41Z' id='g1-103'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.161 -2.262 0.932 -2.012 0.832 -1.908V-3.457H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-104'/>
+<path d='M0.877 -3.417H0.374V-2.914H0.877V-3.417ZM0.837 -2.212H0.418V0H0.837V-2.212Z' id='g1-105'/>
+<path d='M0.986 -3.417H0.483V-2.914H0.986V-3.417ZM-0.324 0.847C-0.095 0.971 0.13 1.016 0.319 1.016C0.663 1.016 0.986 0.752 0.986 0.294V-2.212H0.568V0.329C0.568 0.418 0.568 0.498 0.463 0.583C0.349 0.667 0.209 0.667 0.164 0.667C-0.045 0.667 -0.174 0.573 -0.234 0.518L-0.324 0.847Z' id='g1-106'/>
+<path d='M0.837 -3.457H0.418V0H0.837V-3.457Z' id='g1-108'/>
+<path d='M3.786 -1.474C3.786 -1.863 3.671 -2.262 3.059 -2.262C2.64 -2.262 2.381 -2.017 2.262 -1.858C2.212 -1.993 2.087 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.21C2.316 -1.539 2.456 -1.933 2.839 -1.933C3.352 -1.933 3.352 -1.584 3.352 -1.44V0H3.786V-1.474Z' id='g1-109'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-110'/>
+<path d='M2.491 -1.091C2.491 -1.748 1.968 -2.286 1.32 -2.286S0.149 -1.743 0.149 -1.091C0.149 -0.458 0.677 0.05 1.32 0.05C1.968 0.05 2.491 -0.458 2.491 -1.091ZM1.32 -0.294C0.927 -0.294 0.583 -0.583 0.583 -1.146S0.951 -1.958 1.32 -1.958C1.694 -1.958 2.057 -1.699 2.057 -1.146C2.057 -0.578 1.704 -0.294 1.32 -0.294Z' id='g1-111'/>
+<path d='M0.857 -0.234C1.121 0.005 1.405 0.05 1.574 0.05C2.102 0.05 2.55 -0.453 2.55 -1.111C2.55 -1.709 2.222 -2.262 1.729 -2.262C1.504 -2.262 1.131 -2.197 0.842 -1.973V-2.212H0.423V0.966H0.857V-0.234ZM0.857 -1.654C0.971 -1.793 1.166 -1.918 1.405 -1.918C1.803 -1.918 2.117 -1.549 2.117 -1.111C2.117 -0.618 1.743 -0.279 1.355 -0.279C1.28 -0.279 1.156 -0.289 1.026 -0.394C0.877 -0.508 0.857 -0.583 0.857 -0.677V-1.654Z' id='g1-112'/>
+<path d='M0.842 -1.061C0.842 -1.599 1.29 -1.888 1.729 -1.893V-2.262C1.31 -2.257 1.006 -2.052 0.807 -1.788V-2.247H0.423V0H0.842V-1.061Z' id='g1-114'/>
+<path d='M1.818 -2.132C1.479 -2.271 1.23 -2.286 1.051 -2.286C0.927 -2.286 0.174 -2.286 0.174 -1.624C0.174 -1.39 0.304 -1.26 0.369 -1.2C0.543 -1.026 0.752 -0.986 1.016 -0.936C1.25 -0.892 1.519 -0.842 1.519 -0.603C1.519 -0.289 1.106 -0.289 1.036 -0.289C0.717 -0.289 0.418 -0.403 0.219 -0.543L0.149 -0.169C0.319 -0.085 0.623 0.05 1.036 0.05C1.26 0.05 1.479 0.015 1.664 -0.12C1.848 -0.259 1.908 -0.478 1.908 -0.648C1.908 -0.737 1.898 -0.932 1.689 -1.121C1.504 -1.285 1.325 -1.32 1.086 -1.365C0.792 -1.42 0.563 -1.465 0.563 -1.684C0.563 -1.968 0.927 -1.968 1.001 -1.968C1.285 -1.968 1.504 -1.908 1.753 -1.778L1.818 -2.132Z' id='g1-115'/>
+<path d='M0.936 -1.898H1.674V-2.212H0.936V-2.844H0.553V-2.212H0.1V-1.898H0.538V-0.638C0.538 -0.304 0.623 0.05 0.981 0.05S1.614 -0.065 1.763 -0.134L1.679 -0.453C1.514 -0.334 1.34 -0.294 1.2 -0.294C0.991 -0.294 0.936 -0.498 0.936 -0.727V-1.898Z' id='g1-116'/>
+<path d='M2.316 -2.212H1.883V-0.767C1.883 -0.369 1.544 -0.244 1.255 -0.244C0.887 -0.244 0.847 -0.344 0.847 -0.573V-2.212H0.413V-0.543C0.413 -0.1 0.608 0.05 0.956 0.05C1.161 0.05 1.599 0.01 1.898 -0.229V0H2.316V-2.212Z' id='g1-117'/>
+<path d='M3.537 -2.212H3.148C3.103 -2.072 2.824 -1.23 2.67 -0.712C2.63 -0.568 2.58 -0.408 2.565 -0.294H2.56C2.531 -0.498 2.356 -1.036 2.346 -1.071L1.978 -2.212H1.599C1.455 -1.783 1.081 -0.667 1.041 -0.304H1.036C0.996 -0.658 0.628 -1.758 0.548 -1.998C0.508 -2.117 0.508 -2.127 0.483 -2.212H0.075L0.802 0H1.22C1.225 -0.02 1.36 -0.413 1.534 -0.966C1.609 -1.21 1.758 -1.689 1.783 -1.908L1.788 -1.913C1.798 -1.808 1.828 -1.699 1.863 -1.574S1.933 -1.315 1.968 -1.2L2.351 0H2.809L3.537 -2.212Z' id='g1-119'/>
+<path d='M1.38 -1.141L2.346 -2.212H1.908L1.2 -1.395L0.478 -2.212H0.03L1.026 -1.141L0 0H0.443L1.2 -0.936L1.988 0H2.436L1.38 -1.141Z' id='g1-120'/>
+<path d='M2.361 -2.212H1.958C1.554 -1.26 1.275 -0.618 1.255 -0.314C1.245 -0.453 1.156 -0.702 1.066 -0.936C0.986 -1.136 0.907 -1.335 0.817 -1.529L0.503 -2.212H0.075L1.106 0C1.046 0.144 0.946 0.374 0.917 0.438C0.812 0.648 0.742 0.717 0.608 0.717C0.588 0.717 0.403 0.717 0.189 0.628L0.219 0.976C0.264 0.986 0.448 1.016 0.603 1.016C0.802 1.016 0.981 0.941 1.191 0.463L2.361 -2.212Z' id='g1-121'/>
+<path d='M2.112 -2.002V-2.212H0.219V-1.893H0.951C1.011 -1.893 1.071 -1.898 1.131 -1.898H1.519L0.149 -0.219V0H2.127V-0.334H1.355C1.295 -0.334 1.235 -0.329 1.176 -0.329H0.742L2.112 -2.002Z' id='g1-122'/>
+<path d='M1.445 -1.245C1.445 -1.41 1.305 -1.549 1.141 -1.549S0.837 -1.41 0.837 -1.245S0.976 -0.941 1.141 -0.941S1.445 -1.081 1.445 -1.245Z' id='g3-1'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g0-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g0-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g0-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g0-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g0-108'/>
+<path d='M1.462 -1.91C1.462 -2.851 2.197 -3.425 3.013 -3.434V-4.08C2.367 -4.071 1.775 -3.748 1.408 -3.219V-4.035H0.744V0H1.462V-1.91Z' id='g0-114'/>
+<path d='M3.165 -3.847C2.609 -4.098 2.197 -4.133 1.829 -4.133C1.623 -4.133 0.305 -4.133 0.305 -2.95C0.305 -2.52 0.565 -2.251 0.664 -2.152C1.004 -1.856 1.237 -1.811 1.847 -1.695C2.134 -1.641 2.645 -1.542 2.645 -1.085C2.645 -0.502 1.919 -0.502 1.802 -0.502C1.273 -0.502 0.762 -0.681 0.377 -0.95L0.26 -0.296C0.798 -0.009 1.345 0.099 1.802 0.099C2.385 0.099 3.318 -0.09 3.318 -1.157C3.318 -1.47 3.192 -1.784 2.878 -2.053C2.573 -2.313 2.304 -2.367 1.748 -2.475C1.426 -2.537 0.977 -2.618 0.977 -3.04C0.977 -3.569 1.614 -3.569 1.748 -3.569C2.403 -3.569 2.789 -3.362 3.049 -3.219L3.165 -3.847Z' id='g0-115'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g0-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g0-118'/>
+</defs>
+<g id='page8'>
+<path d='M194.617 212.793V203.938M253.285 212.793V203.938M311.953 212.793V203.938M370.625 212.793V203.938M429.293 212.793V203.938M194.617 69.836V78.692M253.285 69.836V78.692M311.953 69.836V78.692M370.625 69.836V78.692M429.293 69.836V78.692' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M165.281 208.192V203.938M223.953 208.192V203.938M282.621 208.192V203.938M341.289 208.192V203.938M399.957 208.192V203.938M458.629 208.192V203.938M165.281 74.442V78.692M223.953 74.442V78.692M282.621 74.442V78.692M341.289 74.442V78.692M399.957 74.442V78.692M458.629 74.442V78.692' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 203.938H140.199M135.949 172.625H140.199M135.949 141.317H140.199M135.949 110.004H140.199M135.949 78.692H140.199M487.961 203.938H483.711M487.961 172.625H483.711M487.961 141.317H483.711M487.961 110.004H483.711M487.961 78.692H483.711' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 203.938V78.692H487.961V203.938H135.949Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -21.265 59.571)'>
+<use x='168.285' xlink:href='#g2-97' y='158.367'/>
+<use x='172.353' xlink:href='#g2-108' y='158.367'/>
+<use x='174.373' xlink:href='#g2-108' y='158.367'/>
+<use x='176.393' xlink:href='#g2-111' y='158.367'/>
+<use x='180.863' xlink:href='#g2-99' y='158.367'/>
+<use x='184.627' xlink:href='#g2-45' y='158.367'/>
+<use x='187.449' xlink:href='#g2-116' y='158.367'/>
+<use x='190.507' xlink:href='#g2-101' y='158.367'/>
+<use x='194.271' xlink:href='#g2-115' y='158.367'/>
+<use x='197.517' xlink:href='#g2-116' y='158.367'/>
+<use x='200.575' xlink:href='#g2-49' y='158.367'/>
+</g>
+<g transform='matrix(1 0 0 1 36.532 59.571)'>
+<use x='168.285' xlink:href='#g2-97' y='158.367'/>
+<use x='172.353' xlink:href='#g2-108' y='158.367'/>
+<use x='174.373' xlink:href='#g2-108' y='158.367'/>
+<use x='176.393' xlink:href='#g2-111' y='158.367'/>
+<use x='180.863' xlink:href='#g2-99' y='158.367'/>
+<use x='184.627' xlink:href='#g2-45' y='158.367'/>
+<use x='187.449' xlink:href='#g2-116' y='158.367'/>
+<use x='190.507' xlink:href='#g2-101' y='158.367'/>
+<use x='194.271' xlink:href='#g2-115' y='158.367'/>
+<use x='197.517' xlink:href='#g2-116' y='158.367'/>
+<use x='200.575' xlink:href='#g2-78' y='158.367'/>
+</g>
+<g transform='matrix(1 0 0 1 94.98 59.571)'>
+<use x='168.285' xlink:href='#g2-115' y='158.367'/>
+<use x='171.531' xlink:href='#g2-104' y='158.367'/>
+<use x='175.904' xlink:href='#g2-54' y='158.367'/>
+<use x='180.138' xlink:href='#g2-98' y='158.367'/>
+<use x='184.746' xlink:href='#g2-101' y='158.367'/>
+<use x='188.509' xlink:href='#g2-110' y='158.367'/>
+<use x='192.882' xlink:href='#g2-99' y='158.367'/>
+<use x='196.646' xlink:href='#g2-104' y='158.367'/>
+<use x='201.018' xlink:href='#g2-78' y='158.367'/>
+</g>
+<g transform='matrix(1 0 0 1 153.649 59.571)'>
+<use x='168.285' xlink:href='#g2-115' y='158.367'/>
+<use x='171.531' xlink:href='#g2-104' y='158.367'/>
+<use x='175.904' xlink:href='#g2-56' y='158.367'/>
+<use x='180.138' xlink:href='#g2-98' y='158.367'/>
+<use x='184.746' xlink:href='#g2-101' y='158.367'/>
+<use x='188.509' xlink:href='#g2-110' y='158.367'/>
+<use x='192.882' xlink:href='#g2-99' y='158.367'/>
+<use x='196.646' xlink:href='#g2-104' y='158.367'/>
+<use x='201.018' xlink:href='#g2-78' y='158.367'/>
+</g>
+<g transform='matrix(1 0 0 1 207.225 59.571)'>
+<use x='168.285' xlink:href='#g2-120' y='158.367'/>
+<use x='172.187' xlink:href='#g2-109' y='158.367'/>
+<use x='178.912' xlink:href='#g2-97' y='158.367'/>
+<use x='182.98' xlink:href='#g2-108' y='158.367'/>
+<use x='185' xlink:href='#g2-108' y='158.367'/>
+<use x='187.02' xlink:href='#g2-111' y='158.367'/>
+<use x='191.49' xlink:href='#g2-99' y='158.367'/>
+<use x='195.254' xlink:href='#g2-45' y='158.367'/>
+<use x='198.076' xlink:href='#g2-116' y='158.367'/>
+<use x='201.134' xlink:href='#g2-101' y='158.367'/>
+<use x='204.898' xlink:href='#g2-115' y='158.367'/>
+<use x='208.144' xlink:href='#g2-116' y='158.367'/>
+<use x='211.202' xlink:href='#g2-78' y='158.367'/>
+</g>
+<g transform='matrix(1 0 0 1 263.494 59.571)'>
+<use x='168.285' xlink:href='#g2-99' y='158.367'/>
+<use x='172.049' xlink:href='#g2-97' y='158.367'/>
+<use x='176.117' xlink:href='#g2-99' y='158.367'/>
+<use x='179.88' xlink:href='#g2-104' y='158.367'/>
+<use x='184.253' xlink:href='#g2-101' y='158.367'/>
+<use x='188.017' xlink:href='#g2-45' y='158.367'/>
+<use x='190.839' xlink:href='#g2-115' y='158.367'/>
+<use x='194.086' xlink:href='#g2-99' y='158.367'/>
+<use x='197.849' xlink:href='#g2-114' y='158.367'/>
+<use x='200.741' xlink:href='#g2-97' y='158.367'/>
+<use x='204.81' xlink:href='#g2-116' y='158.367'/>
+<use x='207.868' xlink:href='#g2-99' y='158.367'/>
+<use x='211.631' xlink:href='#g2-104' y='158.367'/>
+<use x='216.004' xlink:href='#g2-78' y='158.367'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 47.205)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-120' y='158.367'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 15.894)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-53' y='158.367'/>
+<use x='175.048' xlink:href='#g1-120' y='158.367'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -15.418)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-120' y='158.367'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -46.729)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-53' y='158.367'/>
+<use x='175.048' xlink:href='#g1-120' y='158.367'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -78.041)'>
+<use x='168.285' xlink:href='#g1-50' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-120' y='158.367'/>
+</g>
+<path clip-path='url(#clip8)' d='M135.949 141.317H487.961' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M145.356 203.938H148.594V141.317H145.356ZM204.028 203.938H207.266V141.317H204.028ZM262.695 203.938H265.934V141.317H262.695ZM321.363 203.938H324.602V141.317H321.363ZM380.031 203.938H383.27V141.317H380.031ZM438.703 203.938H441.942V141.317H438.703Z' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M145.356 203.938H148.594V141.317H145.356ZM204.028 203.938H207.266V141.317H204.028ZM262.695 203.938H265.934V141.317H262.695ZM321.363 203.938H324.602V141.317H321.363ZM380.031 203.938H383.27V141.317H380.031ZM438.703 203.938H441.942V141.317H438.703Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M146.977 141.317V141.317' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M144.984 141.317H148.968' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M146.977 141.317V141.317' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M144.984 141.317H148.968' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M205.645 141.317V141.317' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M203.652 141.317H207.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M205.645 141.317V141.317' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M203.652 141.317H207.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M264.313 141.317V141.317' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M262.32 141.317H266.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M264.313 141.317V141.317' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M262.32 141.317H266.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M322.984 141.317V141.317' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M320.992 141.317H324.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M322.984 141.317V141.317' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M320.992 141.317H324.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M381.652 141.317V141.317' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M379.66 141.317H383.644' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M381.652 141.317V141.317' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M379.66 141.317H383.644' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M440.32 141.317V141.317' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M438.328 141.317H442.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M440.32 141.317V141.317' fill='#f0e0f0'/>
+<path clip-path='url(#clip8)' d='M438.328 141.317H442.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M150.586 203.938H153.824V137.809H150.586ZM209.258 203.938H212.496V144.196H209.258ZM267.926 203.938H271.164V140.563H267.926ZM326.594 203.938H329.832V141.754H326.594ZM385.262 203.938H388.5V188.406H385.262ZM443.934 203.938H447.172V110.754H443.934Z' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M150.586 203.938H153.824V137.809H150.586ZM209.258 203.938H212.496V144.196H209.258ZM267.926 203.938H271.164V140.563H267.926ZM326.594 203.938H329.832V141.754H326.594ZM385.262 203.938H388.5V188.406H385.262ZM443.934 203.938H447.172V110.754H443.934Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M152.207 137.809V137.809' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M150.215 137.809H154.199' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M152.207 137.809V137.809' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M150.215 137.809H154.199' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M210.875 144.196V144.196' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M208.883 144.195H212.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M210.875 144.196V144.196' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M208.883 144.195H212.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M269.543 140.563V140.563' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M267.551 140.562H271.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M269.543 140.563V140.563' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M267.551 140.562H271.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M328.215 141.754V141.754' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M326.223 141.754H330.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M328.215 141.754V141.754' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M326.223 141.754H330.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M386.883 188.406V188.406' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M384.891 188.406H388.875' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M386.883 188.406V188.406' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M384.891 188.406H388.875' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M445.551 110.754V110.754' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M443.559 110.754H447.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M445.551 110.754V110.754' fill='#e1c2e1'/>
+<path clip-path='url(#clip8)' d='M443.559 110.754H447.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M155.817 203.938H159.055V140.313H155.817ZM214.488 203.938H217.727V147.453H214.488ZM273.156 203.938H276.395V139.625H273.156ZM331.824 203.938H335.063V130.043H331.824ZM390.492 203.938H393.731V170.809H390.492ZM449.164 203.938H452.402V120.024H449.164Z' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M155.817 203.938H159.055V140.313H155.817ZM214.488 203.938H217.727V147.453H214.488ZM273.156 203.938H276.395V139.625H273.156ZM331.824 203.938H335.063V130.043H331.824ZM390.492 203.938H393.731V170.809H390.492ZM449.164 203.938H452.402V120.024H449.164Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M157.438 140.313V140.313' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M155.445 140.313H159.429' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M157.438 140.313V140.313' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M155.445 140.313H159.429' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M216.106 147.453V147.453' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M214.113 147.453H218.097' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M216.106 147.453V147.453' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M214.113 147.453H218.097' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M274.774 139.625V139.625' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M272.781 139.625H276.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M274.774 139.625V139.625' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M272.781 139.625H276.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M333.445 130.043V130.043' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M331.453 130.043H335.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M333.445 130.043V130.043' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M331.453 130.043H335.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M392.113 170.809V170.809' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M390.121 170.808H394.105' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M392.113 170.809V170.809' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M390.121 170.808H394.105' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M450.781 120.024V120.024' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M448.789 120.023H452.773' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M450.781 120.024V120.024' fill='#d1a3d1'/>
+<path clip-path='url(#clip8)' d='M448.789 120.023H452.773' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M161.047 203.938H164.285V137.559H161.047ZM219.719 203.938H222.957V144.383H219.719ZM278.387 203.938H281.625V131.36H278.387ZM337.055 203.938H340.293V128.164H337.055ZM395.723 203.938H398.961V138.121H395.723ZM454.395 203.938H457.633V137.559H454.395Z' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M161.047 203.938H164.285V137.559H161.047ZM219.719 203.938H222.957V144.383H219.719ZM278.387 203.938H281.625V131.36H278.387ZM337.055 203.938H340.293V128.164H337.055ZM395.723 203.938H398.961V138.121H395.723ZM454.395 203.938H457.633V137.559H454.395Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M162.668 137.559V137.559' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M160.676 137.558H164.66' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M162.668 137.559V137.559' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M160.676 137.558H164.66' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M221.336 144.383V144.383' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M219.344 144.382H223.328' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M221.336 144.383V144.383' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M219.344 144.382H223.328' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M280.004 131.36V131.36' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M278.012 131.36H282' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M280.004 131.36V131.36' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M278.012 131.36H282' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M338.676 128.164V128.164' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M336.684 128.164H340.668' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M338.676 128.164V128.164' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M336.684 128.164H340.668' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M397.344 138.121V138.121' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M395.352 138.121H399.336' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M397.344 138.121V138.121' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M395.352 138.121H399.336' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M456.012 137.559V137.559' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M454.02 137.558H458.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M456.012 137.559V137.559' fill='#c285c2'/>
+<path clip-path='url(#clip8)' d='M454.02 137.558H458.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M166.278 203.938H169.516V126.723H166.278ZM224.949 203.938H228.188V141.629H224.949ZM283.617 203.938H286.856V104.055H283.617ZM342.285 203.938H345.524V122.84H342.285ZM400.953 203.938H404.192V186.215H400.953ZM459.625 203.938H462.863V140.188H459.625Z' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M166.278 203.938H169.516V126.723H166.278ZM224.949 203.938H228.188V141.629H224.949ZM283.617 203.938H286.856V104.055H283.617ZM342.285 203.938H345.524V122.84H342.285ZM400.953 203.938H404.192V186.215H400.953ZM459.625 203.938H462.863V140.188H459.625Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M167.899 126.723V126.723' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M165.906 126.723H169.89' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M167.899 126.723V126.723' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M165.906 126.723H169.89' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M226.567 141.629V141.629' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M224.574 141.629H228.558' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M226.567 141.629V141.629' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M224.574 141.629H228.558' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M285.234 104.055V104.055' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M283.242 104.055H287.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M285.234 104.055V104.055' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M283.242 104.055H287.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M343.906 122.84V122.84' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M341.914 122.84H345.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M343.906 122.84V122.84' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M341.914 122.84H345.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M402.574 186.215V186.215' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M400.582 186.215H404.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M402.574 186.215V186.215' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M400.582 186.215H404.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M461.242 140.188V140.188' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M459.25 140.188H463.234' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M461.242 140.188V140.188' fill='#b366b3'/>
+<path clip-path='url(#clip8)' d='M459.25 140.188H463.234' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M171.508 203.938H174.746V115.828H171.508ZM230.18 203.938H233.414V91.215H230.18ZM288.848 203.938H292.086V95.977H288.848ZM347.516 203.938H350.754V78.692H347.516ZM406.184 203.938H409.422V167.68H406.184ZM464.856 203.938H468.094V130.856H464.856Z' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M171.508 203.938H174.746V115.828H171.508ZM230.18 203.938H233.414V91.215H230.18ZM288.848 203.938H292.086V95.977H288.848ZM347.516 203.938H350.754V78.692H347.516ZM406.184 203.938H409.422V167.68H406.184ZM464.856 203.938H468.094V130.856H464.856Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M173.129 115.828V115.828' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M171.137 115.828H175.121' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M173.129 115.828V115.828' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M171.137 115.828H175.121' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M231.797 91.215V91.215' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M229.805 91.215H233.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M231.797 91.215V91.215' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M229.805 91.215H233.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M290.465 95.977V95.977' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M288.473 95.977H292.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M290.465 95.977V95.977' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M288.473 95.977H292.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M349.137 78.692V78.692' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M347.145 78.691H351.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M349.137 78.692V78.692' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M347.145 78.691H351.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M407.805 167.68V167.68' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M405.813 167.68H409.797' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M407.805 167.68V167.68' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M405.813 167.68H409.797' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M466.473 130.856V130.856' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M464.481 130.856H468.465' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M466.473 130.856V130.856' fill='#a447a4'/>
+<path clip-path='url(#clip8)' d='M464.481 130.856H468.465' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M176.738 203.938H179.977V126.973H176.738ZM235.41 203.938H238.645V141.754H235.41ZM294.078 203.938H297.317V104.18H294.078ZM352.746 203.938H355.984V122.903H352.746ZM411.414 203.938H414.652V186.278H411.414ZM470.086 203.938H473.324V140.688H470.086Z' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M176.738 203.938H179.977V126.973H176.738ZM235.41 203.938H238.645V141.754H235.41ZM294.078 203.938H297.317V104.18H294.078ZM352.746 203.938H355.984V122.903H352.746ZM411.414 203.938H414.652V186.278H411.414ZM470.086 203.938H473.324V140.688H470.086Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M178.36 126.973V126.973' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M176.367 126.972H180.351' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M178.36 126.973V126.973' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M176.367 126.972H180.351' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M237.027 141.754V141.754' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M235.035 141.754H239.019' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M237.027 141.754V141.754' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M235.035 141.754H239.019' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M295.695 104.18V104.18' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M293.703 104.179H297.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M295.695 104.18V104.18' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M293.703 104.179H297.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M354.367 122.903V122.903' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M352.375 122.903H356.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M354.367 122.903V122.903' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M352.375 122.903H356.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M413.035 186.278V186.278' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M411.043 186.277H415.027' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M413.035 186.278V186.278' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M411.043 186.277H415.027' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M471.703 140.688V140.688' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M469.711 140.688H473.695' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M471.703 140.688V140.688' fill='#942994'/>
+<path clip-path='url(#clip8)' d='M469.711 140.688H473.695' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M416.645 203.938H419.883V157.973H416.645ZM181.969 203.938H185.207V135.051H181.969ZM240.641 203.938H243.875V142.129H240.641ZM299.309 203.938H302.547V134.676H299.309ZM357.977 203.938H361.215V140.25H357.977ZM475.317 203.938H478.555V131.235H475.317Z' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M416.645 203.938H419.883V157.973H416.645ZM181.969 203.938H185.207V135.051H181.969ZM240.641 203.938H243.875V142.129H240.641ZM299.309 203.938H302.547V134.676H299.309ZM357.977 203.938H361.215V140.25H357.977ZM475.317 203.938H478.555V131.235H475.317Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M418.266 157.973V157.973' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M416.274 157.973H420.258' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M418.266 157.973V157.973' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M416.274 157.973H420.258' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M183.59 135.051V135.051' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M181.598 135.051H185.583' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M183.59 135.051V135.051' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M181.598 135.051H185.583' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M242.258 142.129V142.129' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M240.266 142.129H244.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M242.258 142.129V142.129' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M240.266 142.129H244.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M300.926 134.676V134.676' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M298.933 134.676H302.921' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M300.926 134.676V134.676' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M298.933 134.676H302.921' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M359.598 140.25V140.25' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M357.606 140.25H361.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M359.598 140.25V140.25' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M357.606 140.25H361.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M476.934 131.235V131.235' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M474.942 131.235H478.926' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M476.934 131.235V131.235' fill='#850a85'/>
+<path clip-path='url(#clip8)' d='M474.942 131.235H478.926' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip8)' d='M418.387 251.164H487.762V229.188H418.387Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 253.422 88.837)'>
+<use x='168.285' xlink:href='#g1-114' y='147.13'/>
+<use x='170.092' xlink:href='#g1-53' y='147.13'/>
+<use x='172.739' xlink:href='#g1-97' y='147.13'/>
+<use x='175.281' xlink:href='#g1-45' y='147.13'/>
+<use x='177.046' xlink:href='#g1-52' y='147.13'/>
+<use x='179.692' xlink:href='#g1-120' y='147.13'/>
+<use x='182.131' xlink:href='#g1-108' y='147.13'/>
+<use x='183.393' xlink:href='#g1-97' y='147.13'/>
+<use x='185.789' xlink:href='#g1-114' y='147.13'/>
+<use x='187.596' xlink:href='#g1-103' y='147.13'/>
+<use x='190.243' xlink:href='#g1-101' y='147.13'/>
+<use x='192.595' xlink:href='#g1-44' y='147.13'/>
+<use x='195.83' xlink:href='#g1-49' y='147.13'/>
+<use x='198.476' xlink:href='#g1-50' y='147.13'/>
+<use x='201.122' xlink:href='#g1-56' y='147.13'/>
+<use x='203.769' xlink:href='#g1-71' y='147.13'/>
+<use x='207.297' xlink:href='#g1-98' y='147.13'/>
+<use x='168.285' xlink:href='#g1-49' y='152.749'/>
+<use x='170.931' xlink:href='#g1-54' y='152.749'/>
+<use x='173.578' xlink:href='#g1-45' y='152.749'/>
+<use x='175.342' xlink:href='#g1-99' y='152.749'/>
+<use x='177.694' xlink:href='#g1-111' y='152.749'/>
+<use x='180.193' xlink:href='#g1-114' y='152.749'/>
+<use x='182.001' xlink:href='#g1-101' y='152.749'/>
+<use x='186.118' xlink:href='#g1-65' y='152.749'/>
+<use x='189.646' xlink:href='#g1-77' y='152.749'/>
+<use x='194.264' xlink:href='#g1-68' y='152.749'/>
+<use x='199.851' xlink:href='#g1-69' y='152.749'/>
+<use x='203.025' xlink:href='#g1-112' y='152.749'/>
+<use x='205.611' xlink:href='#g1-121' y='152.749'/>
+<use x='208.049' xlink:href='#g1-99' y='152.749'/>
+<use x='212.166' xlink:href='#g1-64' y='152.749'/>
+<use x='215.695' xlink:href='#g1-50' y='152.749'/>
+<use x='218.341' xlink:href='#g1-46' y='152.749'/>
+<use x='219.811' xlink:href='#g1-53' y='152.749'/>
+<use x='222.457' xlink:href='#g1-71' y='152.749'/>
+<use x='225.986' xlink:href='#g1-104' y='152.749'/>
+<use x='228.719' xlink:href='#g1-122' y='152.749'/>
+<use x='168.285' xlink:href='#g1-85' y='158.367'/>
+<use x='171.917' xlink:href='#g1-98' y='158.367'/>
+<use x='174.65' xlink:href='#g1-117' y='158.367'/>
+<use x='177.383' xlink:href='#g1-110' y='158.367'/>
+<use x='180.116' xlink:href='#g1-116' y='158.367'/>
+<use x='182.027' xlink:href='#g1-117' y='158.367'/>
+<use x='186.524' xlink:href='#g1-49' y='158.367'/>
+<use x='189.17' xlink:href='#g1-56' y='158.367'/>
+<use x='191.817' xlink:href='#g1-46' y='158.367'/>
+<use x='193.287' xlink:href='#g1-48' y='158.367'/>
+<use x='195.933' xlink:href='#g1-52' y='158.367'/>
+<use x='198.58' xlink:href='#g1-46' y='158.367'/>
+<use x='200.05' xlink:href='#g1-49' y='158.367'/>
+<use x='202.696' xlink:href='#g1-44' y='158.367'/>
+<use x='205.931' xlink:href='#g1-71' y='158.367'/>
+<use x='209.459' xlink:href='#g1-67' y='158.367'/>
+<use x='212.841' xlink:href='#g1-67' y='158.367'/>
+<use x='217.986' xlink:href='#g1-55' y='158.367'/>
+<use x='220.633' xlink:href='#g1-46' y='158.367'/>
+<use x='222.103' xlink:href='#g1-51' y='158.367'/>
+<use x='224.749' xlink:href='#g1-46' y='158.367'/>
+<use x='226.219' xlink:href='#g1-48' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 -9.757 303.575)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-48' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 48.912 303.575)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-48' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 107.581 303.575)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-48' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 166.25 303.575)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-48' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 224.919 303.575)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-48' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 283.588 303.575)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-48' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 -4.526 300.068)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-54' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 54.143 306.456)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-57' y='158.367'/>
+<use x='175.048' xlink:href='#g1-53' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 112.812 302.824)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-49' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 171.481 304.013)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-57' y='158.367'/>
+<use x='175.048' xlink:href='#g1-57' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 230.15 350.668)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-50' y='158.367'/>
+<use x='175.048' xlink:href='#g1-53' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 288.819 273.015)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-52' y='158.367'/>
+<use x='175.048' xlink:href='#g1-57' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 0.704 302.573)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-50' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 59.373 309.712)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-57' y='158.367'/>
+<use x='175.048' xlink:href='#g1-48' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 118.042 301.884)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-51' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 176.711 292.303)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-49' y='158.367'/>
+<use x='175.048' xlink:href='#g1-56' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 235.38 333.071)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-53' y='158.367'/>
+<use x='175.048' xlink:href='#g1-51' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 294.049 282.283)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-51' y='158.367'/>
+<use x='175.048' xlink:href='#g1-52' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 5.935 299.818)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-54' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 64.604 306.644)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-57' y='158.367'/>
+<use x='175.048' xlink:href='#g1-53' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 123.273 293.618)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-49' y='158.367'/>
+<use x='175.048' xlink:href='#g1-54' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 181.942 290.424)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-50' y='158.367'/>
+<use x='175.048' xlink:href='#g1-49' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 240.611 300.381)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-53' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 299.28 299.818)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-54' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 11.165 288.984)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-50' y='158.367'/>
+<use x='175.048' xlink:href='#g1-51' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 69.834 303.888)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-57' y='158.367'/>
+<use x='175.048' xlink:href='#g1-57' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 128.503 266.314)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-53' y='158.367'/>
+<use x='175.048' xlink:href='#g1-57' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 187.172 285.101)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-50' y='158.367'/>
+<use x='175.048' xlink:href='#g1-57' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 245.841 348.476)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-50' y='158.367'/>
+<use x='175.048' xlink:href='#g1-56' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 304.51 302.448)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-50' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 16.396 278.088)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-52' y='158.367'/>
+<use x='175.048' xlink:href='#g1-49' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 75.065 253.477)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-56' y='158.367'/>
+<use x='175.048' xlink:href='#g1-48' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 133.734 258.236)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-55' y='158.367'/>
+<use x='175.048' xlink:href='#g1-50' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 192.403 240.952)'>
+<use x='163.396' xlink:href='#g3-1' y='158.367'/>
+<use x='166.901' xlink:href='#g3-1' y='158.367'/>
+<use x='170.407' xlink:href='#g3-1' y='158.367'/>
+<use x='173.912' xlink:href='#g1-50' y='158.367'/>
+<use x='176.558' xlink:href='#g1-46' y='158.367'/>
+<use x='178.029' xlink:href='#g1-52' y='158.367'/>
+<use x='180.675' xlink:href='#g1-54' y='158.367'/>
+<use x='183.321' xlink:href='#g1-120' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 251.072 329.939)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-53' y='158.367'/>
+<use x='175.048' xlink:href='#g1-56' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 309.741 293.117)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-49' y='158.367'/>
+<use x='175.048' xlink:href='#g1-55' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 21.626 289.234)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-50' y='158.367'/>
+<use x='175.048' xlink:href='#g1-51' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 80.295 304.013)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-57' y='158.367'/>
+<use x='175.048' xlink:href='#g1-57' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 138.964 266.44)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-53' y='158.367'/>
+<use x='175.048' xlink:href='#g1-57' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 197.633 285.164)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-50' y='158.367'/>
+<use x='175.048' xlink:href='#g1-57' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 256.302 348.538)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-50' y='158.367'/>
+<use x='175.048' xlink:href='#g1-56' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 314.971 302.949)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-49' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 261.532 320.233)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-55' y='158.367'/>
+<use x='175.048' xlink:href='#g1-51' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 26.856 297.313)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-49' y='158.367'/>
+<use x='175.048' xlink:href='#g1-48' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 85.525 304.389)'>
+<use x='168.285' xlink:href='#g1-48' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-57' y='158.367'/>
+<use x='175.048' xlink:href='#g1-57' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 144.194 296.937)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-49' y='158.367'/>
+<use x='175.048' xlink:href='#g1-49' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 202.863 302.511)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-48' y='158.367'/>
+<use x='175.048' xlink:href='#g1-50' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 320.201 293.493)'>
+<use x='168.285' xlink:href='#g1-49' y='158.367'/>
+<use x='170.931' xlink:href='#g1-46' y='158.367'/>
+<use x='172.401' xlink:href='#g1-49' y='158.367'/>
+<use x='175.048' xlink:href='#g1-54' y='158.367'/>
+</g>
+<g transform='matrix(0 -1 1 0 -45.209 361.494)'>
+<use x='168.285' xlink:href='#g0-82' y='158.367'/>
+<use x='174.255' xlink:href='#g0-101' y='158.367'/>
+<use x='178.351' xlink:href='#g0-108' y='158.367'/>
+<use x='180.551' xlink:href='#g0-97' y='158.367'/>
+<use x='184.979' xlink:href='#g0-116' y='158.367'/>
+<use x='188.307' xlink:href='#g0-105' y='158.367'/>
+<use x='190.507' xlink:href='#g0-118' y='158.367'/>
+<use x='194.755' xlink:href='#g0-101' y='158.367'/>
+<use x='201.922' xlink:href='#g0-114' y='158.367'/>
+<use x='205.07' xlink:href='#g0-115' y='158.367'/>
+<use x='208.603' xlink:href='#g0-115' y='158.367'/>
+<use x='215.207' xlink:href='#g2-40' y='158.367'/>
+<use x='218.5' xlink:href='#g2-108' y='158.367'/>
+<use x='220.521' xlink:href='#g2-111' y='158.367'/>
+<use x='224.52' xlink:href='#g2-119' y='158.367'/>
+<use x='230.068' xlink:href='#g2-101' y='158.367'/>
+<use x='233.832' xlink:href='#g2-114' y='158.367'/>
+<use x='239.547' xlink:href='#g2-105' y='158.367'/>
+<use x='241.567' xlink:href='#g2-115' y='158.367'/>
+<use x='247.636' xlink:href='#g2-98' y='158.367'/>
+<use x='252.244' xlink:href='#g2-101' y='158.367'/>
+<use x='256.008' xlink:href='#g2-116' y='158.367'/>
+<use x='259.066' xlink:href='#g2-116' y='158.367'/>
+<use x='262.124' xlink:href='#g2-101' y='158.367'/>
+<use x='265.887' xlink:href='#g2-114' y='158.367'/>
+<use x='268.779' xlink:href='#g2-41' y='158.367'/>
+</g>
+<path d='M136.149 249.309H349.488V232.317H136.149Z' fill='#ffffff'/>
+<path d='M136.149 249.309H349.488V232.317H136.149Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path d='M139.336 242.804H142.324V234.835H139.336ZM145.313 242.804H148.301V236.828H145.313Z' fill='#f0e0f0'/>
+<path d='M139.336 242.804H142.324V234.835H139.336ZM145.313 242.804H148.301V236.828H145.313Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -33.522 84.43)'>
+<use x='185.011' xlink:href='#g2-109' y='158.367'/>
+<use x='191.736' xlink:href='#g2-105' y='158.367'/>
+</g>
+<path d='M163.422 242.805H166.41V234.836H163.422ZM169.398 242.805H172.391V236.828H169.398Z' fill='#e1c2e1'/>
+<path d='M163.422 242.804H166.41V234.835H163.422ZM169.398 242.804H172.391V236.828H169.398Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.56 83.976)'>
+<use x='208.137' xlink:href='#g2-116' y='158.367'/>
+<use x='211.195' xlink:href='#g2-99' y='158.367'/>
+</g>
+<path d='M185.586 242.805H188.574V234.836H185.586ZM191.563 242.805H194.555V236.828H191.563Z' fill='#d1a3d1'/>
+<path d='M185.586 242.804H188.574V234.835H185.586ZM191.563 242.804H194.555V236.828H191.563Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.159 84.43)'>
+<use x='229.9' xlink:href='#g2-106' y='158.367'/>
+<use x='232.155' xlink:href='#g2-101' y='158.367'/>
+</g>
+<path d='M206.949 242.805H209.938V234.836H206.949ZM212.926 242.805H215.914V236.828H212.926Z' fill='#c285c2'/>
+<path d='M206.949 242.804H209.938V234.835H206.949ZM212.926 242.804H215.914V236.828H212.926Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.958 83.47)'>
+<use x='252.061' xlink:href='#g2-115' y='158.367'/>
+<use x='255.307' xlink:href='#g2-110' y='158.367'/>
+</g>
+<path d='M229.91 242.805H232.898V234.836H229.91ZM235.887 242.805H238.875V236.828H235.887Z' fill='#b366b3'/>
+<path d='M229.91 242.804H232.898V234.835H229.91ZM235.887 242.804H238.875V236.828H235.887Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.781 83.47)'>
+<use x='274.845' xlink:href='#g2-114' y='158.367'/>
+<use x='277.737' xlink:href='#g2-112' y='158.367'/>
+</g>
+<path d='M252.516 242.805H255.504V234.836H252.516ZM258.496 242.805H261.484V236.828H258.496Z' fill='#a447a4'/>
+<path d='M252.516 242.804H255.504V234.835H252.516ZM258.496 242.804H261.484V236.828H258.496Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -39.001 84.467)'>
+<use x='303.672' xlink:href='#g2-104' y='158.367'/>
+<use x='308.045' xlink:href='#g2-111' y='158.367'/>
+<use x='312.279' xlink:href='#g2-97' y='158.367'/>
+<use x='316.112' xlink:href='#g2-114' y='158.367'/>
+<use x='319.004' xlink:href='#g2-100' y='158.367'/>
+</g>
+<path d='M287.563 242.805H290.551V234.836H287.563ZM293.543 242.805H296.531V236.828H293.543Z' fill='#942994'/>
+<path d='M287.563 242.804H290.551V234.835H287.563ZM293.543 242.804H296.531V236.828H293.543Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.472 84.467)'>
+<use x='337.19' xlink:href='#g2-103' y='158.367'/>
+<use x='341.424' xlink:href='#g2-108' y='158.367'/>
+<use x='343.444' xlink:href='#g2-105' y='158.367'/>
+<use x='345.464' xlink:href='#g2-98' y='158.367'/>
+<use x='350.072' xlink:href='#g2-99' y='158.367'/>
+</g>
+<path d='M319.551 242.805H322.539V234.836H319.551ZM325.527 242.805H328.52V236.828H325.527Z' fill='#850a85'/>
+<path d='M319.551 242.804H322.539V234.835H319.551ZM325.527 242.804H328.52V236.828H325.527Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -35.051 84.467)'>
+<use x='366.757' xlink:href='#g2-116' y='158.367'/>
+<use x='369.815' xlink:href='#g2-98' y='158.367'/>
+<use x='374.187' xlink:href='#g2-98' y='158.367'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='182.025pt' version='1.1' viewBox='106.736 51.674 270.486 182.025' width='270.486pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip6'>
+<path d='M135.949 186.074H355.586V60.828H135.949Z'/>
+</clipPath>
+<use id='g2-40' transform='scale(1.6)' xlink:href='#g1-40'/>
+<use id='g2-41' transform='scale(1.6)' xlink:href='#g1-41'/>
+<use id='g2-46' transform='scale(1.6)' xlink:href='#g1-46'/>
+<use id='g2-48' transform='scale(1.6)' xlink:href='#g1-48'/>
+<use id='g2-50' transform='scale(1.6)' xlink:href='#g1-50'/>
+<use id='g2-51' transform='scale(1.6)' xlink:href='#g1-51'/>
+<use id='g2-52' transform='scale(1.6)' xlink:href='#g1-52'/>
+<use id='g2-54' transform='scale(1.6)' xlink:href='#g1-54'/>
+<use id='g2-56' transform='scale(1.6)' xlink:href='#g1-56'/>
+<use id='g2-97' transform='scale(1.6)' xlink:href='#g1-97'/>
+<use id='g2-98' transform='scale(1.6)' xlink:href='#g1-98'/>
+<use id='g2-99' transform='scale(1.6)' xlink:href='#g1-99'/>
+<use id='g2-100' transform='scale(1.6)' xlink:href='#g1-100'/>
+<use id='g2-101' transform='scale(1.6)' xlink:href='#g1-101'/>
+<use id='g2-103' transform='scale(1.6)' xlink:href='#g1-103'/>
+<use id='g2-104' transform='scale(1.6)' xlink:href='#g1-104'/>
+<use id='g2-105' transform='scale(1.6)' xlink:href='#g1-105'/>
+<use id='g2-106' transform='scale(1.6)' xlink:href='#g1-106'/>
+<use id='g2-107' transform='scale(1.6)' xlink:href='#g1-107'/>
+<use id='g2-108' transform='scale(1.6)' xlink:href='#g1-108'/>
+<use id='g2-109' transform='scale(1.6)' xlink:href='#g1-109'/>
+<use id='g2-110' transform='scale(1.6)' xlink:href='#g1-110'/>
+<use id='g2-111' transform='scale(1.6)' xlink:href='#g1-111'/>
+<use id='g2-112' transform='scale(1.6)' xlink:href='#g1-112'/>
+<use id='g2-114' transform='scale(1.6)' xlink:href='#g1-114'/>
+<use id='g2-115' transform='scale(1.6)' xlink:href='#g1-115'/>
+<use id='g2-116' transform='scale(1.6)' xlink:href='#g1-116'/>
+<use id='g2-119' transform='scale(1.6)' xlink:href='#g1-119'/>
+<use id='g2-120' transform='scale(1.6)' xlink:href='#g1-120'/>
+<path d='M1.519 -3.736C1.435 -3.736 1.425 -3.736 1.365 -3.681C0.737 -3.133 0.418 -2.247 0.418 -1.245C0.418 -0.304 0.702 0.603 1.36 1.181C1.425 1.245 1.435 1.245 1.519 1.245H1.758C1.743 1.235 1.26 0.822 1.031 0.045C0.912 -0.344 0.852 -0.752 0.852 -1.245C0.852 -2.969 1.659 -3.651 1.758 -3.736H1.519Z' id='g1-40'/>
+<path d='M0.533 1.245C0.618 1.245 0.628 1.245 0.687 1.191C1.315 0.643 1.634 -0.244 1.634 -1.245C1.634 -2.187 1.35 -3.093 0.692 -3.671C0.628 -3.736 0.618 -3.736 0.533 -3.736H0.294C0.309 -3.726 0.792 -3.313 1.021 -2.535C1.141 -2.147 1.2 -1.738 1.2 -1.245C1.2 0.478 0.394 1.161 0.294 1.245H0.533Z' id='g1-41'/>
+<path d='M0.956 -0.005V-0.448H0.508V0H0.648L0.503 0.638H0.727L0.956 -0.005Z' id='g1-44'/>
+<path d='M1.465 -0.951V-1.265H0.06V-0.951H1.465Z' id='g1-45'/>
+<path d='M0.956 -0.448H0.508V0H0.956V-0.448Z' id='g1-46'/>
+<path d='M2.431 -1.619C2.431 -1.858 2.431 -2.441 2.197 -2.849C1.943 -3.298 1.559 -3.372 1.32 -3.372C1.096 -3.372 0.707 -3.303 0.458 -2.874C0.219 -2.476 0.209 -1.933 0.209 -1.619C0.209 -1.25 0.229 -0.797 0.438 -0.418C0.658 -0.015 1.026 0.105 1.32 0.105C1.818 0.105 2.092 -0.184 2.242 -0.498C2.416 -0.852 2.431 -1.31 2.431 -1.619ZM1.32 -0.224C1.111 -0.224 0.872 -0.344 0.747 -0.702C0.648 -1.006 0.643 -1.32 0.643 -1.684C0.643 -2.142 0.643 -3.044 1.32 -3.044S1.998 -2.142 1.998 -1.684C1.998 -1.355 1.998 -0.981 1.878 -0.663C1.738 -0.304 1.484 -0.224 1.32 -0.224Z' id='g1-48'/>
+<path d='M1.599 -3.372H1.489C1.166 -3.073 0.757 -3.054 0.458 -3.044V-2.73C0.653 -2.735 0.902 -2.745 1.151 -2.844V-0.314H0.488V0H2.262V-0.314H1.599V-3.372Z' id='g1-49'/>
+<path d='M1.41 -0.384C1.35 -0.384 1.29 -0.379 1.23 -0.379H0.663L1.435 -1.061C1.524 -1.141 1.768 -1.325 1.863 -1.405C2.082 -1.604 2.376 -1.863 2.376 -2.296C2.376 -2.859 1.958 -3.372 1.245 -3.372C0.717 -3.372 0.389 -3.088 0.219 -2.58L0.453 -2.286C0.568 -2.705 0.742 -3.029 1.176 -3.029C1.594 -3.029 1.913 -2.735 1.913 -2.286C1.913 -1.873 1.669 -1.639 1.37 -1.355C1.27 -1.255 1.001 -1.031 0.897 -0.932C0.752 -0.802 0.408 -0.468 0.264 -0.344V0H2.376V-0.384H1.41Z' id='g1-50'/>
+<path d='M0.498 -2.555C0.702 -2.954 1.061 -3.064 1.3 -3.064C1.594 -3.064 1.813 -2.894 1.813 -2.61C1.813 -2.346 1.634 -2.022 1.255 -1.958C1.23 -1.953 1.21 -1.953 0.882 -1.928V-1.599H1.27C1.743 -1.599 1.918 -1.225 1.918 -0.912C1.918 -0.523 1.679 -0.224 1.29 -0.224C0.936 -0.224 0.533 -0.394 0.284 -0.712L0.219 -0.389C0.508 -0.065 0.912 0.105 1.3 0.105C1.953 0.105 2.421 -0.384 2.421 -0.907C2.421 -1.315 2.092 -1.644 1.699 -1.758C2.077 -1.953 2.271 -2.286 2.271 -2.61C2.271 -3.034 1.838 -3.372 1.305 -3.372C0.867 -3.372 0.503 -3.143 0.294 -2.844L0.498 -2.555Z' id='g1-51'/>
+<path d='M1.973 -0.832H2.491V-1.161H1.973V-3.268H1.479L0.149 -1.161V-0.832H1.544V0H1.973V-0.832ZM0.573 -1.161C0.722 -1.395 1.579 -2.725 1.579 -3.024V-1.161H0.573Z' id='g1-52'/>
+<path d='M0.817 -2.924H2.197V-3.268H0.418V-1.405H0.782C0.902 -1.674 1.136 -1.793 1.36 -1.793C1.564 -1.793 1.873 -1.654 1.873 -1.021C1.873 -0.369 1.46 -0.224 1.205 -0.224C0.877 -0.224 0.558 -0.399 0.389 -0.682L0.199 -0.384C0.443 -0.08 0.812 0.105 1.205 0.105C1.863 0.105 2.376 -0.403 2.376 -1.011C2.376 -1.629 1.918 -2.122 1.37 -2.122C1.156 -2.122 0.966 -2.047 0.817 -1.923V-2.924Z' id='g1-53'/>
+<path d='M2.187 -3.273C1.918 -3.372 1.729 -3.372 1.634 -3.372C0.877 -3.372 0.219 -2.66 0.219 -1.609C0.219 -0.259 0.827 0.105 1.33 0.105C1.733 0.105 1.943 -0.085 2.097 -0.244C2.416 -0.583 2.421 -0.936 2.421 -1.111C2.421 -1.763 2.067 -2.306 1.584 -2.306C1.096 -2.306 0.832 -2.052 0.687 -1.908C0.752 -2.59 1.101 -3.064 1.639 -3.064C1.738 -3.064 1.938 -3.054 2.187 -2.959V-3.273ZM0.692 -1.096C0.692 -1.126 0.692 -1.2 0.697 -1.225C0.697 -1.564 0.912 -1.978 1.355 -1.978C1.963 -1.978 1.963 -1.28 1.963 -1.111C1.963 -0.922 1.963 -0.712 1.828 -0.503C1.709 -0.324 1.559 -0.224 1.33 -0.224C0.802 -0.224 0.717 -0.877 0.692 -1.096Z' id='g1-54'/>
+<path d='M1.23 -2.884C1.29 -2.884 1.35 -2.889 1.41 -2.889H2.037C1.28 -2.147 0.797 -1.106 0.797 0.05H1.265C1.265 -1.405 1.973 -2.451 2.421 -2.919V-3.268H0.219V-2.884H1.23Z' id='g1-55'/>
+<path d='M1.704 -1.763C2.032 -1.868 2.346 -2.132 2.346 -2.501C2.346 -2.954 1.913 -3.372 1.32 -3.372S0.294 -2.954 0.294 -2.501C0.294 -2.127 0.618 -1.863 0.936 -1.763C0.498 -1.629 0.219 -1.29 0.219 -0.907C0.219 -0.374 0.692 0.105 1.32 0.105S2.421 -0.374 2.421 -0.907C2.421 -1.29 2.137 -1.629 1.704 -1.763ZM1.32 -1.928C0.966 -1.928 0.677 -2.132 0.677 -2.496C0.677 -2.814 0.902 -3.064 1.32 -3.064C1.733 -3.064 1.963 -2.814 1.963 -2.496C1.963 -2.142 1.684 -1.928 1.32 -1.928ZM1.32 -0.224C0.976 -0.224 0.672 -0.443 0.672 -0.912C0.672 -1.36 0.961 -1.599 1.32 -1.599S1.968 -1.355 1.968 -0.912C1.968 -0.443 1.659 -0.224 1.32 -0.224Z' id='g1-56'/>
+<path d='M0.384 -0.125C0.628 0.055 0.852 0.105 1.086 0.105C1.783 0.105 2.421 -0.598 2.421 -1.669C2.421 -3.029 1.818 -3.372 1.34 -3.372C0.897 -3.372 0.692 -3.163 0.548 -3.019C0.229 -2.695 0.219 -2.351 0.219 -2.157C0.219 -1.514 0.568 -0.961 1.056 -0.961C1.619 -0.961 1.928 -1.335 1.953 -1.365C1.883 -0.573 1.494 -0.224 1.086 -0.224C0.827 -0.224 0.667 -0.319 0.553 -0.413L0.384 -0.125ZM1.938 -2.162C1.943 -2.132 1.943 -2.082 1.943 -2.052C1.943 -1.684 1.719 -1.29 1.285 -1.29C1.096 -1.29 0.946 -1.345 0.817 -1.549C0.687 -1.743 0.677 -1.933 0.677 -2.157C0.677 -2.351 0.677 -2.575 0.832 -2.795C0.936 -2.944 1.086 -3.064 1.335 -3.064C1.818 -3.064 1.923 -2.481 1.938 -2.162Z' id='g1-57'/>
+<path d='M2.959 -0.438C2.884 -0.438 2.874 -0.438 2.834 -0.418C2.59 -0.334 2.336 -0.279 2.072 -0.279C1.27 -0.279 0.697 -0.956 0.697 -1.729C0.697 -2.565 1.345 -3.178 2.042 -3.178C2.182 -3.178 2.511 -3.143 2.675 -2.745C2.535 -2.824 2.381 -2.859 2.252 -2.859C1.719 -2.859 1.27 -2.361 1.27 -1.729C1.27 -1.081 1.733 -0.598 2.247 -0.598C2.635 -0.598 3.228 -0.912 3.228 -1.798C3.228 -2.301 3.193 -3.507 2.047 -3.507C1.101 -3.507 0.294 -2.725 0.294 -1.729C0.294 -0.742 1.091 0.05 2.052 0.05C2.511 0.05 2.939 -0.139 3.228 -0.438H2.959ZM2.252 -0.927C1.943 -0.927 1.674 -1.27 1.674 -1.729C1.674 -2.202 1.953 -2.531 2.247 -2.531C2.555 -2.531 2.824 -2.187 2.824 -1.729C2.824 -1.255 2.545 -0.927 2.252 -0.927Z' id='g1-64'/>
+<path d='M3.083 -0.608C2.735 -0.394 2.575 -0.299 2.077 -0.299C1.305 -0.299 0.837 -1.021 0.837 -1.738C0.837 -2.476 1.35 -3.168 2.077 -3.168C2.406 -3.168 2.745 -3.064 2.974 -2.889L3.054 -3.342C2.705 -3.472 2.426 -3.512 2.062 -3.512C1.076 -3.512 0.339 -2.695 0.339 -1.733C0.339 -0.707 1.121 0.05 2.092 0.05C2.58 0.05 2.785 -0.05 3.113 -0.229L3.083 -0.608Z' id='g1-67'/>
+<path d='M2.725 -1.624V-1.953H0.986V-3.098H1.714C1.773 -3.098 1.833 -3.093 1.893 -3.093H2.874V-3.442H0.483V0H2.949V-0.389H2.501C2.082 -0.389 1.664 -0.379 1.245 -0.379H0.986V-1.624H2.725Z' id='g1-69'/>
+<path d='M3.173 -1.489H2.057V-1.161H2.735V-0.399C2.516 -0.344 2.301 -0.299 2.077 -0.299C1.31 -0.299 0.837 -1.021 0.837 -1.733C0.837 -2.416 1.3 -3.168 2.052 -3.168C2.511 -3.168 2.8 -3.029 3.049 -2.819L3.128 -3.273C2.785 -3.437 2.481 -3.517 2.102 -3.517C1.096 -3.517 0.339 -2.73 0.339 -1.733C0.339 -0.762 1.091 0.05 2.072 0.05C2.431 0.05 2.854 -0.03 3.173 -0.194V-1.489Z' id='g1-71'/>
+<path d='M3.248 -3.457H2.745V-1.963H0.986V-3.457H0.483V0H0.986V-1.634H2.745V0H3.248V-3.457Z' id='g1-72'/>
+<path d='M0.986 -3.457H0.483V0H0.986V-3.457Z' id='g1-73'/>
+<path d='M1.176 -3.457H0.498V0H0.917V-3.064H0.922L2.555 0H3.233V-3.457H2.814V-0.394H2.809L1.176 -3.457Z' id='g1-78'/>
+<path d='M3.611 -1.714C3.611 -2.745 2.854 -3.562 1.953 -3.562S0.294 -2.745 0.294 -1.714S1.061 0.105 1.953 0.105C2.849 0.105 3.611 -0.687 3.611 -1.714ZM1.953 -0.249C1.35 -0.249 0.797 -0.852 0.797 -1.793C0.797 -2.675 1.355 -3.218 1.953 -3.218S3.108 -2.675 3.108 -1.793C3.108 -0.847 2.555 -0.249 1.953 -0.249Z' id='g1-79'/>
+<path d='M1.868 -1.42C2.511 -1.42 3.083 -1.873 3.083 -2.446C3.083 -2.979 2.555 -3.457 1.833 -3.457H0.488V0H0.991V-1.42H1.868ZM1.709 -3.163C2.271 -3.163 2.63 -2.864 2.63 -2.446C2.63 -2.037 2.291 -1.729 1.709 -1.729H0.976V-3.163H1.709Z' id='g1-80'/>
+<path d='M3.143 -3.457H2.71V-1.161C2.71 -0.493 2.262 -0.189 1.833 -0.189S0.986 -0.498 0.986 -1.156V-3.457H0.483V-1.166C0.483 -0.433 1.111 0.105 1.828 0.105C2.54 0.105 3.143 -0.438 3.143 -1.166V-3.457Z' id='g1-85'/>
+<path d='M1.968 -1.823L3.228 -3.457H2.685L1.724 -2.182L0.742 -3.457H0.149L1.479 -1.823L0.075 0H0.618L1.724 -1.499L2.854 0H3.447L1.968 -1.823Z' id='g1-88'/>
+<path d='M2.934 -3.238V-3.457H0.369V-3.123H1.41C1.479 -3.123 1.539 -3.128 1.609 -3.128H2.291L0.294 -0.229V0H2.964V-0.354H2.466C1.958 -0.354 1.45 -0.344 0.941 -0.344L2.934 -3.238Z' id='g1-90'/>
+<path d='M2.122 -1.435C2.122 -1.943 1.733 -2.286 1.24 -2.286C0.927 -2.286 0.687 -2.222 0.408 -2.072L0.438 -1.709C0.603 -1.818 0.847 -1.968 1.24 -1.968C1.46 -1.968 1.689 -1.803 1.689 -1.43V-1.23C0.951 -1.205 0.224 -1.051 0.224 -0.588C0.224 -0.339 0.394 0.05 0.832 0.05C1.046 0.05 1.44 0.005 1.704 -0.189V0H2.122V-1.435ZM1.689 -0.707C1.689 -0.608 1.689 -0.478 1.514 -0.374C1.355 -0.284 1.161 -0.279 1.106 -0.279C0.832 -0.279 0.623 -0.403 0.623 -0.593C0.623 -0.912 1.465 -0.941 1.689 -0.951V-0.707Z' id='g1-97'/>
+<path d='M0.842 -3.457H0.423V0H0.857V-0.234C0.966 -0.139 1.205 0.05 1.569 0.05C2.112 0.05 2.55 -0.458 2.55 -1.111C2.55 -1.714 2.207 -2.262 1.709 -2.262C1.395 -2.262 1.091 -2.162 0.842 -1.978V-3.457ZM0.857 -1.569C0.857 -1.649 0.857 -1.709 1.031 -1.823C1.106 -1.868 1.24 -1.933 1.41 -1.933C1.743 -1.933 2.117 -1.709 2.117 -1.111C2.117 -0.503 1.704 -0.279 1.355 -0.279C1.171 -0.279 0.996 -0.364 0.857 -0.588V-1.569Z' id='g1-98'/>
+<path d='M2.167 -0.543C1.918 -0.384 1.649 -0.294 1.34 -0.294C0.882 -0.294 0.613 -0.663 0.613 -1.111C0.613 -1.494 0.812 -1.943 1.355 -1.943C1.694 -1.943 1.853 -1.873 2.107 -1.714L2.172 -2.072C1.873 -2.222 1.743 -2.286 1.355 -2.286C0.608 -2.286 0.179 -1.684 0.179 -1.106C0.179 -0.498 0.658 0.05 1.335 0.05C1.684 0.05 1.983 -0.055 2.197 -0.179L2.167 -0.543Z' id='g1-99'/>
+<path d='M2.306 -3.457H1.888V-1.998C1.569 -2.232 1.245 -2.262 1.101 -2.262C0.578 -2.262 0.179 -1.738 0.179 -1.106S0.573 0.05 1.086 0.05C1.395 0.05 1.684 -0.09 1.873 -0.259V0H2.306V-3.457ZM1.873 -0.618C1.748 -0.413 1.559 -0.279 1.32 -0.279C0.971 -0.279 0.613 -0.523 0.613 -1.101C0.613 -1.724 1.036 -1.933 1.375 -1.933C1.574 -1.933 1.743 -1.848 1.873 -1.679V-0.618Z' id='g1-100'/>
+<path d='M2.142 -0.543C1.863 -0.344 1.549 -0.279 1.335 -0.279C0.902 -0.279 0.573 -0.633 0.558 -1.091H2.192C2.192 -1.32 2.167 -1.654 1.973 -1.938C1.793 -2.192 1.494 -2.286 1.25 -2.286C0.643 -2.286 0.174 -1.753 0.174 -1.121C0.174 -0.483 0.672 0.05 1.33 0.05C1.619 0.05 1.918 -0.035 2.172 -0.189L2.142 -0.543ZM0.593 -1.39C0.707 -1.788 1.001 -1.958 1.25 -1.958C1.469 -1.958 1.793 -1.853 1.888 -1.39H0.593Z' id='g1-101'/>
+<path d='M2.506 -2.262C2.396 -2.262 2.062 -2.257 1.684 -2.112L1.674 -2.107C1.494 -2.227 1.32 -2.262 1.176 -2.262C0.687 -2.262 0.324 -1.878 0.324 -1.45C0.324 -1.275 0.384 -1.096 0.498 -0.956C0.428 -0.872 0.354 -0.732 0.354 -0.543C0.354 -0.349 0.433 -0.224 0.478 -0.164C0.204 -0.005 0.149 0.224 0.149 0.344C0.149 0.722 0.672 1.021 1.32 1.021C1.973 1.021 2.491 0.722 2.491 0.344C2.491 -0.359 1.619 -0.359 1.405 -0.359H0.941C0.862 -0.359 0.648 -0.359 0.648 -0.618C0.648 -0.717 0.682 -0.767 0.687 -0.777C0.862 -0.667 1.036 -0.633 1.171 -0.633C1.659 -0.633 2.022 -1.016 2.022 -1.445C2.022 -1.604 1.978 -1.748 1.888 -1.883C1.868 -1.913 1.868 -1.918 1.868 -1.923C1.868 -1.943 2.167 -1.943 2.192 -1.943C2.197 -1.943 2.386 -1.943 2.565 -1.923L2.506 -2.262ZM1.176 -0.941C0.907 -0.941 0.707 -1.111 0.707 -1.445C0.707 -1.833 0.956 -1.953 1.171 -1.953C1.44 -1.953 1.639 -1.783 1.639 -1.45C1.639 -1.061 1.39 -0.941 1.176 -0.941ZM1.41 0.03C1.524 0.03 2.112 0.03 2.112 0.349C2.112 0.563 1.738 0.712 1.32 0.712S0.528 0.563 0.528 0.349C0.528 0.324 0.528 0.03 0.932 0.03H1.41Z' id='g1-103'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.161 -2.262 0.932 -2.012 0.832 -1.908V-3.457H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-104'/>
+<path d='M0.877 -3.417H0.374V-2.914H0.877V-3.417ZM0.837 -2.212H0.418V0H0.837V-2.212Z' id='g1-105'/>
+<path d='M0.986 -3.417H0.483V-2.914H0.986V-3.417ZM-0.324 0.847C-0.095 0.971 0.13 1.016 0.319 1.016C0.663 1.016 0.986 0.752 0.986 0.294V-2.212H0.568V0.329C0.568 0.418 0.568 0.498 0.463 0.583C0.349 0.667 0.209 0.667 0.164 0.667C-0.045 0.667 -0.174 0.573 -0.234 0.518L-0.324 0.847Z' id='g1-106'/>
+<path d='M1.489 -1.355L2.376 -2.212H1.893L0.827 -1.181V-3.457H0.438V0H0.807V-0.697L1.23 -1.106L2.052 0H2.491L1.489 -1.355Z' id='g1-107'/>
+<path d='M0.837 -3.457H0.418V0H0.837V-3.457Z' id='g1-108'/>
+<path d='M3.786 -1.474C3.786 -1.863 3.671 -2.262 3.059 -2.262C2.64 -2.262 2.381 -2.017 2.262 -1.858C2.212 -1.993 2.087 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.21C2.316 -1.539 2.456 -1.933 2.839 -1.933C3.352 -1.933 3.352 -1.584 3.352 -1.44V0H3.786V-1.474Z' id='g1-109'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-110'/>
+<path d='M2.491 -1.091C2.491 -1.748 1.968 -2.286 1.32 -2.286S0.149 -1.743 0.149 -1.091C0.149 -0.458 0.677 0.05 1.32 0.05C1.968 0.05 2.491 -0.458 2.491 -1.091ZM1.32 -0.294C0.927 -0.294 0.583 -0.583 0.583 -1.146S0.951 -1.958 1.32 -1.958C1.694 -1.958 2.057 -1.699 2.057 -1.146C2.057 -0.578 1.704 -0.294 1.32 -0.294Z' id='g1-111'/>
+<path d='M0.857 -0.234C1.121 0.005 1.405 0.05 1.574 0.05C2.102 0.05 2.55 -0.453 2.55 -1.111C2.55 -1.709 2.222 -2.262 1.729 -2.262C1.504 -2.262 1.131 -2.197 0.842 -1.973V-2.212H0.423V0.966H0.857V-0.234ZM0.857 -1.654C0.971 -1.793 1.166 -1.918 1.405 -1.918C1.803 -1.918 2.117 -1.549 2.117 -1.111C2.117 -0.618 1.743 -0.279 1.355 -0.279C1.28 -0.279 1.156 -0.289 1.026 -0.394C0.877 -0.508 0.857 -0.583 0.857 -0.677V-1.654Z' id='g1-112'/>
+<path d='M0.842 -1.061C0.842 -1.599 1.29 -1.888 1.729 -1.893V-2.262C1.31 -2.257 1.006 -2.052 0.807 -1.788V-2.247H0.423V0H0.842V-1.061Z' id='g1-114'/>
+<path d='M1.818 -2.132C1.479 -2.271 1.23 -2.286 1.051 -2.286C0.927 -2.286 0.174 -2.286 0.174 -1.624C0.174 -1.39 0.304 -1.26 0.369 -1.2C0.543 -1.026 0.752 -0.986 1.016 -0.936C1.25 -0.892 1.519 -0.842 1.519 -0.603C1.519 -0.289 1.106 -0.289 1.036 -0.289C0.717 -0.289 0.418 -0.403 0.219 -0.543L0.149 -0.169C0.319 -0.085 0.623 0.05 1.036 0.05C1.26 0.05 1.479 0.015 1.664 -0.12C1.848 -0.259 1.908 -0.478 1.908 -0.648C1.908 -0.737 1.898 -0.932 1.689 -1.121C1.504 -1.285 1.325 -1.32 1.086 -1.365C0.792 -1.42 0.563 -1.465 0.563 -1.684C0.563 -1.968 0.927 -1.968 1.001 -1.968C1.285 -1.968 1.504 -1.908 1.753 -1.778L1.818 -2.132Z' id='g1-115'/>
+<path d='M0.936 -1.898H1.674V-2.212H0.936V-2.844H0.553V-2.212H0.1V-1.898H0.538V-0.638C0.538 -0.304 0.623 0.05 0.981 0.05S1.614 -0.065 1.763 -0.134L1.679 -0.453C1.514 -0.334 1.34 -0.294 1.2 -0.294C0.991 -0.294 0.936 -0.498 0.936 -0.727V-1.898Z' id='g1-116'/>
+<path d='M2.316 -2.212H1.883V-0.767C1.883 -0.369 1.544 -0.244 1.255 -0.244C0.887 -0.244 0.847 -0.344 0.847 -0.573V-2.212H0.413V-0.543C0.413 -0.1 0.608 0.05 0.956 0.05C1.161 0.05 1.599 0.01 1.898 -0.229V0H2.316V-2.212Z' id='g1-117'/>
+<path d='M3.537 -2.212H3.148C3.103 -2.072 2.824 -1.23 2.67 -0.712C2.63 -0.568 2.58 -0.408 2.565 -0.294H2.56C2.531 -0.498 2.356 -1.036 2.346 -1.071L1.978 -2.212H1.599C1.455 -1.783 1.081 -0.667 1.041 -0.304H1.036C0.996 -0.658 0.628 -1.758 0.548 -1.998C0.508 -2.117 0.508 -2.127 0.483 -2.212H0.075L0.802 0H1.22C1.225 -0.02 1.36 -0.413 1.534 -0.966C1.609 -1.21 1.758 -1.689 1.783 -1.908L1.788 -1.913C1.798 -1.808 1.828 -1.699 1.863 -1.574S1.933 -1.315 1.968 -1.2L2.351 0H2.809L3.537 -2.212Z' id='g1-119'/>
+<path d='M1.38 -1.141L2.346 -2.212H1.908L1.2 -1.395L0.478 -2.212H0.03L1.026 -1.141L0 0H0.443L1.2 -0.936L1.988 0H2.436L1.38 -1.141Z' id='g1-120'/>
+<path d='M2.112 -2.002V-2.212H0.219V-1.893H0.951C1.011 -1.893 1.071 -1.898 1.131 -1.898H1.519L0.149 -0.219V0H2.127V-0.334H1.355C1.295 -0.334 1.235 -0.329 1.176 -0.329H0.742L2.112 -2.002Z' id='g1-122'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g0-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g0-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g0-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g0-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g0-108'/>
+<path d='M1.462 -1.91C1.462 -2.851 2.197 -3.425 3.013 -3.434V-4.08C2.367 -4.071 1.775 -3.748 1.408 -3.219V-4.035H0.744V0H1.462V-1.91Z' id='g0-114'/>
+<path d='M3.165 -3.847C2.609 -4.098 2.197 -4.133 1.829 -4.133C1.623 -4.133 0.305 -4.133 0.305 -2.95C0.305 -2.52 0.565 -2.251 0.664 -2.152C1.004 -1.856 1.237 -1.811 1.847 -1.695C2.134 -1.641 2.645 -1.542 2.645 -1.085C2.645 -0.502 1.919 -0.502 1.802 -0.502C1.273 -0.502 0.762 -0.681 0.377 -0.95L0.26 -0.296C0.798 -0.009 1.345 0.099 1.802 0.099C2.385 0.099 3.318 -0.09 3.318 -1.157C3.318 -1.47 3.192 -1.784 2.878 -2.053C2.573 -2.313 2.304 -2.367 1.748 -2.475C1.426 -2.537 0.977 -2.618 0.977 -3.04C0.977 -3.569 1.614 -3.569 1.748 -3.569C2.403 -3.569 2.789 -3.362 3.049 -3.219L3.165 -3.847Z' id='g0-115'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g0-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g0-118'/>
+</defs>
+<g id='page6'>
+<path d='M190.86 194.93V186.074M245.766 194.93V186.074M300.676 194.93V186.074M190.86 51.973V60.828M245.766 51.973V60.828M300.676 51.973V60.828' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M163.403 190.324V186.074M218.313 190.324V186.074M273.223 190.324V186.074M328.133 190.324V186.074M163.403 56.574V60.828M218.313 56.574V60.828M273.223 56.574V60.828M328.133 56.574V60.828' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 186.074H140.199M135.949 154.762H140.199M135.949 123.449H140.199M135.949 92.141H140.199M135.949 60.828H140.199M355.586 186.074H351.336M355.586 154.762H351.336M355.586 123.449H351.336M355.586 92.141H351.336M355.586 60.828H351.336' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 186.074V60.828H355.586V186.074H135.949Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -18.291 59.571)'>
+<use x='168.285' xlink:href='#g2-54' y='140.503'/>
+<use x='172.519' xlink:href='#g2-48' y='140.503'/>
+<use x='176.753' xlink:href='#g2-50' y='140.503'/>
+<use x='180.987' xlink:href='#g2-46' y='140.503'/>
+<use x='183.34' xlink:href='#g2-103' y='140.503'/>
+<use x='187.574' xlink:href='#g2-99' y='140.503'/>
+<use x='191.338' xlink:href='#g2-99' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 27.05 59.571)'>
+<use x='168.285' xlink:href='#g2-54' y='140.503'/>
+<use x='172.519' xlink:href='#g2-50' y='140.503'/>
+<use x='176.753' xlink:href='#g2-48' y='140.503'/>
+<use x='180.987' xlink:href='#g2-46' y='140.503'/>
+<use x='183.34' xlink:href='#g2-111' y='140.503'/>
+<use x='187.574' xlink:href='#g2-109' y='140.503'/>
+<use x='194.299' xlink:href='#g2-110' y='140.503'/>
+<use x='198.671' xlink:href='#g2-101' y='140.503'/>
+<use x='202.435' xlink:href='#g2-116' y='140.503'/>
+<use x='205.493' xlink:href='#g2-112' y='140.503'/>
+<use x='209.866' xlink:href='#g2-112' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 78.694 59.571)'>
+<use x='168.285' xlink:href='#g2-54' y='140.503'/>
+<use x='172.519' xlink:href='#g2-50' y='140.503'/>
+<use x='176.753' xlink:href='#g2-51' y='140.503'/>
+<use x='180.987' xlink:href='#g2-46' y='140.503'/>
+<use x='183.34' xlink:href='#g2-120' y='140.503'/>
+<use x='187.242' xlink:href='#g2-97' y='140.503'/>
+<use x='191.31' xlink:href='#g2-108' y='140.503'/>
+<use x='193.33' xlink:href='#g2-97' y='140.503'/>
+<use x='197.398' xlink:href='#g2-110' y='140.503'/>
+<use x='201.771' xlink:href='#g2-99' y='140.503'/>
+<use x='205.535' xlink:href='#g2-98' y='140.503'/>
+<use x='209.907' xlink:href='#g2-109' y='140.503'/>
+<use x='216.632' xlink:href='#g2-107' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 134.081 59.571)'>
+<use x='168.285' xlink:href='#g2-54' y='140.503'/>
+<use x='172.519' xlink:href='#g2-52' y='140.503'/>
+<use x='176.753' xlink:href='#g2-56' y='140.503'/>
+<use x='180.987' xlink:href='#g2-46' y='140.503'/>
+<use x='183.34' xlink:href='#g2-101' y='140.503'/>
+<use x='187.103' xlink:href='#g2-120' y='140.503'/>
+<use x='191.006' xlink:href='#g2-99' y='140.503'/>
+<use x='194.769' xlink:href='#g2-104' y='140.503'/>
+<use x='199.142' xlink:href='#g2-97' y='140.503'/>
+<use x='203.21' xlink:href='#g2-110' y='140.503'/>
+<use x='207.582' xlink:href='#g2-103' y='140.503'/>
+<use x='211.817' xlink:href='#g2-101' y='140.503'/>
+<use x='215.58' xlink:href='#g2-50' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 47.205)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-120' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 15.894)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-53' y='140.503'/>
+<use x='175.048' xlink:href='#g1-120' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -15.418)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-120' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -46.729)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-53' y='140.503'/>
+<use x='175.048' xlink:href='#g1-120' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -78.041)'>
+<use x='168.285' xlink:href='#g1-50' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-120' y='140.503'/>
+</g>
+<path clip-path='url(#clip6)' d='M135.949 123.449H355.586' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M140.863 186.074H144.102V123.449H140.863ZM195.77 186.074H199.008V123.449H195.77ZM250.68 186.074H253.918V123.449H250.68ZM305.59 186.074H308.828V123.449H305.59Z' fill='#f0e0f0'/>
+<path clip-path='url(#clip6)' d='M140.863 186.074H144.102V123.449H140.863ZM195.77 186.074H199.008V123.449H195.77ZM250.68 186.074H253.918V123.449H250.68ZM305.59 186.074H308.828V123.449H305.59Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M142.481 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip6)' d='M140.488 123.449H144.472' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M142.481 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip6)' d='M140.488 123.449H144.472' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M197.391 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip6)' d='M195.398 123.449H199.383' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M197.391 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip6)' d='M195.398 123.449H199.383' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M252.301 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip6)' d='M250.308 123.449H254.292' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M252.301 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip6)' d='M250.308 123.449H254.292' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M307.211 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip6)' d='M305.219 123.449H309.203' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M307.211 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip6)' d='M305.219 123.449H309.203' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M146.094 186.074H149.332V123.262H146.094ZM201 186.074H204.238V123.137H201ZM255.91 186.074H259.149V124.141H255.91ZM310.82 186.074H314.059V73.664H310.82Z' fill='#e1c2e1'/>
+<path clip-path='url(#clip6)' d='M146.094 186.074H149.332V123.262H146.094ZM201 186.074H204.238V123.137H201ZM255.91 186.074H259.149V124.141H255.91ZM310.82 186.074H314.059V73.664H310.82Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M147.711 123.262V123.262' fill='#e1c2e1'/>
+<path clip-path='url(#clip6)' d='M145.719 123.262H149.703' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M147.711 123.262V123.262' fill='#e1c2e1'/>
+<path clip-path='url(#clip6)' d='M145.719 123.262H149.703' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M202.621 123.137V123.137' fill='#e1c2e1'/>
+<path clip-path='url(#clip6)' d='M200.629 123.137H204.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M202.621 123.137V123.137' fill='#e1c2e1'/>
+<path clip-path='url(#clip6)' d='M200.629 123.137H204.613' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M257.531 124.141V124.141' fill='#e1c2e1'/>
+<path clip-path='url(#clip6)' d='M255.539 124.141H259.523' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M257.531 124.141V124.141' fill='#e1c2e1'/>
+<path clip-path='url(#clip6)' d='M255.539 124.141H259.523' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M312.442 73.664V73.664' fill='#e1c2e1'/>
+<path clip-path='url(#clip6)' d='M310.449 73.664H314.433' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M312.442 73.664V73.664' fill='#e1c2e1'/>
+<path clip-path='url(#clip6)' d='M310.449 73.664H314.433' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M151.324 186.074H154.563V121.762H151.324ZM206.231 186.074H209.469V123.891H206.231ZM261.141 186.074H264.379V121.321H261.141ZM316.051 186.074H319.289V94.52H316.051Z' fill='#d1a3d1'/>
+<path clip-path='url(#clip6)' d='M151.324 186.074H154.563V121.762H151.324ZM206.231 186.074H209.469V123.891H206.231ZM261.141 186.074H264.379V121.321H261.141ZM316.051 186.074H319.289V94.52H316.051Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M152.942 121.762V121.762' fill='#d1a3d1'/>
+<path clip-path='url(#clip6)' d='M150.949 121.762H154.933' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M152.942 121.762V121.762' fill='#d1a3d1'/>
+<path clip-path='url(#clip6)' d='M150.949 121.762H154.933' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M207.852 123.891V123.891' fill='#d1a3d1'/>
+<path clip-path='url(#clip6)' d='M205.86 123.89H209.844' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M207.852 123.891V123.891' fill='#d1a3d1'/>
+<path clip-path='url(#clip6)' d='M205.86 123.89H209.844' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M262.762 121.321V121.321' fill='#d1a3d1'/>
+<path clip-path='url(#clip6)' d='M260.769 121.321H264.753' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M262.762 121.321V121.321' fill='#d1a3d1'/>
+<path clip-path='url(#clip6)' d='M260.769 121.321H264.753' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M317.672 94.52V94.52' fill='#d1a3d1'/>
+<path clip-path='url(#clip6)' d='M315.68 94.52H319.664' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M317.672 94.52V94.52' fill='#d1a3d1'/>
+<path clip-path='url(#clip6)' d='M315.68 94.52H319.664' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M156.555 186.074H159.793V122.449H156.555ZM211.461 186.074H214.699V124.328H211.461ZM266.371 186.074H269.609V124.953H266.371ZM321.281 186.074H324.52V106.168H321.281Z' fill='#c285c2'/>
+<path clip-path='url(#clip6)' d='M156.555 186.074H159.793V122.449H156.555ZM211.461 186.074H214.699V124.328H211.461ZM266.371 186.074H269.609V124.953H266.371ZM321.281 186.074H324.52V106.168H321.281Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M158.172 122.449V122.449' fill='#c285c2'/>
+<path clip-path='url(#clip6)' d='M156.18 122.449H160.164' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M158.172 122.449V122.449' fill='#c285c2'/>
+<path clip-path='url(#clip6)' d='M156.18 122.449H160.164' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M213.082 124.328V124.328' fill='#c285c2'/>
+<path clip-path='url(#clip6)' d='M211.09 124.329H215.074' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M213.082 124.328V124.328' fill='#c285c2'/>
+<path clip-path='url(#clip6)' d='M211.09 124.329H215.074' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M267.992 124.953V124.953' fill='#c285c2'/>
+<path clip-path='url(#clip6)' d='M266 124.953H269.985' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M267.992 124.953V124.953' fill='#c285c2'/>
+<path clip-path='url(#clip6)' d='M266 124.953H269.985' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M322.902 106.168V106.168' fill='#c285c2'/>
+<path clip-path='url(#clip6)' d='M320.91 106.168H324.894' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M322.902 106.168V106.168' fill='#c285c2'/>
+<path clip-path='url(#clip6)' d='M320.91 106.168H324.894' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M161.785 186.074H165.024V123.891H161.785ZM216.692 186.074H219.93V122.262H216.692ZM271.602 186.074H274.84V127.395H271.602ZM326.512 186.074H329.75V125.328H326.512Z' fill='#b366b3'/>
+<path clip-path='url(#clip6)' d='M161.785 186.074H165.024V123.891H161.785ZM216.692 186.074H219.93V122.262H216.692ZM271.602 186.074H274.84V127.395H271.602ZM326.512 186.074H329.75V125.328H326.512Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M163.403 123.891V123.891' fill='#b366b3'/>
+<path clip-path='url(#clip6)' d='M161.41 123.89H165.394' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M163.403 123.891V123.891' fill='#b366b3'/>
+<path clip-path='url(#clip6)' d='M161.41 123.89H165.394' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M218.313 122.262V122.262' fill='#b366b3'/>
+<path clip-path='url(#clip6)' d='M216.321 122.262H220.305' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M218.313 122.262V122.262' fill='#b366b3'/>
+<path clip-path='url(#clip6)' d='M216.321 122.262H220.305' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M273.223 127.395V127.395' fill='#b366b3'/>
+<path clip-path='url(#clip6)' d='M271.23 127.394H275.215' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M273.223 127.395V127.395' fill='#b366b3'/>
+<path clip-path='url(#clip6)' d='M271.23 127.394H275.215' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M328.133 125.328V125.328' fill='#b366b3'/>
+<path clip-path='url(#clip6)' d='M326.141 125.329H330.125' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M328.133 125.328V125.328' fill='#b366b3'/>
+<path clip-path='url(#clip6)' d='M326.141 125.329H330.125' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M167.016 186.074H170.254V104.914H167.016ZM221.922 186.074H225.16V113.18H221.922ZM276.832 186.074H280.07V100.781H276.832ZM331.742 186.074H334.981V71.848H331.742Z' fill='#a447a4'/>
+<path clip-path='url(#clip6)' d='M167.016 186.074H170.254V104.914H167.016ZM221.922 186.074H225.16V113.18H221.922ZM276.832 186.074H280.07V100.781H276.832ZM331.742 186.074H334.981V71.848H331.742Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M168.633 104.914V104.914' fill='#a447a4'/>
+<path clip-path='url(#clip6)' d='M166.641 104.914H170.625' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M168.633 104.914V104.914' fill='#a447a4'/>
+<path clip-path='url(#clip6)' d='M166.641 104.914H170.625' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M223.543 113.18V113.18' fill='#a447a4'/>
+<path clip-path='url(#clip6)' d='M221.551 113.18H225.535' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M223.543 113.18V113.18' fill='#a447a4'/>
+<path clip-path='url(#clip6)' d='M221.551 113.18H225.535' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M278.453 100.781V100.781' fill='#a447a4'/>
+<path clip-path='url(#clip6)' d='M276.461 100.781H280.446' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M278.453 100.781V100.781' fill='#a447a4'/>
+<path clip-path='url(#clip6)' d='M276.461 100.781H280.446' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M333.363 71.848V71.848' fill='#a447a4'/>
+<path clip-path='url(#clip6)' d='M331.371 71.848H335.355' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M333.363 71.848V71.848' fill='#a447a4'/>
+<path clip-path='url(#clip6)' d='M331.371 71.848H335.355' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M172.246 186.074H175.485V123.891H172.246ZM227.153 186.074H230.391V122.324H227.153ZM282.063 186.074H285.301V127.395H282.063ZM336.973 186.074H340.211V123.891H336.973Z' fill='#942994'/>
+<path clip-path='url(#clip6)' d='M172.246 186.074H175.485V123.891H172.246ZM227.153 186.074H230.391V122.324H227.153ZM282.063 186.074H285.301V127.395H282.063ZM336.973 186.074H340.211V123.891H336.973Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M173.863 123.891V123.891' fill='#942994'/>
+<path clip-path='url(#clip6)' d='M171.871 123.89H175.855' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M173.863 123.891V123.891' fill='#942994'/>
+<path clip-path='url(#clip6)' d='M171.871 123.89H175.855' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M228.774 122.324V122.324' fill='#942994'/>
+<path clip-path='url(#clip6)' d='M226.782 122.325H230.766' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M228.774 122.324V122.324' fill='#942994'/>
+<path clip-path='url(#clip6)' d='M226.782 122.325H230.766' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M283.684 127.395V127.395' fill='#942994'/>
+<path clip-path='url(#clip6)' d='M281.691 127.394H285.676' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M283.684 127.395V127.395' fill='#942994'/>
+<path clip-path='url(#clip6)' d='M281.691 127.394H285.676' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M338.594 123.891V123.891' fill='#942994'/>
+<path clip-path='url(#clip6)' d='M336.602 123.89H340.586' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M338.594 123.891V123.891' fill='#942994'/>
+<path clip-path='url(#clip6)' d='M336.602 123.89H340.586' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M177.477 186.074H180.715V104.602H177.477ZM232.383 186.074H235.621V113.367H232.383ZM287.293 186.074H290.531V112.117H287.293ZM342.203 186.074H345.442V103.16H342.203Z' fill='#850a85'/>
+<path clip-path='url(#clip6)' d='M177.477 186.074H180.715V104.602H177.477ZM232.383 186.074H235.621V113.367H232.383ZM287.293 186.074H290.531V112.117H287.293ZM342.203 186.074H345.442V103.16H342.203Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M179.094 104.602V104.602' fill='#850a85'/>
+<path clip-path='url(#clip6)' d='M177.102 104.601H181.086' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M179.094 104.602V104.602' fill='#850a85'/>
+<path clip-path='url(#clip6)' d='M177.102 104.601H181.086' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M234.004 113.367V113.367' fill='#850a85'/>
+<path clip-path='url(#clip6)' d='M232.012 113.368H235.996' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M234.004 113.367V113.367' fill='#850a85'/>
+<path clip-path='url(#clip6)' d='M232.012 113.368H235.996' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M288.914 112.117V112.117' fill='#850a85'/>
+<path clip-path='url(#clip6)' d='M286.922 112.117H290.907' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M288.914 112.117V112.117' fill='#850a85'/>
+<path clip-path='url(#clip6)' d='M286.922 112.117H290.907' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M343.824 103.16V103.16' fill='#850a85'/>
+<path clip-path='url(#clip6)' d='M341.832 103.16H345.816' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M343.824 103.16V103.16' fill='#850a85'/>
+<path clip-path='url(#clip6)' d='M341.832 103.16H345.816' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M182.707 186.074H185.945V123.324H182.707ZM237.613 186.074H240.852V117.754H237.613ZM292.524 186.074H295.762V121.508H292.524ZM347.434 186.074H350.672V117.375H347.434Z' fill='#760076'/>
+<path clip-path='url(#clip6)' d='M182.707 186.074H185.945V123.324H182.707ZM237.613 186.074H240.852V117.754H237.613ZM292.524 186.074H295.762V121.508H292.524ZM347.434 186.074H350.672V117.375H347.434Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M184.324 123.324V123.324' fill='#760076'/>
+<path clip-path='url(#clip6)' d='M182.332 123.325H186.316' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M184.324 123.324V123.324' fill='#760076'/>
+<path clip-path='url(#clip6)' d='M182.332 123.325H186.316' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M239.234 117.754V117.754' fill='#760076'/>
+<path clip-path='url(#clip6)' d='M237.243 117.754H241.227' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M239.234 117.754V117.754' fill='#760076'/>
+<path clip-path='url(#clip6)' d='M237.243 117.754H241.227' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M294.145 121.508V121.508' fill='#760076'/>
+<path clip-path='url(#clip6)' d='M292.152 121.508H296.137' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M294.145 121.508V121.508' fill='#760076'/>
+<path clip-path='url(#clip6)' d='M292.152 121.508H296.137' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M349.055 117.375V117.375' fill='#760076'/>
+<path clip-path='url(#clip6)' d='M347.063 117.375H351.047' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M349.055 117.375V117.375' fill='#760076'/>
+<path clip-path='url(#clip6)' d='M347.063 117.375H351.047' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip6)' d='M287.102 233.301H355.387V211.324H287.102Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 122.136 88.837)'>
+<use x='168.285' xlink:href='#g1-72' y='129.265'/>
+<use x='172.021' xlink:href='#g1-80' y='129.265'/>
+<use x='175.402' xlink:href='#g1-45' y='129.265'/>
+<use x='177.167' xlink:href='#g1-90' y='129.265'/>
+<use x='180.401' xlink:href='#g1-52' y='129.265'/>
+<use x='183.047' xlink:href='#g1-45' y='129.265'/>
+<use x='184.812' xlink:href='#g1-71' y='129.265'/>
+<use x='188.34' xlink:href='#g1-52' y='129.265'/>
+<use x='190.987' xlink:href='#g1-44' y='129.265'/>
+<use x='194.221' xlink:href='#g1-49' y='129.265'/>
+<use x='196.867' xlink:href='#g1-54' y='129.265'/>
+<use x='199.514' xlink:href='#g1-71' y='129.265'/>
+<use x='203.042' xlink:href='#g1-98' y='129.265'/>
+<use x='168.285' xlink:href='#g1-56' y='134.884'/>
+<use x='170.931' xlink:href='#g1-45' y='134.884'/>
+<use x='172.695' xlink:href='#g1-99' y='134.884'/>
+<use x='175.048' xlink:href='#g1-111' y='134.884'/>
+<use x='177.547' xlink:href='#g1-114' y='134.884'/>
+<use x='179.355' xlink:href='#g1-101' y='134.884'/>
+<use x='183.471' xlink:href='#g1-73' y='134.884'/>
+<use x='184.941' xlink:href='#g1-110' y='134.884'/>
+<use x='187.674' xlink:href='#g1-116' y='134.884'/>
+<use x='189.585' xlink:href='#g1-101' y='134.884'/>
+<use x='191.938' xlink:href='#g1-108' y='134.884'/>
+<use x='194.965' xlink:href='#g1-88' y='134.884'/>
+<use x='198.493' xlink:href='#g1-69' y='134.884'/>
+<use x='201.667' xlink:href='#g1-79' y='134.884'/>
+<use x='205.576' xlink:href='#g1-78' y='134.884'/>
+<use x='211.076' xlink:href='#g1-64' y='134.884'/>
+<use x='214.605' xlink:href='#g1-50' y='134.884'/>
+<use x='217.251' xlink:href='#g1-46' y='134.884'/>
+<use x='218.721' xlink:href='#g1-55' y='134.884'/>
+<use x='221.368' xlink:href='#g1-71' y='134.884'/>
+<use x='224.896' xlink:href='#g1-104' y='134.884'/>
+<use x='227.629' xlink:href='#g1-122' y='134.884'/>
+<use x='168.285' xlink:href='#g1-85' y='140.503'/>
+<use x='171.917' xlink:href='#g1-98' y='140.503'/>
+<use x='174.65' xlink:href='#g1-117' y='140.503'/>
+<use x='177.383' xlink:href='#g1-110' y='140.503'/>
+<use x='180.116' xlink:href='#g1-116' y='140.503'/>
+<use x='182.027' xlink:href='#g1-117' y='140.503'/>
+<use x='186.524' xlink:href='#g1-49' y='140.503'/>
+<use x='189.17' xlink:href='#g1-56' y='140.503'/>
+<use x='191.817' xlink:href='#g1-46' y='140.503'/>
+<use x='193.287' xlink:href='#g1-48' y='140.503'/>
+<use x='195.933' xlink:href='#g1-52' y='140.503'/>
+<use x='198.58' xlink:href='#g1-46' y='140.503'/>
+<use x='200.05' xlink:href='#g1-49' y='140.503'/>
+<use x='202.696' xlink:href='#g1-44' y='140.503'/>
+<use x='205.931' xlink:href='#g1-71' y='140.503'/>
+<use x='209.459' xlink:href='#g1-67' y='140.503'/>
+<use x='212.841' xlink:href='#g1-67' y='140.503'/>
+<use x='217.986' xlink:href='#g1-55' y='140.503'/>
+<use x='220.633' xlink:href='#g1-46' y='140.503'/>
+<use x='222.103' xlink:href='#g1-52' y='140.503'/>
+<use x='224.749' xlink:href='#g1-46' y='140.503'/>
+<use x='226.219' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 3.612 285.711)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 58.522 285.711)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 113.431 285.711)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 168.341 285.711)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 8.843 285.523)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 63.752 285.398)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 118.662 286.4)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 173.571 235.926)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-55' y='140.503'/>
+<use x='175.048' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 14.073 284.02)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-51' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 68.983 286.149)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 123.892 283.582)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-51' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 178.802 256.779)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-52' y='140.503'/>
+<use x='175.048' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 19.304 284.709)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 74.213 286.588)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 129.123 287.214)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-56' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 184.032 268.427)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-50' y='140.503'/>
+<use x='175.048' xlink:href='#g1-56' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 24.534 286.149)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 79.444 284.521)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 134.353 289.656)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-52' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 189.263 287.59)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-55' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 29.765 267.175)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-51' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 84.674 275.441)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-49' y='140.503'/>
+<use x='175.048' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 139.584 263.042)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-51' y='140.503'/>
+<use x='175.048' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 194.493 234.11)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-56' y='140.503'/>
+<use x='175.048' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 34.995 286.149)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 89.905 284.584)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 144.814 289.656)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-52' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 199.724 286.149)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 40.226 266.862)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-51' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 95.135 275.629)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-49' y='140.503'/>
+<use x='175.048' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 150.045 274.376)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-49' y='140.503'/>
+<use x='175.048' xlink:href='#g1-56' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 204.954 265.421)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-51' y='140.503'/>
+<use x='175.048' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 45.456 285.586)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 100.366 280.012)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 155.275 283.77)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-51' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 210.185 279.637)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-49' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 -27.345 343.63)'>
+<use x='168.285' xlink:href='#g0-82' y='140.503'/>
+<use x='174.255' xlink:href='#g0-101' y='140.503'/>
+<use x='178.351' xlink:href='#g0-108' y='140.503'/>
+<use x='180.551' xlink:href='#g0-97' y='140.503'/>
+<use x='184.979' xlink:href='#g0-116' y='140.503'/>
+<use x='188.307' xlink:href='#g0-105' y='140.503'/>
+<use x='190.507' xlink:href='#g0-118' y='140.503'/>
+<use x='194.755' xlink:href='#g0-101' y='140.503'/>
+<use x='201.922' xlink:href='#g0-114' y='140.503'/>
+<use x='205.07' xlink:href='#g0-115' y='140.503'/>
+<use x='208.603' xlink:href='#g0-115' y='140.503'/>
+<use x='215.207' xlink:href='#g2-40' y='140.503'/>
+<use x='218.5' xlink:href='#g2-108' y='140.503'/>
+<use x='220.521' xlink:href='#g2-111' y='140.503'/>
+<use x='224.52' xlink:href='#g2-119' y='140.503'/>
+<use x='230.068' xlink:href='#g2-101' y='140.503'/>
+<use x='233.832' xlink:href='#g2-114' y='140.503'/>
+<use x='239.547' xlink:href='#g2-105' y='140.503'/>
+<use x='241.567' xlink:href='#g2-115' y='140.503'/>
+<use x='247.636' xlink:href='#g2-98' y='140.503'/>
+<use x='252.244' xlink:href='#g2-101' y='140.503'/>
+<use x='256.008' xlink:href='#g2-116' y='140.503'/>
+<use x='259.066' xlink:href='#g2-116' y='140.503'/>
+<use x='262.124' xlink:href='#g2-101' y='140.503'/>
+<use x='265.887' xlink:href='#g2-114' y='140.503'/>
+<use x='268.779' xlink:href='#g2-41' y='140.503'/>
+</g>
+<path d='M136.149 231.446H376.824V214.453H136.149Z' fill='#ffffff'/>
+<path d='M136.149 231.446H376.824V214.453H136.149Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path d='M139.336 224.941H142.324V216.972H139.336ZM145.313 224.941H148.301V218.964H145.313Z' fill='#f0e0f0'/>
+<path d='M139.336 224.941H142.324V216.972H139.336ZM145.313 224.941H148.301V218.964H145.313Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -33.522 84.43)'>
+<use x='185.011' xlink:href='#g2-109' y='140.503'/>
+<use x='191.736' xlink:href='#g2-105' y='140.503'/>
+</g>
+<path d='M163.422 224.941H166.41V216.973H163.422ZM169.398 224.941H172.391V218.965H169.398Z' fill='#e1c2e1'/>
+<path d='M163.422 224.941H166.41V216.972H163.422ZM169.398 224.941H172.391V218.964H169.398Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.56 83.976)'>
+<use x='208.137' xlink:href='#g2-116' y='140.503'/>
+<use x='211.195' xlink:href='#g2-99' y='140.503'/>
+</g>
+<path d='M185.586 224.941H188.574V216.973H185.586ZM191.563 224.941H194.555V218.965H191.563Z' fill='#d1a3d1'/>
+<path d='M185.586 224.941H188.574V216.972H185.586ZM191.563 224.941H194.555V218.964H191.563Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.159 84.43)'>
+<use x='229.9' xlink:href='#g2-106' y='140.503'/>
+<use x='232.155' xlink:href='#g2-101' y='140.503'/>
+</g>
+<path d='M206.949 224.941H209.938V216.973H206.949ZM212.926 224.941H215.914V218.965H212.926Z' fill='#c285c2'/>
+<path d='M206.949 224.941H209.938V216.972H206.949ZM212.926 224.941H215.914V218.964H212.926Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.958 83.47)'>
+<use x='252.061' xlink:href='#g2-115' y='140.503'/>
+<use x='255.307' xlink:href='#g2-110' y='140.503'/>
+</g>
+<path d='M229.91 224.941H232.898V216.973H229.91ZM235.887 224.941H238.875V218.965H235.887Z' fill='#b366b3'/>
+<path d='M229.91 224.941H232.898V216.972H229.91ZM235.887 224.941H238.875V218.964H235.887Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.781 83.47)'>
+<use x='274.845' xlink:href='#g2-114' y='140.503'/>
+<use x='277.737' xlink:href='#g2-112' y='140.503'/>
+</g>
+<path d='M252.516 224.941H255.504V216.973H252.516ZM258.496 224.941H261.484V218.965H258.496Z' fill='#a447a4'/>
+<path d='M252.516 224.941H255.504V216.972H252.516ZM258.496 224.941H261.484V218.964H258.496Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -39.001 84.467)'>
+<use x='303.672' xlink:href='#g2-104' y='140.503'/>
+<use x='308.045' xlink:href='#g2-111' y='140.503'/>
+<use x='312.279' xlink:href='#g2-97' y='140.503'/>
+<use x='316.112' xlink:href='#g2-114' y='140.503'/>
+<use x='319.004' xlink:href='#g2-100' y='140.503'/>
+</g>
+<path d='M287.563 224.941H290.551V216.973H287.563ZM293.543 224.941H296.531V218.965H293.543Z' fill='#942994'/>
+<path d='M287.563 224.941H290.551V216.972H287.563ZM293.543 224.941H296.531V218.964H293.543Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.472 84.467)'>
+<use x='337.19' xlink:href='#g2-103' y='140.503'/>
+<use x='341.424' xlink:href='#g2-108' y='140.503'/>
+<use x='343.444' xlink:href='#g2-105' y='140.503'/>
+<use x='345.464' xlink:href='#g2-98' y='140.503'/>
+<use x='350.072' xlink:href='#g2-99' y='140.503'/>
+</g>
+<path d='M319.551 224.941H322.539V216.973H319.551ZM325.527 224.941H328.52V218.965H325.527Z' fill='#850a85'/>
+<path d='M319.551 224.941H322.539V216.972H319.551ZM325.527 224.941H328.52V218.964H325.527Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -35.051 84.467)'>
+<use x='366.757' xlink:href='#g2-116' y='140.503'/>
+<use x='369.815' xlink:href='#g2-98' y='140.503'/>
+<use x='374.187' xlink:href='#g2-98' y='140.503'/>
+</g>
+<path d='M346.699 224.941H349.688V216.973H346.699ZM352.676 224.941H355.664V218.965H352.676Z' fill='#760076'/>
+<path d='M346.699 224.941H349.688V216.972H346.699ZM352.676 224.941H355.664V218.964H352.676Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -35.145 84.43)'>
+<use x='393.997' xlink:href='#g2-115' y='140.503'/>
+<use x='397.243' xlink:href='#g2-109' y='140.503'/>
+<use x='403.968' xlink:href='#g2-105' y='140.503'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='182.025pt' version='1.1' viewBox='52.938 51.674 270.487 182.025' width='270.487pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip5'>
+<path d='M82.148 186.074H301.789V60.828H82.148Z'/>
+</clipPath>
+<use id='g2-40' transform='scale(1.6)' xlink:href='#g1-40'/>
+<use id='g2-41' transform='scale(1.6)' xlink:href='#g1-41'/>
+<use id='g2-46' transform='scale(1.6)' xlink:href='#g1-46'/>
+<use id='g2-48' transform='scale(1.6)' xlink:href='#g1-48'/>
+<use id='g2-50' transform='scale(1.6)' xlink:href='#g1-50'/>
+<use id='g2-51' transform='scale(1.6)' xlink:href='#g1-51'/>
+<use id='g2-52' transform='scale(1.6)' xlink:href='#g1-52'/>
+<use id='g2-54' transform='scale(1.6)' xlink:href='#g1-54'/>
+<use id='g2-56' transform='scale(1.6)' xlink:href='#g1-56'/>
+<use id='g2-97' transform='scale(1.6)' xlink:href='#g1-97'/>
+<use id='g2-98' transform='scale(1.6)' xlink:href='#g1-98'/>
+<use id='g2-99' transform='scale(1.6)' xlink:href='#g1-99'/>
+<use id='g2-100' transform='scale(1.6)' xlink:href='#g1-100'/>
+<use id='g2-101' transform='scale(1.6)' xlink:href='#g1-101'/>
+<use id='g2-103' transform='scale(1.6)' xlink:href='#g1-103'/>
+<use id='g2-104' transform='scale(1.6)' xlink:href='#g1-104'/>
+<use id='g2-105' transform='scale(1.6)' xlink:href='#g1-105'/>
+<use id='g2-106' transform='scale(1.6)' xlink:href='#g1-106'/>
+<use id='g2-107' transform='scale(1.6)' xlink:href='#g1-107'/>
+<use id='g2-108' transform='scale(1.6)' xlink:href='#g1-108'/>
+<use id='g2-109' transform='scale(1.6)' xlink:href='#g1-109'/>
+<use id='g2-110' transform='scale(1.6)' xlink:href='#g1-110'/>
+<use id='g2-111' transform='scale(1.6)' xlink:href='#g1-111'/>
+<use id='g2-112' transform='scale(1.6)' xlink:href='#g1-112'/>
+<use id='g2-114' transform='scale(1.6)' xlink:href='#g1-114'/>
+<use id='g2-115' transform='scale(1.6)' xlink:href='#g1-115'/>
+<use id='g2-116' transform='scale(1.6)' xlink:href='#g1-116'/>
+<use id='g2-119' transform='scale(1.6)' xlink:href='#g1-119'/>
+<use id='g2-120' transform='scale(1.6)' xlink:href='#g1-120'/>
+<path d='M1.519 -3.736C1.435 -3.736 1.425 -3.736 1.365 -3.681C0.737 -3.133 0.418 -2.247 0.418 -1.245C0.418 -0.304 0.702 0.603 1.36 1.181C1.425 1.245 1.435 1.245 1.519 1.245H1.758C1.743 1.235 1.26 0.822 1.031 0.045C0.912 -0.344 0.852 -0.752 0.852 -1.245C0.852 -2.969 1.659 -3.651 1.758 -3.736H1.519Z' id='g1-40'/>
+<path d='M0.533 1.245C0.618 1.245 0.628 1.245 0.687 1.191C1.315 0.643 1.634 -0.244 1.634 -1.245C1.634 -2.187 1.35 -3.093 0.692 -3.671C0.628 -3.736 0.618 -3.736 0.533 -3.736H0.294C0.309 -3.726 0.792 -3.313 1.021 -2.535C1.141 -2.147 1.2 -1.738 1.2 -1.245C1.2 0.478 0.394 1.161 0.294 1.245H0.533Z' id='g1-41'/>
+<path d='M0.956 -0.005V-0.448H0.508V0H0.648L0.503 0.638H0.727L0.956 -0.005Z' id='g1-44'/>
+<path d='M1.465 -0.951V-1.265H0.06V-0.951H1.465Z' id='g1-45'/>
+<path d='M0.956 -0.448H0.508V0H0.956V-0.448Z' id='g1-46'/>
+<path d='M2.431 -1.619C2.431 -1.858 2.431 -2.441 2.197 -2.849C1.943 -3.298 1.559 -3.372 1.32 -3.372C1.096 -3.372 0.707 -3.303 0.458 -2.874C0.219 -2.476 0.209 -1.933 0.209 -1.619C0.209 -1.25 0.229 -0.797 0.438 -0.418C0.658 -0.015 1.026 0.105 1.32 0.105C1.818 0.105 2.092 -0.184 2.242 -0.498C2.416 -0.852 2.431 -1.31 2.431 -1.619ZM1.32 -0.224C1.111 -0.224 0.872 -0.344 0.747 -0.702C0.648 -1.006 0.643 -1.32 0.643 -1.684C0.643 -2.142 0.643 -3.044 1.32 -3.044S1.998 -2.142 1.998 -1.684C1.998 -1.355 1.998 -0.981 1.878 -0.663C1.738 -0.304 1.484 -0.224 1.32 -0.224Z' id='g1-48'/>
+<path d='M1.599 -3.372H1.489C1.166 -3.073 0.757 -3.054 0.458 -3.044V-2.73C0.653 -2.735 0.902 -2.745 1.151 -2.844V-0.314H0.488V0H2.262V-0.314H1.599V-3.372Z' id='g1-49'/>
+<path d='M1.41 -0.384C1.35 -0.384 1.29 -0.379 1.23 -0.379H0.663L1.435 -1.061C1.524 -1.141 1.768 -1.325 1.863 -1.405C2.082 -1.604 2.376 -1.863 2.376 -2.296C2.376 -2.859 1.958 -3.372 1.245 -3.372C0.717 -3.372 0.389 -3.088 0.219 -2.58L0.453 -2.286C0.568 -2.705 0.742 -3.029 1.176 -3.029C1.594 -3.029 1.913 -2.735 1.913 -2.286C1.913 -1.873 1.669 -1.639 1.37 -1.355C1.27 -1.255 1.001 -1.031 0.897 -0.932C0.752 -0.802 0.408 -0.468 0.264 -0.344V0H2.376V-0.384H1.41Z' id='g1-50'/>
+<path d='M0.498 -2.555C0.702 -2.954 1.061 -3.064 1.3 -3.064C1.594 -3.064 1.813 -2.894 1.813 -2.61C1.813 -2.346 1.634 -2.022 1.255 -1.958C1.23 -1.953 1.21 -1.953 0.882 -1.928V-1.599H1.27C1.743 -1.599 1.918 -1.225 1.918 -0.912C1.918 -0.523 1.679 -0.224 1.29 -0.224C0.936 -0.224 0.533 -0.394 0.284 -0.712L0.219 -0.389C0.508 -0.065 0.912 0.105 1.3 0.105C1.953 0.105 2.421 -0.384 2.421 -0.907C2.421 -1.315 2.092 -1.644 1.699 -1.758C2.077 -1.953 2.271 -2.286 2.271 -2.61C2.271 -3.034 1.838 -3.372 1.305 -3.372C0.867 -3.372 0.503 -3.143 0.294 -2.844L0.498 -2.555Z' id='g1-51'/>
+<path d='M1.973 -0.832H2.491V-1.161H1.973V-3.268H1.479L0.149 -1.161V-0.832H1.544V0H1.973V-0.832ZM0.573 -1.161C0.722 -1.395 1.579 -2.725 1.579 -3.024V-1.161H0.573Z' id='g1-52'/>
+<path d='M0.817 -2.924H2.197V-3.268H0.418V-1.405H0.782C0.902 -1.674 1.136 -1.793 1.36 -1.793C1.564 -1.793 1.873 -1.654 1.873 -1.021C1.873 -0.369 1.46 -0.224 1.205 -0.224C0.877 -0.224 0.558 -0.399 0.389 -0.682L0.199 -0.384C0.443 -0.08 0.812 0.105 1.205 0.105C1.863 0.105 2.376 -0.403 2.376 -1.011C2.376 -1.629 1.918 -2.122 1.37 -2.122C1.156 -2.122 0.966 -2.047 0.817 -1.923V-2.924Z' id='g1-53'/>
+<path d='M2.187 -3.273C1.918 -3.372 1.729 -3.372 1.634 -3.372C0.877 -3.372 0.219 -2.66 0.219 -1.609C0.219 -0.259 0.827 0.105 1.33 0.105C1.733 0.105 1.943 -0.085 2.097 -0.244C2.416 -0.583 2.421 -0.936 2.421 -1.111C2.421 -1.763 2.067 -2.306 1.584 -2.306C1.096 -2.306 0.832 -2.052 0.687 -1.908C0.752 -2.59 1.101 -3.064 1.639 -3.064C1.738 -3.064 1.938 -3.054 2.187 -2.959V-3.273ZM0.692 -1.096C0.692 -1.126 0.692 -1.2 0.697 -1.225C0.697 -1.564 0.912 -1.978 1.355 -1.978C1.963 -1.978 1.963 -1.28 1.963 -1.111C1.963 -0.922 1.963 -0.712 1.828 -0.503C1.709 -0.324 1.559 -0.224 1.33 -0.224C0.802 -0.224 0.717 -0.877 0.692 -1.096Z' id='g1-54'/>
+<path d='M1.23 -2.884C1.29 -2.884 1.35 -2.889 1.41 -2.889H2.037C1.28 -2.147 0.797 -1.106 0.797 0.05H1.265C1.265 -1.405 1.973 -2.451 2.421 -2.919V-3.268H0.219V-2.884H1.23Z' id='g1-55'/>
+<path d='M1.704 -1.763C2.032 -1.868 2.346 -2.132 2.346 -2.501C2.346 -2.954 1.913 -3.372 1.32 -3.372S0.294 -2.954 0.294 -2.501C0.294 -2.127 0.618 -1.863 0.936 -1.763C0.498 -1.629 0.219 -1.29 0.219 -0.907C0.219 -0.374 0.692 0.105 1.32 0.105S2.421 -0.374 2.421 -0.907C2.421 -1.29 2.137 -1.629 1.704 -1.763ZM1.32 -1.928C0.966 -1.928 0.677 -2.132 0.677 -2.496C0.677 -2.814 0.902 -3.064 1.32 -3.064C1.733 -3.064 1.963 -2.814 1.963 -2.496C1.963 -2.142 1.684 -1.928 1.32 -1.928ZM1.32 -0.224C0.976 -0.224 0.672 -0.443 0.672 -0.912C0.672 -1.36 0.961 -1.599 1.32 -1.599S1.968 -1.355 1.968 -0.912C1.968 -0.443 1.659 -0.224 1.32 -0.224Z' id='g1-56'/>
+<path d='M0.384 -0.125C0.628 0.055 0.852 0.105 1.086 0.105C1.783 0.105 2.421 -0.598 2.421 -1.669C2.421 -3.029 1.818 -3.372 1.34 -3.372C0.897 -3.372 0.692 -3.163 0.548 -3.019C0.229 -2.695 0.219 -2.351 0.219 -2.157C0.219 -1.514 0.568 -0.961 1.056 -0.961C1.619 -0.961 1.928 -1.335 1.953 -1.365C1.883 -0.573 1.494 -0.224 1.086 -0.224C0.827 -0.224 0.667 -0.319 0.553 -0.413L0.384 -0.125ZM1.938 -2.162C1.943 -2.132 1.943 -2.082 1.943 -2.052C1.943 -1.684 1.719 -1.29 1.285 -1.29C1.096 -1.29 0.946 -1.345 0.817 -1.549C0.687 -1.743 0.677 -1.933 0.677 -2.157C0.677 -2.351 0.677 -2.575 0.832 -2.795C0.936 -2.944 1.086 -3.064 1.335 -3.064C1.818 -3.064 1.923 -2.481 1.938 -2.162Z' id='g1-57'/>
+<path d='M2.959 -0.438C2.884 -0.438 2.874 -0.438 2.834 -0.418C2.59 -0.334 2.336 -0.279 2.072 -0.279C1.27 -0.279 0.697 -0.956 0.697 -1.729C0.697 -2.565 1.345 -3.178 2.042 -3.178C2.182 -3.178 2.511 -3.143 2.675 -2.745C2.535 -2.824 2.381 -2.859 2.252 -2.859C1.719 -2.859 1.27 -2.361 1.27 -1.729C1.27 -1.081 1.733 -0.598 2.247 -0.598C2.635 -0.598 3.228 -0.912 3.228 -1.798C3.228 -2.301 3.193 -3.507 2.047 -3.507C1.101 -3.507 0.294 -2.725 0.294 -1.729C0.294 -0.742 1.091 0.05 2.052 0.05C2.511 0.05 2.939 -0.139 3.228 -0.438H2.959ZM2.252 -0.927C1.943 -0.927 1.674 -1.27 1.674 -1.729C1.674 -2.202 1.953 -2.531 2.247 -2.531C2.555 -2.531 2.824 -2.187 2.824 -1.729C2.824 -1.255 2.545 -0.927 2.252 -0.927Z' id='g1-64'/>
+<path d='M3.083 -0.608C2.735 -0.394 2.575 -0.299 2.077 -0.299C1.305 -0.299 0.837 -1.021 0.837 -1.738C0.837 -2.476 1.35 -3.168 2.077 -3.168C2.406 -3.168 2.745 -3.064 2.974 -2.889L3.054 -3.342C2.705 -3.472 2.426 -3.512 2.062 -3.512C1.076 -3.512 0.339 -2.695 0.339 -1.733C0.339 -0.707 1.121 0.05 2.092 0.05C2.58 0.05 2.785 -0.05 3.113 -0.229L3.083 -0.608Z' id='g1-67'/>
+<path d='M2.725 -1.624V-1.953H0.986V-3.098H1.714C1.773 -3.098 1.833 -3.093 1.893 -3.093H2.874V-3.442H0.483V0H2.949V-0.389H2.501C2.082 -0.389 1.664 -0.379 1.245 -0.379H0.986V-1.624H2.725Z' id='g1-69'/>
+<path d='M3.173 -1.489H2.057V-1.161H2.735V-0.399C2.516 -0.344 2.301 -0.299 2.077 -0.299C1.31 -0.299 0.837 -1.021 0.837 -1.733C0.837 -2.416 1.3 -3.168 2.052 -3.168C2.511 -3.168 2.8 -3.029 3.049 -2.819L3.128 -3.273C2.785 -3.437 2.481 -3.517 2.102 -3.517C1.096 -3.517 0.339 -2.73 0.339 -1.733C0.339 -0.762 1.091 0.05 2.072 0.05C2.431 0.05 2.854 -0.03 3.173 -0.194V-1.489Z' id='g1-71'/>
+<path d='M3.248 -3.457H2.745V-1.963H0.986V-3.457H0.483V0H0.986V-1.634H2.745V0H3.248V-3.457Z' id='g1-72'/>
+<path d='M0.986 -3.457H0.483V0H0.986V-3.457Z' id='g1-73'/>
+<path d='M1.176 -3.457H0.498V0H0.917V-3.064H0.922L2.555 0H3.233V-3.457H2.814V-0.394H2.809L1.176 -3.457Z' id='g1-78'/>
+<path d='M3.611 -1.714C3.611 -2.745 2.854 -3.562 1.953 -3.562S0.294 -2.745 0.294 -1.714S1.061 0.105 1.953 0.105C2.849 0.105 3.611 -0.687 3.611 -1.714ZM1.953 -0.249C1.35 -0.249 0.797 -0.852 0.797 -1.793C0.797 -2.675 1.355 -3.218 1.953 -3.218S3.108 -2.675 3.108 -1.793C3.108 -0.847 2.555 -0.249 1.953 -0.249Z' id='g1-79'/>
+<path d='M1.868 -1.42C2.511 -1.42 3.083 -1.873 3.083 -2.446C3.083 -2.979 2.555 -3.457 1.833 -3.457H0.488V0H0.991V-1.42H1.868ZM1.709 -3.163C2.271 -3.163 2.63 -2.864 2.63 -2.446C2.63 -2.037 2.291 -1.729 1.709 -1.729H0.976V-3.163H1.709Z' id='g1-80'/>
+<path d='M3.143 -3.457H2.71V-1.161C2.71 -0.493 2.262 -0.189 1.833 -0.189S0.986 -0.498 0.986 -1.156V-3.457H0.483V-1.166C0.483 -0.433 1.111 0.105 1.828 0.105C2.54 0.105 3.143 -0.438 3.143 -1.166V-3.457Z' id='g1-85'/>
+<path d='M1.968 -1.823L3.228 -3.457H2.685L1.724 -2.182L0.742 -3.457H0.149L1.479 -1.823L0.075 0H0.618L1.724 -1.499L2.854 0H3.447L1.968 -1.823Z' id='g1-88'/>
+<path d='M2.934 -3.238V-3.457H0.369V-3.123H1.41C1.479 -3.123 1.539 -3.128 1.609 -3.128H2.291L0.294 -0.229V0H2.964V-0.354H2.466C1.958 -0.354 1.45 -0.344 0.941 -0.344L2.934 -3.238Z' id='g1-90'/>
+<path d='M2.122 -1.435C2.122 -1.943 1.733 -2.286 1.24 -2.286C0.927 -2.286 0.687 -2.222 0.408 -2.072L0.438 -1.709C0.603 -1.818 0.847 -1.968 1.24 -1.968C1.46 -1.968 1.689 -1.803 1.689 -1.43V-1.23C0.951 -1.205 0.224 -1.051 0.224 -0.588C0.224 -0.339 0.394 0.05 0.832 0.05C1.046 0.05 1.44 0.005 1.704 -0.189V0H2.122V-1.435ZM1.689 -0.707C1.689 -0.608 1.689 -0.478 1.514 -0.374C1.355 -0.284 1.161 -0.279 1.106 -0.279C0.832 -0.279 0.623 -0.403 0.623 -0.593C0.623 -0.912 1.465 -0.941 1.689 -0.951V-0.707Z' id='g1-97'/>
+<path d='M0.842 -3.457H0.423V0H0.857V-0.234C0.966 -0.139 1.205 0.05 1.569 0.05C2.112 0.05 2.55 -0.458 2.55 -1.111C2.55 -1.714 2.207 -2.262 1.709 -2.262C1.395 -2.262 1.091 -2.162 0.842 -1.978V-3.457ZM0.857 -1.569C0.857 -1.649 0.857 -1.709 1.031 -1.823C1.106 -1.868 1.24 -1.933 1.41 -1.933C1.743 -1.933 2.117 -1.709 2.117 -1.111C2.117 -0.503 1.704 -0.279 1.355 -0.279C1.171 -0.279 0.996 -0.364 0.857 -0.588V-1.569Z' id='g1-98'/>
+<path d='M2.167 -0.543C1.918 -0.384 1.649 -0.294 1.34 -0.294C0.882 -0.294 0.613 -0.663 0.613 -1.111C0.613 -1.494 0.812 -1.943 1.355 -1.943C1.694 -1.943 1.853 -1.873 2.107 -1.714L2.172 -2.072C1.873 -2.222 1.743 -2.286 1.355 -2.286C0.608 -2.286 0.179 -1.684 0.179 -1.106C0.179 -0.498 0.658 0.05 1.335 0.05C1.684 0.05 1.983 -0.055 2.197 -0.179L2.167 -0.543Z' id='g1-99'/>
+<path d='M2.306 -3.457H1.888V-1.998C1.569 -2.232 1.245 -2.262 1.101 -2.262C0.578 -2.262 0.179 -1.738 0.179 -1.106S0.573 0.05 1.086 0.05C1.395 0.05 1.684 -0.09 1.873 -0.259V0H2.306V-3.457ZM1.873 -0.618C1.748 -0.413 1.559 -0.279 1.32 -0.279C0.971 -0.279 0.613 -0.523 0.613 -1.101C0.613 -1.724 1.036 -1.933 1.375 -1.933C1.574 -1.933 1.743 -1.848 1.873 -1.679V-0.618Z' id='g1-100'/>
+<path d='M2.142 -0.543C1.863 -0.344 1.549 -0.279 1.335 -0.279C0.902 -0.279 0.573 -0.633 0.558 -1.091H2.192C2.192 -1.32 2.167 -1.654 1.973 -1.938C1.793 -2.192 1.494 -2.286 1.25 -2.286C0.643 -2.286 0.174 -1.753 0.174 -1.121C0.174 -0.483 0.672 0.05 1.33 0.05C1.619 0.05 1.918 -0.035 2.172 -0.189L2.142 -0.543ZM0.593 -1.39C0.707 -1.788 1.001 -1.958 1.25 -1.958C1.469 -1.958 1.793 -1.853 1.888 -1.39H0.593Z' id='g1-101'/>
+<path d='M2.506 -2.262C2.396 -2.262 2.062 -2.257 1.684 -2.112L1.674 -2.107C1.494 -2.227 1.32 -2.262 1.176 -2.262C0.687 -2.262 0.324 -1.878 0.324 -1.45C0.324 -1.275 0.384 -1.096 0.498 -0.956C0.428 -0.872 0.354 -0.732 0.354 -0.543C0.354 -0.349 0.433 -0.224 0.478 -0.164C0.204 -0.005 0.149 0.224 0.149 0.344C0.149 0.722 0.672 1.021 1.32 1.021C1.973 1.021 2.491 0.722 2.491 0.344C2.491 -0.359 1.619 -0.359 1.405 -0.359H0.941C0.862 -0.359 0.648 -0.359 0.648 -0.618C0.648 -0.717 0.682 -0.767 0.687 -0.777C0.862 -0.667 1.036 -0.633 1.171 -0.633C1.659 -0.633 2.022 -1.016 2.022 -1.445C2.022 -1.604 1.978 -1.748 1.888 -1.883C1.868 -1.913 1.868 -1.918 1.868 -1.923C1.868 -1.943 2.167 -1.943 2.192 -1.943C2.197 -1.943 2.386 -1.943 2.565 -1.923L2.506 -2.262ZM1.176 -0.941C0.907 -0.941 0.707 -1.111 0.707 -1.445C0.707 -1.833 0.956 -1.953 1.171 -1.953C1.44 -1.953 1.639 -1.783 1.639 -1.45C1.639 -1.061 1.39 -0.941 1.176 -0.941ZM1.41 0.03C1.524 0.03 2.112 0.03 2.112 0.349C2.112 0.563 1.738 0.712 1.32 0.712S0.528 0.563 0.528 0.349C0.528 0.324 0.528 0.03 0.932 0.03H1.41Z' id='g1-103'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.161 -2.262 0.932 -2.012 0.832 -1.908V-3.457H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-104'/>
+<path d='M0.877 -3.417H0.374V-2.914H0.877V-3.417ZM0.837 -2.212H0.418V0H0.837V-2.212Z' id='g1-105'/>
+<path d='M0.986 -3.417H0.483V-2.914H0.986V-3.417ZM-0.324 0.847C-0.095 0.971 0.13 1.016 0.319 1.016C0.663 1.016 0.986 0.752 0.986 0.294V-2.212H0.568V0.329C0.568 0.418 0.568 0.498 0.463 0.583C0.349 0.667 0.209 0.667 0.164 0.667C-0.045 0.667 -0.174 0.573 -0.234 0.518L-0.324 0.847Z' id='g1-106'/>
+<path d='M1.489 -1.355L2.376 -2.212H1.893L0.827 -1.181V-3.457H0.438V0H0.807V-0.697L1.23 -1.106L2.052 0H2.491L1.489 -1.355Z' id='g1-107'/>
+<path d='M0.837 -3.457H0.418V0H0.837V-3.457Z' id='g1-108'/>
+<path d='M3.786 -1.474C3.786 -1.863 3.671 -2.262 3.059 -2.262C2.64 -2.262 2.381 -2.017 2.262 -1.858C2.212 -1.993 2.087 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.21C2.316 -1.539 2.456 -1.933 2.839 -1.933C3.352 -1.933 3.352 -1.584 3.352 -1.44V0H3.786V-1.474Z' id='g1-109'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-110'/>
+<path d='M2.491 -1.091C2.491 -1.748 1.968 -2.286 1.32 -2.286S0.149 -1.743 0.149 -1.091C0.149 -0.458 0.677 0.05 1.32 0.05C1.968 0.05 2.491 -0.458 2.491 -1.091ZM1.32 -0.294C0.927 -0.294 0.583 -0.583 0.583 -1.146S0.951 -1.958 1.32 -1.958C1.694 -1.958 2.057 -1.699 2.057 -1.146C2.057 -0.578 1.704 -0.294 1.32 -0.294Z' id='g1-111'/>
+<path d='M0.857 -0.234C1.121 0.005 1.405 0.05 1.574 0.05C2.102 0.05 2.55 -0.453 2.55 -1.111C2.55 -1.709 2.222 -2.262 1.729 -2.262C1.504 -2.262 1.131 -2.197 0.842 -1.973V-2.212H0.423V0.966H0.857V-0.234ZM0.857 -1.654C0.971 -1.793 1.166 -1.918 1.405 -1.918C1.803 -1.918 2.117 -1.549 2.117 -1.111C2.117 -0.618 1.743 -0.279 1.355 -0.279C1.28 -0.279 1.156 -0.289 1.026 -0.394C0.877 -0.508 0.857 -0.583 0.857 -0.677V-1.654Z' id='g1-112'/>
+<path d='M0.842 -1.061C0.842 -1.599 1.29 -1.888 1.729 -1.893V-2.262C1.31 -2.257 1.006 -2.052 0.807 -1.788V-2.247H0.423V0H0.842V-1.061Z' id='g1-114'/>
+<path d='M1.818 -2.132C1.479 -2.271 1.23 -2.286 1.051 -2.286C0.927 -2.286 0.174 -2.286 0.174 -1.624C0.174 -1.39 0.304 -1.26 0.369 -1.2C0.543 -1.026 0.752 -0.986 1.016 -0.936C1.25 -0.892 1.519 -0.842 1.519 -0.603C1.519 -0.289 1.106 -0.289 1.036 -0.289C0.717 -0.289 0.418 -0.403 0.219 -0.543L0.149 -0.169C0.319 -0.085 0.623 0.05 1.036 0.05C1.26 0.05 1.479 0.015 1.664 -0.12C1.848 -0.259 1.908 -0.478 1.908 -0.648C1.908 -0.737 1.898 -0.932 1.689 -1.121C1.504 -1.285 1.325 -1.32 1.086 -1.365C0.792 -1.42 0.563 -1.465 0.563 -1.684C0.563 -1.968 0.927 -1.968 1.001 -1.968C1.285 -1.968 1.504 -1.908 1.753 -1.778L1.818 -2.132Z' id='g1-115'/>
+<path d='M0.936 -1.898H1.674V-2.212H0.936V-2.844H0.553V-2.212H0.1V-1.898H0.538V-0.638C0.538 -0.304 0.623 0.05 0.981 0.05S1.614 -0.065 1.763 -0.134L1.679 -0.453C1.514 -0.334 1.34 -0.294 1.2 -0.294C0.991 -0.294 0.936 -0.498 0.936 -0.727V-1.898Z' id='g1-116'/>
+<path d='M2.316 -2.212H1.883V-0.767C1.883 -0.369 1.544 -0.244 1.255 -0.244C0.887 -0.244 0.847 -0.344 0.847 -0.573V-2.212H0.413V-0.543C0.413 -0.1 0.608 0.05 0.956 0.05C1.161 0.05 1.599 0.01 1.898 -0.229V0H2.316V-2.212Z' id='g1-117'/>
+<path d='M3.537 -2.212H3.148C3.103 -2.072 2.824 -1.23 2.67 -0.712C2.63 -0.568 2.58 -0.408 2.565 -0.294H2.56C2.531 -0.498 2.356 -1.036 2.346 -1.071L1.978 -2.212H1.599C1.455 -1.783 1.081 -0.667 1.041 -0.304H1.036C0.996 -0.658 0.628 -1.758 0.548 -1.998C0.508 -2.117 0.508 -2.127 0.483 -2.212H0.075L0.802 0H1.22C1.225 -0.02 1.36 -0.413 1.534 -0.966C1.609 -1.21 1.758 -1.689 1.783 -1.908L1.788 -1.913C1.798 -1.808 1.828 -1.699 1.863 -1.574S1.933 -1.315 1.968 -1.2L2.351 0H2.809L3.537 -2.212Z' id='g1-119'/>
+<path d='M1.38 -1.141L2.346 -2.212H1.908L1.2 -1.395L0.478 -2.212H0.03L1.026 -1.141L0 0H0.443L1.2 -0.936L1.988 0H2.436L1.38 -1.141Z' id='g1-120'/>
+<path d='M2.112 -2.002V-2.212H0.219V-1.893H0.951C1.011 -1.893 1.071 -1.898 1.131 -1.898H1.519L0.149 -0.219V0H2.127V-0.334H1.355C1.295 -0.334 1.235 -0.329 1.176 -0.329H0.742L2.112 -2.002Z' id='g1-122'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g0-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g0-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g0-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g0-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g0-108'/>
+<path d='M6.581 -2.663C6.581 -3.327 6.402 -4.08 5.317 -4.08C4.564 -4.08 4.142 -3.622 3.927 -3.344C3.865 -3.524 3.676 -4.08 2.762 -4.08C2.053 -4.08 1.623 -3.667 1.417 -3.398V-4.035H0.726V0H1.47V-2.188C1.47 -2.78 1.704 -3.497 2.385 -3.497C3.282 -3.497 3.282 -2.86 3.282 -2.6V0H4.026V-2.188C4.026 -2.78 4.259 -3.497 4.94 -3.497C5.837 -3.497 5.837 -2.86 5.837 -2.6V0H6.581V-2.663Z' id='g0-109'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g0-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g0-118'/>
+</defs>
+<g id='page5'>
+<path d='M137.059 194.93V186.074M191.969 194.93V186.074M246.879 194.93V186.074M137.059 51.973V60.828M191.969 51.973V60.828M246.879 51.973V60.828' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M109.606 190.324V186.074M164.516 190.324V186.074M219.422 190.324V186.074M274.332 190.324V186.074M109.606 56.574V60.828M164.516 56.574V60.828M219.422 56.574V60.828M274.332 56.574V60.828' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 186.074H86.402M82.148 154.762H86.402M82.148 123.449H86.402M82.148 92.141H86.402M82.148 60.828H86.402M301.789 186.074H297.535M301.789 154.762H297.535M301.789 123.449H297.535M301.789 92.141H297.535M301.789 60.828H297.535' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 186.074V60.828H301.789V186.074H82.148Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -18.291 59.571)'>
+<use x='114.487' xlink:href='#g2-54' y='140.503'/>
+<use x='118.721' xlink:href='#g2-48' y='140.503'/>
+<use x='122.955' xlink:href='#g2-50' y='140.503'/>
+<use x='127.189' xlink:href='#g2-46' y='140.503'/>
+<use x='129.541' xlink:href='#g2-103' y='140.503'/>
+<use x='133.776' xlink:href='#g2-99' y='140.503'/>
+<use x='137.539' xlink:href='#g2-99' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 27.05 59.571)'>
+<use x='114.487' xlink:href='#g2-54' y='140.503'/>
+<use x='118.721' xlink:href='#g2-50' y='140.503'/>
+<use x='122.955' xlink:href='#g2-48' y='140.503'/>
+<use x='127.189' xlink:href='#g2-46' y='140.503'/>
+<use x='129.541' xlink:href='#g2-111' y='140.503'/>
+<use x='133.776' xlink:href='#g2-109' y='140.503'/>
+<use x='140.501' xlink:href='#g2-110' y='140.503'/>
+<use x='144.873' xlink:href='#g2-101' y='140.503'/>
+<use x='148.637' xlink:href='#g2-116' y='140.503'/>
+<use x='151.695' xlink:href='#g2-112' y='140.503'/>
+<use x='156.067' xlink:href='#g2-112' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 78.694 59.571)'>
+<use x='114.487' xlink:href='#g2-54' y='140.503'/>
+<use x='118.721' xlink:href='#g2-50' y='140.503'/>
+<use x='122.955' xlink:href='#g2-51' y='140.503'/>
+<use x='127.189' xlink:href='#g2-46' y='140.503'/>
+<use x='129.541' xlink:href='#g2-120' y='140.503'/>
+<use x='133.444' xlink:href='#g2-97' y='140.503'/>
+<use x='137.512' xlink:href='#g2-108' y='140.503'/>
+<use x='139.532' xlink:href='#g2-97' y='140.503'/>
+<use x='143.6' xlink:href='#g2-110' y='140.503'/>
+<use x='147.973' xlink:href='#g2-99' y='140.503'/>
+<use x='151.736' xlink:href='#g2-98' y='140.503'/>
+<use x='156.109' xlink:href='#g2-109' y='140.503'/>
+<use x='162.834' xlink:href='#g2-107' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 134.081 59.571)'>
+<use x='114.487' xlink:href='#g2-54' y='140.503'/>
+<use x='118.721' xlink:href='#g2-52' y='140.503'/>
+<use x='122.955' xlink:href='#g2-56' y='140.503'/>
+<use x='127.189' xlink:href='#g2-46' y='140.503'/>
+<use x='129.541' xlink:href='#g2-101' y='140.503'/>
+<use x='133.305' xlink:href='#g2-120' y='140.503'/>
+<use x='137.207' xlink:href='#g2-99' y='140.503'/>
+<use x='140.971' xlink:href='#g2-104' y='140.503'/>
+<use x='145.344' xlink:href='#g2-97' y='140.503'/>
+<use x='149.412' xlink:href='#g2-110' y='140.503'/>
+<use x='153.784' xlink:href='#g2-103' y='140.503'/>
+<use x='158.018' xlink:href='#g2-101' y='140.503'/>
+<use x='161.782' xlink:href='#g2-50' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 47.205)'>
+<use x='114.487' xlink:href='#g1-48' y='140.503'/>
+<use x='117.133' xlink:href='#g1-120' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 15.894)'>
+<use x='114.487' xlink:href='#g1-48' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-53' y='140.503'/>
+<use x='121.25' xlink:href='#g1-120' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -15.418)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-120' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -46.729)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-53' y='140.503'/>
+<use x='121.25' xlink:href='#g1-120' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -78.041)'>
+<use x='114.487' xlink:href='#g1-50' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-120' y='140.503'/>
+</g>
+<path clip-path='url(#clip5)' d='M82.148 123.449H301.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M87.063 186.074H90.301V123.449H87.063ZM141.973 186.074H145.211V123.449H141.973ZM196.883 186.074H200.121V123.449H196.883ZM251.793 186.074H255.031V123.449H251.793Z' fill='#e0e0f0'/>
+<path clip-path='url(#clip5)' d='M87.063 186.074H90.301V123.449H87.063ZM141.973 186.074H145.211V123.449H141.973ZM196.883 186.074H200.121V123.449H196.883ZM251.793 186.074H255.031V123.449H251.793Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M88.684 123.449V123.449' fill='#e0e0f0'/>
+<path clip-path='url(#clip5)' d='M86.691 123.449H90.676' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M88.684 123.449V123.449' fill='#e0e0f0'/>
+<path clip-path='url(#clip5)' d='M86.691 123.449H90.676' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M143.594 123.449V123.449' fill='#e0e0f0'/>
+<path clip-path='url(#clip5)' d='M141.602 123.449H145.586' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M143.594 123.449V123.449' fill='#e0e0f0'/>
+<path clip-path='url(#clip5)' d='M141.602 123.449H145.586' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M198.5 123.449V123.449' fill='#e0e0f0'/>
+<path clip-path='url(#clip5)' d='M196.511 123.449H200.496' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M198.5 123.449V123.449' fill='#e0e0f0'/>
+<path clip-path='url(#clip5)' d='M196.511 123.449H200.496' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M253.41 123.449V123.449' fill='#e0e0f0'/>
+<path clip-path='url(#clip5)' d='M251.418 123.449H255.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M253.41 123.449V123.449' fill='#e0e0f0'/>
+<path clip-path='url(#clip5)' d='M251.418 123.449H255.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M92.293 186.074H95.531V103.535H92.293ZM147.203 186.074H150.441V125.77H147.203ZM202.113 186.074H205.352V124.203H202.113ZM257.024 186.074H260.262V123.074H257.024Z' fill='#c2c2e1'/>
+<path clip-path='url(#clip5)' d='M92.293 186.074H95.531V103.535H92.293ZM147.203 186.074H150.441V125.77H147.203ZM202.113 186.074H205.352V124.203H202.113ZM257.024 186.074H260.262V123.074H257.024Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M93.914 103.535V103.535' fill='#c2c2e1'/>
+<path clip-path='url(#clip5)' d='M91.922 103.535H95.906' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M93.914 103.535V103.535' fill='#c2c2e1'/>
+<path clip-path='url(#clip5)' d='M91.922 103.535H95.906' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M148.824 125.77V125.77' fill='#c2c2e1'/>
+<path clip-path='url(#clip5)' d='M146.832 125.77H150.816' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M148.824 125.77V125.77' fill='#c2c2e1'/>
+<path clip-path='url(#clip5)' d='M146.832 125.77H150.816' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M203.731 124.203V124.203' fill='#c2c2e1'/>
+<path clip-path='url(#clip5)' d='M201.742 124.203H205.727' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M203.731 124.203V124.203' fill='#c2c2e1'/>
+<path clip-path='url(#clip5)' d='M201.742 124.203H205.727' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M258.641 123.074V123.074' fill='#c2c2e1'/>
+<path clip-path='url(#clip5)' d='M256.648 123.074H260.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M258.641 123.074V123.074' fill='#c2c2e1'/>
+<path clip-path='url(#clip5)' d='M256.648 123.074H260.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M97.524 186.074H100.762V123.324H97.524ZM152.434 186.074H155.672V123.828H152.434ZM207.344 186.074H210.582V124.203H207.344ZM262.254 186.074H265.492V123.137H262.254Z' fill='#a3a3d1'/>
+<path clip-path='url(#clip5)' d='M97.524 186.074H100.762V123.324H97.524ZM152.434 186.074H155.672V123.828H152.434ZM207.344 186.074H210.582V124.203H207.344ZM262.254 186.074H265.492V123.137H262.254Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M99.145 123.324V123.324' fill='#a3a3d1'/>
+<path clip-path='url(#clip5)' d='M97.152 123.325H101.136' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M99.145 123.324V123.324' fill='#a3a3d1'/>
+<path clip-path='url(#clip5)' d='M97.152 123.325H101.136' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M154.055 123.828V123.828' fill='#a3a3d1'/>
+<path clip-path='url(#clip5)' d='M152.063 123.829H156.047' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M154.055 123.828V123.828' fill='#a3a3d1'/>
+<path clip-path='url(#clip5)' d='M152.063 123.829H156.047' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M208.961 124.203V124.203' fill='#a3a3d1'/>
+<path clip-path='url(#clip5)' d='M206.972 124.203H210.957' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M208.961 124.203V124.203' fill='#a3a3d1'/>
+<path clip-path='url(#clip5)' d='M206.972 124.203H210.957' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M263.871 123.137V123.137' fill='#a3a3d1'/>
+<path clip-path='url(#clip5)' d='M261.879 123.137H265.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M263.871 123.137V123.137' fill='#a3a3d1'/>
+<path clip-path='url(#clip5)' d='M261.879 123.137H265.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M102.754 186.074H105.992V123.449H102.754ZM157.664 186.074H160.902V125.77H157.664ZM212.574 186.074H215.813V125.391H212.574ZM267.484 186.074H270.723V122.449H267.484Z' fill='#8585c2'/>
+<path clip-path='url(#clip5)' d='M102.754 186.074H105.992V123.449H102.754ZM157.664 186.074H160.902V125.77H157.664ZM212.574 186.074H215.813V125.391H212.574ZM267.484 186.074H270.723V122.449H267.484Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M104.375 123.449V123.449' fill='#8585c2'/>
+<path clip-path='url(#clip5)' d='M102.383 123.449H106.367' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M104.375 123.449V123.449' fill='#8585c2'/>
+<path clip-path='url(#clip5)' d='M102.383 123.449H106.367' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M159.285 125.77V125.77' fill='#8585c2'/>
+<path clip-path='url(#clip5)' d='M157.293 125.77H161.277' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M159.285 125.77V125.77' fill='#8585c2'/>
+<path clip-path='url(#clip5)' d='M157.293 125.77H161.277' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M214.192 125.391V125.391' fill='#8585c2'/>
+<path clip-path='url(#clip5)' d='M212.203 125.39H216.188' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M214.192 125.391V125.391' fill='#8585c2'/>
+<path clip-path='url(#clip5)' d='M212.203 125.39H216.188' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M269.102 122.449V122.449' fill='#8585c2'/>
+<path clip-path='url(#clip5)' d='M267.109 122.449H271.097' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M269.102 122.449V122.449' fill='#8585c2'/>
+<path clip-path='url(#clip5)' d='M267.109 122.449H271.097' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M107.984 186.074H111.223V122.699H107.984ZM162.895 186.074H166.133V117.188H162.895ZM217.805 186.074H221.043V104.727H217.805ZM272.715 186.074H275.953V122.762H272.715Z' fill='#6666b3'/>
+<path clip-path='url(#clip5)' d='M107.984 186.074H111.223V122.699H107.984ZM162.895 186.074H166.133V117.188H162.895ZM217.805 186.074H221.043V104.727H217.805ZM272.715 186.074H275.953V122.762H272.715Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M109.606 122.699V122.699' fill='#6666b3'/>
+<path clip-path='url(#clip5)' d='M107.613 122.699H111.597' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M109.606 122.699V122.699' fill='#6666b3'/>
+<path clip-path='url(#clip5)' d='M107.613 122.699H111.597' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M164.516 117.188V117.188' fill='#6666b3'/>
+<path clip-path='url(#clip5)' d='M162.524 117.188H166.508' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M164.516 117.188V117.188' fill='#6666b3'/>
+<path clip-path='url(#clip5)' d='M162.524 117.188H166.508' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M219.422 104.727V104.727' fill='#6666b3'/>
+<path clip-path='url(#clip5)' d='M217.433 104.727H221.418' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M219.422 104.727V104.727' fill='#6666b3'/>
+<path clip-path='url(#clip5)' d='M217.433 104.727H221.418' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M274.332 122.762V122.762' fill='#6666b3'/>
+<path clip-path='url(#clip5)' d='M272.34 122.762H276.328' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M274.332 122.762V122.762' fill='#6666b3'/>
+<path clip-path='url(#clip5)' d='M272.34 122.762H276.328' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M113.215 186.074H116.453V123.574H113.215ZM168.125 186.074H171.363V119.696H168.125ZM223.035 186.074H226.274V122.637H223.035ZM277.945 186.074H281.184V122.449H277.945Z' fill='#4747a4'/>
+<path clip-path='url(#clip5)' d='M113.215 186.074H116.453V123.574H113.215ZM168.125 186.074H171.363V119.696H168.125ZM223.035 186.074H226.274V122.637H223.035ZM277.945 186.074H281.184V122.449H277.945Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M114.836 123.574V123.574' fill='#4747a4'/>
+<path clip-path='url(#clip5)' d='M112.844 123.574H116.828' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M114.836 123.574V123.574' fill='#4747a4'/>
+<path clip-path='url(#clip5)' d='M112.844 123.574H116.828' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M169.746 119.696V119.696' fill='#4747a4'/>
+<path clip-path='url(#clip5)' d='M167.754 119.695H171.738' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M169.746 119.696V119.696' fill='#4747a4'/>
+<path clip-path='url(#clip5)' d='M167.754 119.695H171.738' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M224.652 122.637V122.637' fill='#4747a4'/>
+<path clip-path='url(#clip5)' d='M222.664 122.637H226.649' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M224.652 122.637V122.637' fill='#4747a4'/>
+<path clip-path='url(#clip5)' d='M222.664 122.637H226.649' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M279.563 122.449V122.449' fill='#4747a4'/>
+<path clip-path='url(#clip5)' d='M277.57 122.449H281.558' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M279.563 122.449V122.449' fill='#4747a4'/>
+<path clip-path='url(#clip5)' d='M277.57 122.449H281.558' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M118.445 186.074H121.684V122.574H118.445ZM173.356 186.074H176.594V117.25H173.356ZM228.266 186.074H231.504V105.477H228.266ZM283.176 186.074H286.414V121.887H283.176Z' fill='#292994'/>
+<path clip-path='url(#clip5)' d='M118.445 186.074H121.684V122.574H118.445ZM173.356 186.074H176.594V117.25H173.356ZM228.266 186.074H231.504V105.477H228.266ZM283.176 186.074H286.414V121.887H283.176Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M120.066 122.574V122.574' fill='#292994'/>
+<path clip-path='url(#clip5)' d='M118.074 122.574H122.058' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M120.066 122.574V122.574' fill='#292994'/>
+<path clip-path='url(#clip5)' d='M118.074 122.574H122.058' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M174.977 117.25V117.25' fill='#292994'/>
+<path clip-path='url(#clip5)' d='M172.985 117.25H176.969' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M174.977 117.25V117.25' fill='#292994'/>
+<path clip-path='url(#clip5)' d='M172.985 117.25H176.969' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M229.883 105.477V105.477' fill='#292994'/>
+<path clip-path='url(#clip5)' d='M227.894 105.477H231.879' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M229.883 105.477V105.477' fill='#292994'/>
+<path clip-path='url(#clip5)' d='M227.894 105.477H231.879' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M284.793 121.887V121.887' fill='#292994'/>
+<path clip-path='url(#clip5)' d='M282.801 121.886H286.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M284.793 121.887V121.887' fill='#292994'/>
+<path clip-path='url(#clip5)' d='M282.801 121.886H286.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M123.676 186.074H126.914V106.606H123.676ZM178.586 186.074H181.824V122.699H178.586ZM233.496 186.074H236.734V122.449H233.496ZM288.406 186.074H291.645V122.012H288.406Z' fill='#0a0a85'/>
+<path clip-path='url(#clip5)' d='M123.676 186.074H126.914V106.606H123.676ZM178.586 186.074H181.824V122.699H178.586ZM233.496 186.074H236.734V122.449H233.496ZM288.406 186.074H291.645V122.012H288.406Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M125.297 106.606V106.606' fill='#0a0a85'/>
+<path clip-path='url(#clip5)' d='M123.305 106.605H127.289' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M125.297 106.606V106.606' fill='#0a0a85'/>
+<path clip-path='url(#clip5)' d='M123.305 106.605H127.289' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M180.207 122.699V122.699' fill='#0a0a85'/>
+<path clip-path='url(#clip5)' d='M178.215 122.699H182.199' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M180.207 122.699V122.699' fill='#0a0a85'/>
+<path clip-path='url(#clip5)' d='M178.215 122.699H182.199' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M235.113 122.449V122.449' fill='#0a0a85'/>
+<path clip-path='url(#clip5)' d='M233.125 122.449H237.11' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M235.113 122.449V122.449' fill='#0a0a85'/>
+<path clip-path='url(#clip5)' d='M233.125 122.449H237.11' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M290.024 122.012V122.012' fill='#0a0a85'/>
+<path clip-path='url(#clip5)' d='M288.031 122.012H292.019' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M290.024 122.012V122.012' fill='#0a0a85'/>
+<path clip-path='url(#clip5)' d='M288.031 122.012H292.019' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M128.906 186.074H132.145V120.633H128.906ZM183.817 186.074H187.055V122.074H183.817ZM238.727 186.074H241.965V119.696H238.727ZM293.637 186.074H296.875V123.387H293.637Z' fill='#000076'/>
+<path clip-path='url(#clip5)' d='M128.906 186.074H132.145V120.633H128.906ZM183.817 186.074H187.055V122.074H183.817ZM238.727 186.074H241.965V119.696H238.727ZM293.637 186.074H296.875V123.387H293.637Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M130.527 120.633V120.633' fill='#000076'/>
+<path clip-path='url(#clip5)' d='M128.535 120.633H132.52' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M130.527 120.633V120.633' fill='#000076'/>
+<path clip-path='url(#clip5)' d='M128.535 120.633H132.52' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M185.438 122.074V122.074' fill='#000076'/>
+<path clip-path='url(#clip5)' d='M183.446 122.074H187.43' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M185.438 122.074V122.074' fill='#000076'/>
+<path clip-path='url(#clip5)' d='M183.446 122.074H187.43' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M240.344 119.696V119.696' fill='#000076'/>
+<path clip-path='url(#clip5)' d='M238.355 119.695H242.34' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M240.344 119.696V119.696' fill='#000076'/>
+<path clip-path='url(#clip5)' d='M238.355 119.695H242.34' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M295.254 123.387V123.387' fill='#000076'/>
+<path clip-path='url(#clip5)' d='M293.262 123.387H297.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M295.254 123.387V123.387' fill='#000076'/>
+<path clip-path='url(#clip5)' d='M293.262 123.387H297.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip5)' d='M233.301 233.301H301.59V211.324H233.301Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 122.136 88.837)'>
+<use x='114.487' xlink:href='#g1-72' y='129.265'/>
+<use x='118.223' xlink:href='#g1-80' y='129.265'/>
+<use x='121.604' xlink:href='#g1-45' y='129.265'/>
+<use x='123.368' xlink:href='#g1-90' y='129.265'/>
+<use x='126.603' xlink:href='#g1-52' y='129.265'/>
+<use x='129.249' xlink:href='#g1-45' y='129.265'/>
+<use x='131.013' xlink:href='#g1-71' y='129.265'/>
+<use x='134.542' xlink:href='#g1-52' y='129.265'/>
+<use x='137.188' xlink:href='#g1-44' y='129.265'/>
+<use x='140.423' xlink:href='#g1-49' y='129.265'/>
+<use x='143.069' xlink:href='#g1-54' y='129.265'/>
+<use x='145.715' xlink:href='#g1-71' y='129.265'/>
+<use x='149.244' xlink:href='#g1-98' y='129.265'/>
+<use x='114.487' xlink:href='#g1-56' y='134.884'/>
+<use x='117.133' xlink:href='#g1-45' y='134.884'/>
+<use x='118.897' xlink:href='#g1-99' y='134.884'/>
+<use x='121.25' xlink:href='#g1-111' y='134.884'/>
+<use x='123.749' xlink:href='#g1-114' y='134.884'/>
+<use x='125.556' xlink:href='#g1-101' y='134.884'/>
+<use x='129.673' xlink:href='#g1-73' y='134.884'/>
+<use x='131.143' xlink:href='#g1-110' y='134.884'/>
+<use x='133.876' xlink:href='#g1-116' y='134.884'/>
+<use x='135.787' xlink:href='#g1-101' y='134.884'/>
+<use x='138.14' xlink:href='#g1-108' y='134.884'/>
+<use x='141.166' xlink:href='#g1-88' y='134.884'/>
+<use x='144.695' xlink:href='#g1-69' y='134.884'/>
+<use x='147.869' xlink:href='#g1-79' y='134.884'/>
+<use x='151.778' xlink:href='#g1-78' y='134.884'/>
+<use x='157.278' xlink:href='#g1-64' y='134.884'/>
+<use x='160.807' xlink:href='#g1-50' y='134.884'/>
+<use x='163.453' xlink:href='#g1-46' y='134.884'/>
+<use x='164.923' xlink:href='#g1-55' y='134.884'/>
+<use x='167.57' xlink:href='#g1-71' y='134.884'/>
+<use x='171.098' xlink:href='#g1-104' y='134.884'/>
+<use x='173.831' xlink:href='#g1-122' y='134.884'/>
+<use x='114.487' xlink:href='#g1-85' y='140.503'/>
+<use x='118.119' xlink:href='#g1-98' y='140.503'/>
+<use x='120.852' xlink:href='#g1-117' y='140.503'/>
+<use x='123.585' xlink:href='#g1-110' y='140.503'/>
+<use x='126.317' xlink:href='#g1-116' y='140.503'/>
+<use x='128.229' xlink:href='#g1-117' y='140.503'/>
+<use x='132.726' xlink:href='#g1-49' y='140.503'/>
+<use x='135.372' xlink:href='#g1-56' y='140.503'/>
+<use x='138.018' xlink:href='#g1-46' y='140.503'/>
+<use x='139.489' xlink:href='#g1-48' y='140.503'/>
+<use x='142.135' xlink:href='#g1-52' y='140.503'/>
+<use x='144.781' xlink:href='#g1-46' y='140.503'/>
+<use x='146.252' xlink:href='#g1-49' y='140.503'/>
+<use x='148.898' xlink:href='#g1-44' y='140.503'/>
+<use x='152.132' xlink:href='#g1-71' y='140.503'/>
+<use x='155.661' xlink:href='#g1-67' y='140.503'/>
+<use x='159.042' xlink:href='#g1-67' y='140.503'/>
+<use x='164.188' xlink:href='#g1-55' y='140.503'/>
+<use x='166.834' xlink:href='#g1-46' y='140.503'/>
+<use x='168.305' xlink:href='#g1-52' y='140.503'/>
+<use x='170.951' xlink:href='#g1-46' y='140.503'/>
+<use x='172.421' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 -50.186 231.913)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 4.724 231.913)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 59.633 231.913)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 114.543 231.913)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 -44.955 211.999)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-51' y='140.503'/>
+<use x='121.25' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 9.954 234.23)'>
+<use x='114.487' xlink:href='#g1-48' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-57' y='140.503'/>
+<use x='121.25' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 64.864 232.665)'>
+<use x='114.487' xlink:href='#g1-48' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-57' y='140.503'/>
+<use x='121.25' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 119.773 231.537)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 -39.725 231.788)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 15.185 232.289)'>
+<use x='114.487' xlink:href='#g1-48' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-57' y='140.503'/>
+<use x='121.25' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 70.094 232.665)'>
+<use x='114.487' xlink:href='#g1-48' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-57' y='140.503'/>
+<use x='121.25' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 125.004 231.6)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 -34.494 231.913)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 20.415 234.23)'>
+<use x='114.487' xlink:href='#g1-48' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-57' y='140.503'/>
+<use x='121.25' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 75.325 233.854)'>
+<use x='114.487' xlink:href='#g1-48' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-57' y='140.503'/>
+<use x='121.25' xlink:href='#g1-55' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 130.234 230.911)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 -29.264 231.162)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 25.646 225.651)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-49' y='140.503'/>
+<use x='121.25' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 80.555 213.189)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-51' y='140.503'/>
+<use x='121.25' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 135.465 231.224)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 -24.033 232.038)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 30.876 228.156)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 85.786 231.099)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 140.695 230.911)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 -18.803 231.036)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 36.107 225.713)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-49' y='140.503'/>
+<use x='121.25' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 91.016 213.94)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-50' y='140.503'/>
+<use x='121.25' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 145.926 230.348)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 -13.572 215.068)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-50' y='140.503'/>
+<use x='121.25' xlink:href='#g1-55' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 41.337 231.162)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 96.247 230.911)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 151.156 230.473)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 -8.342 229.095)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-52' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 46.568 230.535)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 101.477 228.156)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 156.387 231.85)'>
+<use x='114.487' xlink:href='#g1-49' y='140.503'/>
+<use x='117.133' xlink:href='#g1-46' y='140.503'/>
+<use x='118.603' xlink:href='#g1-48' y='140.503'/>
+<use x='121.25' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 -81.143 293.197)'>
+<use x='114.487' xlink:href='#g0-82' y='140.503'/>
+<use x='120.457' xlink:href='#g0-101' y='140.503'/>
+<use x='124.553' xlink:href='#g0-108' y='140.503'/>
+<use x='126.753' xlink:href='#g0-97' y='140.503'/>
+<use x='131.181' xlink:href='#g0-116' y='140.503'/>
+<use x='134.509' xlink:href='#g0-105' y='140.503'/>
+<use x='136.709' xlink:href='#g0-118' y='140.503'/>
+<use x='140.957' xlink:href='#g0-101' y='140.503'/>
+<use x='148.124' xlink:href='#g0-116' y='140.503'/>
+<use x='151.452' xlink:href='#g0-105' y='140.503'/>
+<use x='153.652' xlink:href='#g0-109' y='140.503'/>
+<use x='160.972' xlink:href='#g0-101' y='140.503'/>
+<use x='168.139' xlink:href='#g2-40' y='140.503'/>
+<use x='171.432' xlink:href='#g2-108' y='140.503'/>
+<use x='173.453' xlink:href='#g2-111' y='140.503'/>
+<use x='177.452' xlink:href='#g2-119' y='140.503'/>
+<use x='183' xlink:href='#g2-101' y='140.503'/>
+<use x='186.764' xlink:href='#g2-114' y='140.503'/>
+<use x='192.479' xlink:href='#g2-105' y='140.503'/>
+<use x='194.499' xlink:href='#g2-115' y='140.503'/>
+<use x='200.568' xlink:href='#g2-98' y='140.503'/>
+<use x='205.176' xlink:href='#g2-101' y='140.503'/>
+<use x='208.94' xlink:href='#g2-116' y='140.503'/>
+<use x='211.998' xlink:href='#g2-116' y='140.503'/>
+<use x='215.056' xlink:href='#g2-101' y='140.503'/>
+<use x='218.819' xlink:href='#g2-114' y='140.503'/>
+<use x='221.711' xlink:href='#g2-41' y='140.503'/>
+</g>
+<path d='M82.348 231.446H323.027V214.453H82.348Z' fill='#ffffff'/>
+<path d='M82.348 231.446H323.027V214.453H82.348Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path d='M85.535 224.941H88.523V216.972H85.535ZM91.516 224.941H94.504V218.964H91.516Z' fill='#e0e0f0'/>
+<path d='M85.535 224.941H88.523V216.972H85.535ZM91.516 224.941H94.504V218.964H91.516Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -33.522 84.43)'>
+<use x='131.213' xlink:href='#g2-109' y='140.503'/>
+<use x='137.938' xlink:href='#g2-105' y='140.503'/>
+</g>
+<path d='M109.625 224.941H112.613V216.973H109.625ZM115.602 224.941H118.59V218.965H115.602Z' fill='#c2c2e1'/>
+<path d='M109.625 224.941H112.613V216.972H109.625ZM115.602 224.941H118.59V218.964H115.602Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.56 83.976)'>
+<use x='154.339' xlink:href='#g2-116' y='140.503'/>
+<use x='157.397' xlink:href='#g2-99' y='140.503'/>
+</g>
+<path d='M131.789 224.941H134.777V216.973H131.789ZM137.766 224.941H140.754V218.965H137.766Z' fill='#a3a3d1'/>
+<path d='M131.789 224.941H134.777V216.972H131.789ZM137.766 224.941H140.754V218.964H137.766Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.159 84.43)'>
+<use x='176.102' xlink:href='#g2-106' y='140.503'/>
+<use x='178.357' xlink:href='#g2-101' y='140.503'/>
+</g>
+<path d='M153.152 224.941H156.141V216.973H153.152ZM159.129 224.941H162.117V218.965H159.129Z' fill='#8585c2'/>
+<path d='M153.152 224.941H156.141V216.972H153.152ZM159.129 224.941H162.117V218.964H159.129Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.958 83.47)'>
+<use x='198.263' xlink:href='#g2-115' y='140.503'/>
+<use x='201.509' xlink:href='#g2-110' y='140.503'/>
+</g>
+<path d='M176.113 224.941H179.102V216.973H176.113ZM182.09 224.941H185.078V218.965H182.09Z' fill='#6666b3'/>
+<path d='M176.113 224.941H179.102V216.972H176.113ZM182.09 224.941H185.078V218.964H182.09Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.781 83.47)'>
+<use x='221.047' xlink:href='#g2-114' y='140.503'/>
+<use x='223.939' xlink:href='#g2-112' y='140.503'/>
+</g>
+<path d='M198.719 224.941H201.707V216.973H198.719ZM204.695 224.941H207.684V218.965H204.695Z' fill='#4747a4'/>
+<path d='M198.719 224.941H201.707V216.972H198.719ZM204.695 224.941H207.684V218.964H204.695Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -39.001 84.467)'>
+<use x='249.874' xlink:href='#g2-104' y='140.503'/>
+<use x='254.246' xlink:href='#g2-111' y='140.503'/>
+<use x='258.481' xlink:href='#g2-97' y='140.503'/>
+<use x='262.314' xlink:href='#g2-114' y='140.503'/>
+<use x='265.206' xlink:href='#g2-100' y='140.503'/>
+</g>
+<path d='M233.766 224.941H236.754V216.973H233.766ZM239.742 224.941H242.731V218.965H239.742Z' fill='#292994'/>
+<path d='M233.766 224.941H236.754V216.972H233.766ZM239.742 224.941H242.731V218.964H239.742Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.472 84.467)'>
+<use x='283.392' xlink:href='#g2-103' y='140.503'/>
+<use x='287.626' xlink:href='#g2-108' y='140.503'/>
+<use x='289.646' xlink:href='#g2-105' y='140.503'/>
+<use x='291.666' xlink:href='#g2-98' y='140.503'/>
+<use x='296.274' xlink:href='#g2-99' y='140.503'/>
+</g>
+<path d='M265.754 224.941H268.742V216.973H265.754ZM271.73 224.941H274.719V218.965H271.73Z' fill='#0a0a85'/>
+<path d='M265.754 224.941H268.742V216.972H265.754ZM271.73 224.941H274.719V218.964H271.73Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -35.051 84.467)'>
+<use x='312.959' xlink:href='#g2-116' y='140.503'/>
+<use x='316.017' xlink:href='#g2-98' y='140.503'/>
+<use x='320.389' xlink:href='#g2-98' y='140.503'/>
+</g>
+<path d='M292.898 224.941H295.887V216.973H292.898ZM298.879 224.941H301.867V218.965H298.879Z' fill='#000076'/>
+<path d='M292.898 224.941H295.887V216.972H292.898ZM298.879 224.941H301.867V218.964H298.879Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -35.145 84.43)'>
+<use x='340.198' xlink:href='#g2-115' y='140.503'/>
+<use x='343.444' xlink:href='#g2-109' y='140.503'/>
+<use x='350.169' xlink:href='#g2-105' y='140.503'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='164.687pt' version='1.1' viewBox='52.938 54.996 381.625 164.687' width='381.625pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip3'>
+<path d='M82.148 203.937H434.164V78.691H82.148Z'/>
+</clipPath>
+<use id='g2-40' transform='scale(1.6)' xlink:href='#g1-40'/>
+<use id='g2-41' transform='scale(1.6)' xlink:href='#g1-41'/>
+<use id='g2-78' transform='scale(1.6)' xlink:href='#g1-78'/>
+<use id='g2-97' transform='scale(1.6)' xlink:href='#g1-97'/>
+<use id='g2-98' transform='scale(1.6)' xlink:href='#g1-98'/>
+<use id='g2-99' transform='scale(1.6)' xlink:href='#g1-99'/>
+<use id='g2-100' transform='scale(1.6)' xlink:href='#g1-100'/>
+<use id='g2-101' transform='scale(1.6)' xlink:href='#g1-101'/>
+<use id='g2-102' transform='scale(1.6)' xlink:href='#g1-102'/>
+<use id='g2-105' transform='scale(1.6)' xlink:href='#g1-105'/>
+<use id='g2-108' transform='scale(1.6)' xlink:href='#g1-108'/>
+<use id='g2-110' transform='scale(1.6)' xlink:href='#g1-110'/>
+<use id='g2-111' transform='scale(1.6)' xlink:href='#g1-111'/>
+<use id='g2-112' transform='scale(1.6)' xlink:href='#g1-112'/>
+<use id='g2-114' transform='scale(1.6)' xlink:href='#g1-114'/>
+<use id='g2-115' transform='scale(1.6)' xlink:href='#g1-115'/>
+<use id='g2-116' transform='scale(1.6)' xlink:href='#g1-116'/>
+<use id='g2-119' transform='scale(1.6)' xlink:href='#g1-119'/>
+<path d='M1.519 -3.736C1.435 -3.736 1.425 -3.736 1.365 -3.681C0.737 -3.133 0.418 -2.247 0.418 -1.245C0.418 -0.304 0.702 0.603 1.36 1.181C1.425 1.245 1.435 1.245 1.519 1.245H1.758C1.743 1.235 1.26 0.822 1.031 0.045C0.912 -0.344 0.852 -0.752 0.852 -1.245C0.852 -2.969 1.659 -3.651 1.758 -3.736H1.519Z' id='g1-40'/>
+<path d='M0.533 1.245C0.618 1.245 0.628 1.245 0.687 1.191C1.315 0.643 1.634 -0.244 1.634 -1.245C1.634 -2.187 1.35 -3.093 0.692 -3.671C0.628 -3.736 0.618 -3.736 0.533 -3.736H0.294C0.309 -3.726 0.792 -3.313 1.021 -2.535C1.141 -2.147 1.2 -1.738 1.2 -1.245C1.2 0.478 0.394 1.161 0.294 1.245H0.533Z' id='g1-41'/>
+<path d='M0.956 -0.448H0.508V0H0.956V-0.448Z' id='g1-46'/>
+<path d='M2.431 -1.619C2.431 -1.858 2.431 -2.441 2.197 -2.849C1.943 -3.298 1.559 -3.372 1.32 -3.372C1.096 -3.372 0.707 -3.303 0.458 -2.874C0.219 -2.476 0.209 -1.933 0.209 -1.619C0.209 -1.25 0.229 -0.797 0.438 -0.418C0.658 -0.015 1.026 0.105 1.32 0.105C1.818 0.105 2.092 -0.184 2.242 -0.498C2.416 -0.852 2.431 -1.31 2.431 -1.619ZM1.32 -0.224C1.111 -0.224 0.872 -0.344 0.747 -0.702C0.648 -1.006 0.643 -1.32 0.643 -1.684C0.643 -2.142 0.643 -3.044 1.32 -3.044S1.998 -2.142 1.998 -1.684C1.998 -1.355 1.998 -0.981 1.878 -0.663C1.738 -0.304 1.484 -0.224 1.32 -0.224Z' id='g1-48'/>
+<path d='M1.599 -3.372H1.489C1.166 -3.073 0.757 -3.054 0.458 -3.044V-2.73C0.653 -2.735 0.902 -2.745 1.151 -2.844V-0.314H0.488V0H2.262V-0.314H1.599V-3.372Z' id='g1-49'/>
+<path d='M1.41 -0.384C1.35 -0.384 1.29 -0.379 1.23 -0.379H0.663L1.435 -1.061C1.524 -1.141 1.768 -1.325 1.863 -1.405C2.082 -1.604 2.376 -1.863 2.376 -2.296C2.376 -2.859 1.958 -3.372 1.245 -3.372C0.717 -3.372 0.389 -3.088 0.219 -2.58L0.453 -2.286C0.568 -2.705 0.742 -3.029 1.176 -3.029C1.594 -3.029 1.913 -2.735 1.913 -2.286C1.913 -1.873 1.669 -1.639 1.37 -1.355C1.27 -1.255 1.001 -1.031 0.897 -0.932C0.752 -0.802 0.408 -0.468 0.264 -0.344V0H2.376V-0.384H1.41Z' id='g1-50'/>
+<path d='M0.498 -2.555C0.702 -2.954 1.061 -3.064 1.3 -3.064C1.594 -3.064 1.813 -2.894 1.813 -2.61C1.813 -2.346 1.634 -2.022 1.255 -1.958C1.23 -1.953 1.21 -1.953 0.882 -1.928V-1.599H1.27C1.743 -1.599 1.918 -1.225 1.918 -0.912C1.918 -0.523 1.679 -0.224 1.29 -0.224C0.936 -0.224 0.533 -0.394 0.284 -0.712L0.219 -0.389C0.508 -0.065 0.912 0.105 1.3 0.105C1.953 0.105 2.421 -0.384 2.421 -0.907C2.421 -1.315 2.092 -1.644 1.699 -1.758C2.077 -1.953 2.271 -2.286 2.271 -2.61C2.271 -3.034 1.838 -3.372 1.305 -3.372C0.867 -3.372 0.503 -3.143 0.294 -2.844L0.498 -2.555Z' id='g1-51'/>
+<path d='M1.973 -0.832H2.491V-1.161H1.973V-3.268H1.479L0.149 -1.161V-0.832H1.544V0H1.973V-0.832ZM0.573 -1.161C0.722 -1.395 1.579 -2.725 1.579 -3.024V-1.161H0.573Z' id='g1-52'/>
+<path d='M0.817 -2.924H2.197V-3.268H0.418V-1.405H0.782C0.902 -1.674 1.136 -1.793 1.36 -1.793C1.564 -1.793 1.873 -1.654 1.873 -1.021C1.873 -0.369 1.46 -0.224 1.205 -0.224C0.877 -0.224 0.558 -0.399 0.389 -0.682L0.199 -0.384C0.443 -0.08 0.812 0.105 1.205 0.105C1.863 0.105 2.376 -0.403 2.376 -1.011C2.376 -1.629 1.918 -2.122 1.37 -2.122C1.156 -2.122 0.966 -2.047 0.817 -1.923V-2.924Z' id='g1-53'/>
+<path d='M2.187 -3.273C1.918 -3.372 1.729 -3.372 1.634 -3.372C0.877 -3.372 0.219 -2.66 0.219 -1.609C0.219 -0.259 0.827 0.105 1.33 0.105C1.733 0.105 1.943 -0.085 2.097 -0.244C2.416 -0.583 2.421 -0.936 2.421 -1.111C2.421 -1.763 2.067 -2.306 1.584 -2.306C1.096 -2.306 0.832 -2.052 0.687 -1.908C0.752 -2.59 1.101 -3.064 1.639 -3.064C1.738 -3.064 1.938 -3.054 2.187 -2.959V-3.273ZM0.692 -1.096C0.692 -1.126 0.692 -1.2 0.697 -1.225C0.697 -1.564 0.912 -1.978 1.355 -1.978C1.963 -1.978 1.963 -1.28 1.963 -1.111C1.963 -0.922 1.963 -0.712 1.828 -0.503C1.709 -0.324 1.559 -0.224 1.33 -0.224C0.802 -0.224 0.717 -0.877 0.692 -1.096Z' id='g1-54'/>
+<path d='M1.23 -2.884C1.29 -2.884 1.35 -2.889 1.41 -2.889H2.037C1.28 -2.147 0.797 -1.106 0.797 0.05H1.265C1.265 -1.405 1.973 -2.451 2.421 -2.919V-3.268H0.219V-2.884H1.23Z' id='g1-55'/>
+<path d='M1.704 -1.763C2.032 -1.868 2.346 -2.132 2.346 -2.501C2.346 -2.954 1.913 -3.372 1.32 -3.372S0.294 -2.954 0.294 -2.501C0.294 -2.127 0.618 -1.863 0.936 -1.763C0.498 -1.629 0.219 -1.29 0.219 -0.907C0.219 -0.374 0.692 0.105 1.32 0.105S2.421 -0.374 2.421 -0.907C2.421 -1.29 2.137 -1.629 1.704 -1.763ZM1.32 -1.928C0.966 -1.928 0.677 -2.132 0.677 -2.496C0.677 -2.814 0.902 -3.064 1.32 -3.064C1.733 -3.064 1.963 -2.814 1.963 -2.496C1.963 -2.142 1.684 -1.928 1.32 -1.928ZM1.32 -0.224C0.976 -0.224 0.672 -0.443 0.672 -0.912C0.672 -1.36 0.961 -1.599 1.32 -1.599S1.968 -1.355 1.968 -0.912C1.968 -0.443 1.659 -0.224 1.32 -0.224Z' id='g1-56'/>
+<path d='M0.384 -0.125C0.628 0.055 0.852 0.105 1.086 0.105C1.783 0.105 2.421 -0.598 2.421 -1.669C2.421 -3.029 1.818 -3.372 1.34 -3.372C0.897 -3.372 0.692 -3.163 0.548 -3.019C0.229 -2.695 0.219 -2.351 0.219 -2.157C0.219 -1.514 0.568 -0.961 1.056 -0.961C1.619 -0.961 1.928 -1.335 1.953 -1.365C1.883 -0.573 1.494 -0.224 1.086 -0.224C0.827 -0.224 0.667 -0.319 0.553 -0.413L0.384 -0.125ZM1.938 -2.162C1.943 -2.132 1.943 -2.082 1.943 -2.052C1.943 -1.684 1.719 -1.29 1.285 -1.29C1.096 -1.29 0.946 -1.345 0.817 -1.549C0.687 -1.743 0.677 -1.933 0.677 -2.157C0.677 -2.351 0.677 -2.575 0.832 -2.795C0.936 -2.944 1.086 -3.064 1.335 -3.064C1.818 -3.064 1.923 -2.481 1.938 -2.162Z' id='g1-57'/>
+<path d='M1.176 -3.457H0.498V0H0.917V-3.064H0.922L2.555 0H3.233V-3.457H2.814V-0.394H2.809L1.176 -3.457Z' id='g1-78'/>
+<path d='M2.122 -1.435C2.122 -1.943 1.733 -2.286 1.24 -2.286C0.927 -2.286 0.687 -2.222 0.408 -2.072L0.438 -1.709C0.603 -1.818 0.847 -1.968 1.24 -1.968C1.46 -1.968 1.689 -1.803 1.689 -1.43V-1.23C0.951 -1.205 0.224 -1.051 0.224 -0.588C0.224 -0.339 0.394 0.05 0.832 0.05C1.046 0.05 1.44 0.005 1.704 -0.189V0H2.122V-1.435ZM1.689 -0.707C1.689 -0.608 1.689 -0.478 1.514 -0.374C1.355 -0.284 1.161 -0.279 1.106 -0.279C0.832 -0.279 0.623 -0.403 0.623 -0.593C0.623 -0.912 1.465 -0.941 1.689 -0.951V-0.707Z' id='g1-97'/>
+<path d='M0.842 -3.457H0.423V0H0.857V-0.234C0.966 -0.139 1.205 0.05 1.569 0.05C2.112 0.05 2.55 -0.458 2.55 -1.111C2.55 -1.714 2.207 -2.262 1.709 -2.262C1.395 -2.262 1.091 -2.162 0.842 -1.978V-3.457ZM0.857 -1.569C0.857 -1.649 0.857 -1.709 1.031 -1.823C1.106 -1.868 1.24 -1.933 1.41 -1.933C1.743 -1.933 2.117 -1.709 2.117 -1.111C2.117 -0.503 1.704 -0.279 1.355 -0.279C1.171 -0.279 0.996 -0.364 0.857 -0.588V-1.569Z' id='g1-98'/>
+<path d='M2.167 -0.543C1.918 -0.384 1.649 -0.294 1.34 -0.294C0.882 -0.294 0.613 -0.663 0.613 -1.111C0.613 -1.494 0.812 -1.943 1.355 -1.943C1.694 -1.943 1.853 -1.873 2.107 -1.714L2.172 -2.072C1.873 -2.222 1.743 -2.286 1.355 -2.286C0.608 -2.286 0.179 -1.684 0.179 -1.106C0.179 -0.498 0.658 0.05 1.335 0.05C1.684 0.05 1.983 -0.055 2.197 -0.179L2.167 -0.543Z' id='g1-99'/>
+<path d='M2.306 -3.457H1.888V-1.998C1.569 -2.232 1.245 -2.262 1.101 -2.262C0.578 -2.262 0.179 -1.738 0.179 -1.106S0.573 0.05 1.086 0.05C1.395 0.05 1.684 -0.09 1.873 -0.259V0H2.306V-3.457ZM1.873 -0.618C1.748 -0.413 1.559 -0.279 1.32 -0.279C0.971 -0.279 0.613 -0.523 0.613 -1.101C0.613 -1.724 1.036 -1.933 1.375 -1.933C1.574 -1.933 1.743 -1.848 1.873 -1.679V-0.618Z' id='g1-100'/>
+<path d='M2.142 -0.543C1.863 -0.344 1.549 -0.279 1.335 -0.279C0.902 -0.279 0.573 -0.633 0.558 -1.091H2.192C2.192 -1.32 2.167 -1.654 1.973 -1.938C1.793 -2.192 1.494 -2.286 1.25 -2.286C0.643 -2.286 0.174 -1.753 0.174 -1.121C0.174 -0.483 0.672 0.05 1.33 0.05C1.619 0.05 1.918 -0.035 2.172 -0.189L2.142 -0.543ZM0.593 -1.39C0.707 -1.788 1.001 -1.958 1.25 -1.958C1.469 -1.958 1.793 -1.853 1.888 -1.39H0.593Z' id='g1-101'/>
+<path d='M0.946 -1.898H1.514V-2.212H0.932V-2.785C0.932 -3.128 1.245 -3.178 1.41 -3.178C1.514 -3.178 1.649 -3.163 1.833 -3.093V-3.457C1.704 -3.487 1.549 -3.507 1.415 -3.507C0.902 -3.507 0.528 -3.138 0.528 -2.645V-2.212H0.144V-1.898H0.528V0H0.946V-1.898Z' id='g1-102'/>
+<path d='M0.877 -3.417H0.374V-2.914H0.877V-3.417ZM0.837 -2.212H0.418V0H0.837V-2.212Z' id='g1-105'/>
+<path d='M0.837 -3.457H0.418V0H0.837V-3.457Z' id='g1-108'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-110'/>
+<path d='M2.491 -1.091C2.491 -1.748 1.968 -2.286 1.32 -2.286S0.149 -1.743 0.149 -1.091C0.149 -0.458 0.677 0.05 1.32 0.05C1.968 0.05 2.491 -0.458 2.491 -1.091ZM1.32 -0.294C0.927 -0.294 0.583 -0.583 0.583 -1.146S0.951 -1.958 1.32 -1.958C1.694 -1.958 2.057 -1.699 2.057 -1.146C2.057 -0.578 1.704 -0.294 1.32 -0.294Z' id='g1-111'/>
+<path d='M0.857 -0.234C1.121 0.005 1.405 0.05 1.574 0.05C2.102 0.05 2.55 -0.453 2.55 -1.111C2.55 -1.709 2.222 -2.262 1.729 -2.262C1.504 -2.262 1.131 -2.197 0.842 -1.973V-2.212H0.423V0.966H0.857V-0.234ZM0.857 -1.654C0.971 -1.793 1.166 -1.918 1.405 -1.918C1.803 -1.918 2.117 -1.549 2.117 -1.111C2.117 -0.618 1.743 -0.279 1.355 -0.279C1.28 -0.279 1.156 -0.289 1.026 -0.394C0.877 -0.508 0.857 -0.583 0.857 -0.677V-1.654Z' id='g1-112'/>
+<path d='M0.842 -1.061C0.842 -1.599 1.29 -1.888 1.729 -1.893V-2.262C1.31 -2.257 1.006 -2.052 0.807 -1.788V-2.247H0.423V0H0.842V-1.061Z' id='g1-114'/>
+<path d='M1.818 -2.132C1.479 -2.271 1.23 -2.286 1.051 -2.286C0.927 -2.286 0.174 -2.286 0.174 -1.624C0.174 -1.39 0.304 -1.26 0.369 -1.2C0.543 -1.026 0.752 -0.986 1.016 -0.936C1.25 -0.892 1.519 -0.842 1.519 -0.603C1.519 -0.289 1.106 -0.289 1.036 -0.289C0.717 -0.289 0.418 -0.403 0.219 -0.543L0.149 -0.169C0.319 -0.085 0.623 0.05 1.036 0.05C1.26 0.05 1.479 0.015 1.664 -0.12C1.848 -0.259 1.908 -0.478 1.908 -0.648C1.908 -0.737 1.898 -0.932 1.689 -1.121C1.504 -1.285 1.325 -1.32 1.086 -1.365C0.792 -1.42 0.563 -1.465 0.563 -1.684C0.563 -1.968 0.927 -1.968 1.001 -1.968C1.285 -1.968 1.504 -1.908 1.753 -1.778L1.818 -2.132Z' id='g1-115'/>
+<path d='M0.936 -1.898H1.674V-2.212H0.936V-2.844H0.553V-2.212H0.1V-1.898H0.538V-0.638C0.538 -0.304 0.623 0.05 0.981 0.05S1.614 -0.065 1.763 -0.134L1.679 -0.453C1.514 -0.334 1.34 -0.294 1.2 -0.294C0.991 -0.294 0.936 -0.498 0.936 -0.727V-1.898Z' id='g1-116'/>
+<path d='M3.537 -2.212H3.148C3.103 -2.072 2.824 -1.23 2.67 -0.712C2.63 -0.568 2.58 -0.408 2.565 -0.294H2.56C2.531 -0.498 2.356 -1.036 2.346 -1.071L1.978 -2.212H1.599C1.455 -1.783 1.081 -0.667 1.041 -0.304H1.036C0.996 -0.658 0.628 -1.758 0.548 -1.998C0.508 -2.117 0.508 -2.127 0.483 -2.212H0.075L0.802 0H1.22C1.225 -0.02 1.36 -0.413 1.534 -0.966C1.609 -1.21 1.758 -1.689 1.783 -1.908L1.788 -1.913C1.798 -1.808 1.828 -1.699 1.863 -1.574S1.933 -1.315 1.968 -1.2L2.351 0H2.809L3.537 -2.212Z' id='g1-119'/>
+<path d='M1.38 -1.141L2.346 -2.212H1.908L1.2 -1.395L0.478 -2.212H0.03L1.026 -1.141L0 0H0.443L1.2 -0.936L1.988 0H2.436L1.38 -1.141Z' id='g1-120'/>
+<path d='M1.445 -1.245C1.445 -1.41 1.305 -1.549 1.141 -1.549S0.837 -1.41 0.837 -1.245S0.976 -0.941 1.141 -0.941S1.445 -1.081 1.445 -1.245Z' id='g3-1'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g0-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g0-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g0-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g0-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g0-108'/>
+<path d='M6.581 -2.663C6.581 -3.327 6.402 -4.08 5.317 -4.08C4.564 -4.08 4.142 -3.622 3.927 -3.344C3.865 -3.524 3.676 -4.08 2.762 -4.08C2.053 -4.08 1.623 -3.667 1.417 -3.398V-4.035H0.726V0H1.47V-2.188C1.47 -2.78 1.704 -3.497 2.385 -3.497C3.282 -3.497 3.282 -2.86 3.282 -2.6V0H4.026V-2.188C4.026 -2.78 4.259 -3.497 4.94 -3.497C5.837 -3.497 5.837 -2.86 5.837 -2.6V0H6.581V-2.663Z' id='g0-109'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g0-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g0-118'/>
+</defs>
+<g id='page3'>
+<path d='M140.82 212.793V203.937M199.488 212.793V203.937M258.156 212.793V203.937M316.824 212.793V203.937M375.496 212.793V203.937M140.82 69.836V78.691M199.488 69.836V78.691M258.156 69.836V78.691M316.824 69.836V78.691M375.496 69.836V78.691' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M111.484 208.191V203.937M170.152 208.191V203.937M228.824 208.191V203.937M287.492 208.191V203.937M346.16 208.191V203.937M404.828 208.191V203.937M111.484 74.441V78.691M170.152 74.441V78.691M228.824 74.441V78.691M287.492 74.441V78.691M346.16 74.441V78.691M404.828 74.441V78.691' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 203.937H86.402M82.148 172.625H86.402M82.148 141.316H86.402M82.148 110.004H86.402M82.148 78.691H86.402M434.164 203.937H429.91M434.164 172.625H429.91M434.164 141.316H429.91M434.164 110.004H429.91M434.164 78.691H429.91' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 203.937V78.691H434.164V203.937H82.148Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -11.54 33.989)'>
+<use x='114.487' xlink:href='#g2-99' y='183.949'/>
+<use x='118.25' xlink:href='#g2-102' y='183.949'/>
+<use x='120.838' xlink:href='#g2-114' y='183.949'/>
+<use x='123.73' xlink:href='#g2-97' y='183.949'/>
+<use x='127.798' xlink:href='#g2-99' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 41.401 33.989)'>
+<use x='114.487' xlink:href='#g2-101' y='183.949'/>
+<use x='118.25' xlink:href='#g2-115' y='183.949'/>
+<use x='121.497' xlink:href='#g2-112' y='183.949'/>
+<use x='125.634' xlink:href='#g2-114' y='183.949'/>
+<use x='128.526' xlink:href='#g2-101' y='183.949'/>
+<use x='132.29' xlink:href='#g2-115' y='183.949'/>
+<use x='135.536' xlink:href='#g2-115' y='183.949'/>
+<use x='138.782' xlink:href='#g2-111' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 103.095 33.989)'>
+<use x='114.487' xlink:href='#g2-98' y='183.949'/>
+<use x='118.859' xlink:href='#g2-97' y='183.949'/>
+<use x='122.692' xlink:href='#g2-114' y='183.949'/>
+<use x='125.584' xlink:href='#g2-110' y='183.949'/>
+<use x='129.957' xlink:href='#g2-101' y='183.949'/>
+<use x='133.72' xlink:href='#g2-115' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 162.903 33.989)'>
+<use x='114.487' xlink:href='#g2-108' y='183.949'/>
+<use x='116.507' xlink:href='#g2-101' y='183.949'/>
+<use x='120.271' xlink:href='#g2-97' y='183.949'/>
+<use x='124.339' xlink:href='#g2-110' y='183.949'/>
+<use x='128.711' xlink:href='#g2-78' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 223.526 33.989)'>
+<use x='114.487' xlink:href='#g2-114' y='183.949'/>
+<use x='117.379' xlink:href='#g2-101' y='183.949'/>
+<use x='121.142' xlink:href='#g2-100' y='183.949'/>
+<use x='125.515' xlink:href='#g2-105' y='183.949'/>
+<use x='127.535' xlink:href='#g2-115' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 277.054 33.989)'>
+<use x='114.487' xlink:href='#g2-108' y='183.949'/>
+<use x='116.507' xlink:href='#g2-97' y='183.949'/>
+<use x='120.34' xlink:href='#g2-114' y='183.949'/>
+<use x='123.232' xlink:href='#g2-115' y='183.949'/>
+<use x='126.478' xlink:href='#g2-111' y='183.949'/>
+<use x='130.712' xlink:href='#g2-110' y='183.949'/>
+<use x='135.085' xlink:href='#g2-78' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 21.624)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -9.688)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -40.999)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -72.311)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -103.622)'>
+<use x='114.487' xlink:href='#g1-50' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<path clip-path='url(#clip3)' d='M82.148 141.316H434.164' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M86.328 203.937H89.566V141.316H86.328ZM144.996 203.937H148.234V141.316H144.996ZM203.668 203.937H206.902V141.316H203.668ZM262.336 203.937H265.574V141.316H262.336ZM321.004 203.937H324.242V141.316H321.004ZM379.672 203.937H382.91V141.316H379.672Z' fill='#e0e0f0'/>
+<path clip-path='url(#clip3)' d='M86.328 203.937H89.566V141.316H86.328ZM144.996 203.937H148.234V141.316H144.996ZM203.668 203.937H206.902V141.316H203.668ZM262.336 203.937H265.574V141.316H262.336ZM321.004 203.937H324.242V141.316H321.004ZM379.672 203.937H382.91V141.316H379.672Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M87.949 141.316V141.004' fill='#e0e0f0'/>
+<path clip-path='url(#clip3)' d='M87.949 141.316V141.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M85.953 141.004H89.938' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M87.949 141.316V141.629' fill='#e0e0f0'/>
+<path clip-path='url(#clip3)' d='M87.949 141.316V141.629' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M89.941 141.629H85.957' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M146.617 141.316V138.937' fill='#e0e0f0'/>
+<path clip-path='url(#clip3)' d='M146.617 141.316V138.937' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M144.625 138.938H148.61' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M146.617 141.316V143.695' fill='#e0e0f0'/>
+<path clip-path='url(#clip3)' d='M146.617 141.316V143.695' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M148.61 143.695H144.625' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M205.285 141.316V141.066' fill='#e0e0f0'/>
+<path clip-path='url(#clip3)' d='M205.285 141.316V141.066' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M203.293 141.067H207.277' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M205.285 141.316V141.566' fill='#e0e0f0'/>
+<path clip-path='url(#clip3)' d='M205.285 141.316V141.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M207.278 141.567H203.293' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M263.953 141.316V141.066' fill='#e0e0f0'/>
+<path clip-path='url(#clip3)' d='M263.953 141.316V141.066' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M261.961 141.067H265.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M263.953 141.316V141.566' fill='#e0e0f0'/>
+<path clip-path='url(#clip3)' d='M263.953 141.316V141.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M265.949 141.567H261.961' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M322.625 141.316V140.812' fill='#e0e0f0'/>
+<path clip-path='url(#clip3)' d='M322.625 141.316V140.812' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M320.629 140.812H324.617' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M322.625 141.316V141.816' fill='#e0e0f0'/>
+<path clip-path='url(#clip3)' d='M322.625 141.316V141.816' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M324.617 141.816H320.633' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M381.293 141.316V140' fill='#e0e0f0'/>
+<path clip-path='url(#clip3)' d='M381.293 141.316V140' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M379.301 140H383.285' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M381.293 141.316V142.629' fill='#e0e0f0'/>
+<path clip-path='url(#clip3)' d='M381.293 141.316V142.629' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M383.285 142.629H379.301' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M91.559 203.937H94.797V139.562H91.559ZM150.227 203.937H153.465V138.621H150.227ZM208.899 203.937H212.133V141.441H208.899ZM267.567 203.937H270.805V138.683H267.567ZM326.234 203.937H329.473V137.871H326.234ZM384.902 203.937H388.141V129.855H384.902Z' fill='#c2c2e1'/>
+<path clip-path='url(#clip3)' d='M91.559 203.937H94.797V139.562H91.559ZM150.227 203.937H153.465V138.621H150.227ZM208.899 203.937H212.133V141.441H208.899ZM267.567 203.937H270.805V138.683H267.567ZM326.234 203.937H329.473V137.871H326.234ZM384.902 203.937H388.141V129.855H384.902Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M93.18 139.562V139.562' fill='#c2c2e1'/>
+<path clip-path='url(#clip3)' d='M91.184 139.563H95.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M93.18 139.562V139.562' fill='#c2c2e1'/>
+<path clip-path='url(#clip3)' d='M91.184 139.563H95.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M151.848 138.621V138.621' fill='#c2c2e1'/>
+<path clip-path='url(#clip3)' d='M149.855 138.621H153.84' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M151.848 138.621V138.621' fill='#c2c2e1'/>
+<path clip-path='url(#clip3)' d='M149.855 138.621H153.84' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M210.516 141.441V141.254' fill='#c2c2e1'/>
+<path clip-path='url(#clip3)' d='M210.516 141.441V141.254' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M208.523 141.254H212.507' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M210.516 141.441V141.629' fill='#c2c2e1'/>
+<path clip-path='url(#clip3)' d='M210.516 141.441V141.629' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M212.508 141.629H208.523' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M269.184 138.683V138.621' fill='#c2c2e1'/>
+<path clip-path='url(#clip3)' d='M269.184 138.683V138.621' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M267.191 138.621H271.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M269.184 138.683V138.746' fill='#c2c2e1'/>
+<path clip-path='url(#clip3)' d='M269.184 138.683V138.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M271.179 138.746H267.191' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M327.856 137.871V137.558' fill='#c2c2e1'/>
+<path clip-path='url(#clip3)' d='M327.856 137.871V137.558' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M325.859 137.559H329.847' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M327.856 137.871V138.183' fill='#c2c2e1'/>
+<path clip-path='url(#clip3)' d='M327.856 137.871V138.183' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M329.847 138.183H325.863' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M386.524 129.855V129.543' fill='#c2c2e1'/>
+<path clip-path='url(#clip3)' d='M386.524 129.855V129.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M384.531 129.543H388.515' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M386.524 129.855V130.168' fill='#c2c2e1'/>
+<path clip-path='url(#clip3)' d='M386.524 129.855V130.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M388.515 130.168H384.531' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M96.789 203.937H100.027V138.746H96.789ZM155.457 203.937H158.695V135.867H155.457ZM214.129 203.937H217.363V141.379H214.129ZM272.797 203.937H276.035V140.25H272.797ZM331.465 203.937H334.703V130.918H331.465ZM390.133 203.937H393.371V115.515H390.133Z' fill='#a3a3d1'/>
+<path clip-path='url(#clip3)' d='M96.789 203.937H100.027V138.746H96.789ZM155.457 203.937H158.695V135.867H155.457ZM214.129 203.937H217.363V141.379H214.129ZM272.797 203.937H276.035V140.25H272.797ZM331.465 203.937H334.703V130.918H331.465ZM390.133 203.937H393.371V115.515H390.133Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M98.41 138.746V138.558' fill='#a3a3d1'/>
+<path clip-path='url(#clip3)' d='M98.41 138.746V138.558' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M96.414 138.559H100.399' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M98.41 138.746V138.937' fill='#a3a3d1'/>
+<path clip-path='url(#clip3)' d='M98.41 138.746V138.937' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M100.402 138.938H96.418' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M157.078 135.867V135.867' fill='#a3a3d1'/>
+<path clip-path='url(#clip3)' d='M155.086 135.867H159.071' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M157.078 135.867V135.867' fill='#a3a3d1'/>
+<path clip-path='url(#clip3)' d='M155.086 135.867H159.071' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M215.746 141.379V141.129' fill='#a3a3d1'/>
+<path clip-path='url(#clip3)' d='M215.746 141.379V141.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M213.754 141.128H217.739' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M215.746 141.379V141.629' fill='#a3a3d1'/>
+<path clip-path='url(#clip3)' d='M215.746 141.379V141.629' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M217.739 141.629H213.754' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M274.414 140.25V140.187' fill='#a3a3d1'/>
+<path clip-path='url(#clip3)' d='M274.414 140.25V140.187' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M272.422 140.187H276.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M274.414 140.25V140.312' fill='#a3a3d1'/>
+<path clip-path='url(#clip3)' d='M274.414 140.25V140.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M276.41 140.312H272.422' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M333.086 130.918V130.73' fill='#a3a3d1'/>
+<path clip-path='url(#clip3)' d='M333.086 130.918V130.73' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M331.09 130.73H335.078' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M333.086 130.918V131.109' fill='#a3a3d1'/>
+<path clip-path='url(#clip3)' d='M333.086 130.918V131.109' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M335.078 131.11H331.094' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M391.754 115.515V114.636' fill='#a3a3d1'/>
+<path clip-path='url(#clip3)' d='M391.754 115.515V114.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M389.762 114.637H393.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M391.754 115.515V116.39' fill='#a3a3d1'/>
+<path clip-path='url(#clip3)' d='M391.754 115.515V116.39' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M393.746 116.39H389.762' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M102.02 203.937H105.258V130.293H102.02ZM160.688 203.937H163.926V133.176H160.688ZM219.359 203.937H222.594V141.066H219.359ZM278.027 203.937H281.266V139.312H278.027ZM336.695 203.937H339.934V136.679H336.695ZM395.363 203.937H398.602V96.789H395.363Z' fill='#8585c2'/>
+<path clip-path='url(#clip3)' d='M102.02 203.937H105.258V130.293H102.02ZM160.688 203.937H163.926V133.176H160.688ZM219.359 203.937H222.594V141.066H219.359ZM278.027 203.937H281.266V139.312H278.027ZM336.695 203.937H339.934V136.679H336.695ZM395.363 203.937H398.602V96.789H395.363Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M103.641 130.293V129.98' fill='#8585c2'/>
+<path clip-path='url(#clip3)' d='M103.641 130.293V129.98' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M101.644 129.981H105.629' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M103.641 130.293V130.605' fill='#8585c2'/>
+<path clip-path='url(#clip3)' d='M103.641 130.293V130.605' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M105.633 130.606H101.648' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M162.309 133.176V133.176' fill='#8585c2'/>
+<path clip-path='url(#clip3)' d='M160.316 133.176H164.301' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M162.309 133.176V133.176' fill='#8585c2'/>
+<path clip-path='url(#clip3)' d='M160.316 133.176H164.301' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M220.977 141.066V140.941' fill='#8585c2'/>
+<path clip-path='url(#clip3)' d='M220.977 141.066V140.941' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M218.984 140.942H222.969' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M220.977 141.066V141.191' fill='#8585c2'/>
+<path clip-path='url(#clip3)' d='M220.977 141.066V141.191' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M222.969 141.191H218.984' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M279.645 139.312V139.125' fill='#8585c2'/>
+<path clip-path='url(#clip3)' d='M279.645 139.312V139.125' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M277.652 139.125H281.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M279.645 139.312V139.5' fill='#8585c2'/>
+<path clip-path='url(#clip3)' d='M279.645 139.312V139.5' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M281.64 139.5H277.652' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M338.317 136.679V136.554' fill='#8585c2'/>
+<path clip-path='url(#clip3)' d='M338.317 136.679V136.554' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M336.32 136.555H340.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M338.317 136.679V136.804' fill='#8585c2'/>
+<path clip-path='url(#clip3)' d='M338.317 136.679V136.804' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M340.308 136.804H336.324' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M396.984 96.789V96.164' fill='#8585c2'/>
+<path clip-path='url(#clip3)' d='M396.984 96.789V96.164' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M394.992 96.164H398.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M396.984 96.789V97.418' fill='#8585c2'/>
+<path clip-path='url(#clip3)' d='M396.984 96.789V97.418' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M398.976 97.418H394.992' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M107.25 203.937H110.488V138.621H107.25ZM165.918 203.937H169.156V137.683H165.918ZM224.59 203.937H227.824V141.254H224.59ZM283.258 203.937H286.496V131.859H283.258ZM341.926 203.937H345.164V132.988H341.926ZM400.594 203.937H403.832V124.844H400.594Z' fill='#6666b3'/>
+<path clip-path='url(#clip3)' d='M107.25 203.937H110.488V138.621H107.25ZM165.918 203.937H169.156V137.683H165.918ZM224.59 203.937H227.824V141.254H224.59ZM283.258 203.937H286.496V131.859H283.258ZM341.926 203.937H345.164V132.988H341.926ZM400.594 203.937H403.832V124.844H400.594Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M108.871 138.621V138.433' fill='#6666b3'/>
+<path clip-path='url(#clip3)' d='M108.871 138.621V138.433' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M106.875 138.434H110.86' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M108.871 138.621V138.808' fill='#6666b3'/>
+<path clip-path='url(#clip3)' d='M108.871 138.621V138.808' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M110.864 138.808H106.879' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M167.539 137.683V136.429' fill='#6666b3'/>
+<path clip-path='url(#clip3)' d='M167.539 137.683V136.429' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M165.547 136.43H169.532' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M167.539 137.683V138.937' fill='#6666b3'/>
+<path clip-path='url(#clip3)' d='M167.539 137.683V138.937' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M169.532 138.938H165.547' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M226.207 141.254V141.066' fill='#6666b3'/>
+<path clip-path='url(#clip3)' d='M226.207 141.254V141.066' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M224.215 141.067H228.2' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M226.207 141.254V141.441' fill='#6666b3'/>
+<path clip-path='url(#clip3)' d='M226.207 141.254V141.441' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M228.2 141.442H224.215' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M284.875 131.859V131.609' fill='#6666b3'/>
+<path clip-path='url(#clip3)' d='M284.875 131.859V131.609' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M282.883 131.61H286.868' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M284.875 131.859V132.109' fill='#6666b3'/>
+<path clip-path='url(#clip3)' d='M284.875 131.859V132.109' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M286.868 132.11H282.883' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M343.547 132.988V132.988' fill='#6666b3'/>
+<path clip-path='url(#clip3)' d='M341.551 132.988H345.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M343.547 132.988V132.988' fill='#6666b3'/>
+<path clip-path='url(#clip3)' d='M341.551 132.988H345.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M402.215 124.844V124.844' fill='#6666b3'/>
+<path clip-path='url(#clip3)' d='M400.223 124.843H404.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M402.215 124.844V124.844' fill='#6666b3'/>
+<path clip-path='url(#clip3)' d='M400.223 124.843H404.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M112.481 203.937H115.719V136.179H112.481ZM171.149 203.937H174.387V133.176H171.149ZM229.82 203.937H233.055V140.941H229.82ZM288.488 203.937H291.727V134.051H288.488ZM347.156 203.937H350.395V109.129H347.156ZM405.824 203.937H409.063V123.469H405.824Z' fill='#4747a4'/>
+<path clip-path='url(#clip3)' d='M112.481 203.937H115.719V136.179H112.481ZM171.149 203.937H174.387V133.176H171.149ZM229.82 203.937H233.055V140.941H229.82ZM288.488 203.937H291.727V134.051H288.488ZM347.156 203.937H350.395V109.129H347.156ZM405.824 203.937H409.063V123.469H405.824Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M114.098 136.179V135.867' fill='#4747a4'/>
+<path clip-path='url(#clip3)' d='M114.098 136.179V135.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M112.105 135.867H116.09' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M114.098 136.179V136.492' fill='#4747a4'/>
+<path clip-path='url(#clip3)' d='M114.098 136.179V136.492' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M116.094 136.492H112.109' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M172.77 133.176V133.176' fill='#4747a4'/>
+<path clip-path='url(#clip3)' d='M170.777 133.176H174.762' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M172.77 133.176V133.176' fill='#4747a4'/>
+<path clip-path='url(#clip3)' d='M170.777 133.176H174.762' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M231.438 140.941V140.687' fill='#4747a4'/>
+<path clip-path='url(#clip3)' d='M231.438 140.941V140.687' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M229.445 140.687H233.43' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M231.438 140.941V141.191' fill='#4747a4'/>
+<path clip-path='url(#clip3)' d='M231.438 140.941V141.191' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M233.43 141.191H229.445' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M290.106 134.051V133.926' fill='#4747a4'/>
+<path clip-path='url(#clip3)' d='M290.106 134.051V133.926' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M288.113 133.926H292.098' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M290.106 134.051V134.176' fill='#4747a4'/>
+<path clip-path='url(#clip3)' d='M290.106 134.051V134.176' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M292.098 134.176H288.113' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M348.777 109.129V108.937' fill='#4747a4'/>
+<path clip-path='url(#clip3)' d='M348.777 109.129V108.937' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M346.781 108.937H350.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M348.777 109.129V109.316' fill='#4747a4'/>
+<path clip-path='url(#clip3)' d='M348.777 109.129V109.316' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M350.769 109.316H346.785' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M407.445 123.469V122.465' fill='#4747a4'/>
+<path clip-path='url(#clip3)' d='M407.445 123.469V122.465' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M405.453 122.465H409.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M407.445 123.469V124.469' fill='#4747a4'/>
+<path clip-path='url(#clip3)' d='M407.445 123.469V124.469' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M409.437 124.469H405.453' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M117.711 203.937H120.949V139.062H117.711ZM176.379 203.937H179.617V133.176H176.379ZM235.051 203.937H238.285V140.941H235.051ZM293.719 203.937H296.957V131.734H293.719ZM352.387 203.937H355.625V133.238H352.387ZM411.055 203.937H414.293V124.781H411.055Z' fill='#292994'/>
+<path clip-path='url(#clip3)' d='M117.711 203.937H120.949V139.062H117.711ZM176.379 203.937H179.617V133.176H176.379ZM235.051 203.937H238.285V140.941H235.051ZM293.719 203.937H296.957V131.734H293.719ZM352.387 203.937H355.625V133.238H352.387ZM411.055 203.937H414.293V124.781H411.055Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M119.328 139.062V138.871' fill='#292994'/>
+<path clip-path='url(#clip3)' d='M119.328 139.062V138.871' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M117.336 138.871H121.321' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M119.328 139.062V139.25' fill='#292994'/>
+<path clip-path='url(#clip3)' d='M119.328 139.062V139.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M121.325 139.25H117.34' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M178 133.176V133.176' fill='#292994'/>
+<path clip-path='url(#clip3)' d='M176.008 133.176H179.993' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M178 133.176V133.176' fill='#292994'/>
+<path clip-path='url(#clip3)' d='M176.008 133.176H179.993' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M236.668 140.941V140.5' fill='#292994'/>
+<path clip-path='url(#clip3)' d='M236.668 140.941V140.5' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M234.676 140.5H238.661' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M236.668 140.941V141.379' fill='#292994'/>
+<path clip-path='url(#clip3)' d='M236.668 140.941V141.379' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M238.66 141.379H234.675' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M295.336 131.734V131.672' fill='#292994'/>
+<path clip-path='url(#clip3)' d='M295.336 131.734V131.672' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M293.344 131.672H297.329' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M295.336 131.734V131.797' fill='#292994'/>
+<path clip-path='url(#clip3)' d='M295.336 131.734V131.797' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M297.328 131.797H293.343' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M354.008 133.238V133.176' fill='#292994'/>
+<path clip-path='url(#clip3)' d='M354.008 133.238V133.176' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M352.012 133.175H356' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M354.008 133.238V133.301' fill='#292994'/>
+<path clip-path='url(#clip3)' d='M354.008 133.238V133.301' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M355.999 133.301H352.015' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M412.676 124.781V124.719' fill='#292994'/>
+<path clip-path='url(#clip3)' d='M412.676 124.781V124.719' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M410.684 124.719H414.668' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M412.676 124.781V124.844' fill='#292994'/>
+<path clip-path='url(#clip3)' d='M412.676 124.781V124.844' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M414.667 124.843H410.683' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M122.941 203.937H126.18V127.601H122.941ZM181.609 203.937H184.848V131.797H181.609ZM240.281 203.937H243.516V141.316H240.281ZM298.949 203.937H302.188V140H298.949ZM357.617 203.937H360.856V109.691H357.617ZM416.285 203.937H419.524V105.308H416.285Z' fill='#0a0a85'/>
+<path clip-path='url(#clip3)' d='M122.941 203.937H126.18V127.601H122.941ZM181.609 203.937H184.848V131.797H181.609ZM240.281 203.937H243.516V141.316H240.281ZM298.949 203.937H302.188V140H298.949ZM357.617 203.937H360.856V109.691H357.617ZM416.285 203.937H419.524V105.308H416.285Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M124.559 127.601V127.351' fill='#0a0a85'/>
+<path clip-path='url(#clip3)' d='M124.559 127.601V127.351' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M122.566 127.351H126.551' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M124.559 127.601V127.851' fill='#0a0a85'/>
+<path clip-path='url(#clip3)' d='M124.559 127.601V127.851' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M126.555 127.851H122.57' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M183.231 131.797V129.418' fill='#0a0a85'/>
+<path clip-path='url(#clip3)' d='M183.231 131.797V129.418' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M181.238 129.418H185.223' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M183.231 131.797V134.176' fill='#0a0a85'/>
+<path clip-path='url(#clip3)' d='M183.231 131.797V134.176' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M185.223 134.176H181.238' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M241.899 141.316V141.129' fill='#0a0a85'/>
+<path clip-path='url(#clip3)' d='M241.899 141.316V141.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M239.906 141.128H243.891' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M241.899 141.316V141.504' fill='#0a0a85'/>
+<path clip-path='url(#clip3)' d='M241.899 141.316V141.504' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M243.891 141.504H239.906' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M300.567 140V139.875' fill='#0a0a85'/>
+<path clip-path='url(#clip3)' d='M300.567 140V139.875' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M298.574 139.875H302.559' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M300.567 140V140.125' fill='#0a0a85'/>
+<path clip-path='url(#clip3)' d='M300.567 140V140.125' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M302.559 140.125H298.574' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M359.238 109.691V108.812' fill='#0a0a85'/>
+<path clip-path='url(#clip3)' d='M359.238 109.691V108.812' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M357.242 108.813H361.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M359.238 109.691V110.566' fill='#0a0a85'/>
+<path clip-path='url(#clip3)' d='M359.238 109.691V110.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M361.23 110.566H357.246' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M417.906 105.308V104.554' fill='#0a0a85'/>
+<path clip-path='url(#clip3)' d='M417.906 105.308V104.554' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M415.914 104.555H419.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M417.906 105.308V106.058' fill='#0a0a85'/>
+<path clip-path='url(#clip3)' d='M417.906 105.308V106.058' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M419.898 106.059H415.914' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M128.172 203.937H131.41V116.828H128.172ZM186.84 203.937H190.078V126.785H186.84ZM245.512 203.937H248.746V144.195H245.512ZM362.848 203.937H366.086V117.582H362.848ZM421.516 203.937H424.754V78.691H421.516Z' fill='#000076'/>
+<path clip-path='url(#clip3)' d='M128.172 203.937H131.41V116.828H128.172ZM186.84 203.937H190.078V126.785H186.84ZM245.512 203.937H248.746V144.195H245.512ZM362.848 203.937H366.086V117.582H362.848ZM421.516 203.937H424.754V78.691H421.516Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M129.789 116.828V116.515' fill='#000076'/>
+<path clip-path='url(#clip3)' d='M129.789 116.828V116.515' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M127.797 116.516H131.782' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M129.789 116.828V117.144' fill='#000076'/>
+<path clip-path='url(#clip3)' d='M129.789 116.828V117.144' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M131.785 117.145H127.8' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M188.461 126.785V124.219' fill='#000076'/>
+<path clip-path='url(#clip3)' d='M188.461 126.785V124.219' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M186.469 124.219H190.454' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M188.461 126.785V129.355' fill='#000076'/>
+<path clip-path='url(#clip3)' d='M188.461 126.785V129.355' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M190.453 129.355H186.468' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M247.129 144.195V144.07' fill='#000076'/>
+<path clip-path='url(#clip3)' d='M247.129 144.195V144.07' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M245.137 144.071H249.122' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M247.129 144.195V144.32' fill='#000076'/>
+<path clip-path='url(#clip3)' d='M247.129 144.195V144.32' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M249.121 144.32H245.136' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M364.469 117.582V117.269' fill='#000076'/>
+<path clip-path='url(#clip3)' d='M364.469 117.582V117.269' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M362.473 117.269H366.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M364.469 117.582V117.894' fill='#000076'/>
+<path clip-path='url(#clip3)' d='M364.469 117.582V117.894' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M366.46 117.894H362.476' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M423.137 78.691V78.691' fill='#000076'/>
+<path clip-path='url(#clip3)' d='M421.144 78.691H425.128' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M423.137 78.691V78.691' fill='#000076'/>
+<path clip-path='url(#clip3)' d='M421.144 78.691H425.128' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M133.402 203.937H136.641V140.5H133.402ZM192.07 203.937H195.309V138.621H192.07ZM250.742 203.937H253.977V140.875H250.742ZM309.41 203.937H312.649V140.625H309.41ZM368.078 203.937H371.317V141.066H368.078ZM426.746 203.937H429.984V142.629H426.746Z' fill='#000067'/>
+<path clip-path='url(#clip3)' d='M133.402 203.937H136.641V140.5H133.402ZM192.07 203.937H195.309V138.621H192.07ZM250.742 203.937H253.977V140.875H250.742ZM309.41 203.937H312.649V140.625H309.41ZM368.078 203.937H371.317V141.066H368.078ZM426.746 203.937H429.984V142.629H426.746Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M135.02 140.5V140.187' fill='#000067'/>
+<path clip-path='url(#clip3)' d='M135.02 140.5V140.187' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M133.027 140.187H137.012' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M135.02 140.5V140.812' fill='#000067'/>
+<path clip-path='url(#clip3)' d='M135.02 140.5V140.812' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M137.016 140.812H133.031' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M193.692 138.621V138.621' fill='#000067'/>
+<path clip-path='url(#clip3)' d='M191.699 138.621H195.684' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M193.692 138.621V138.621' fill='#000067'/>
+<path clip-path='url(#clip3)' d='M191.699 138.621H195.684' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M252.359 140.875V140.5' fill='#000067'/>
+<path clip-path='url(#clip3)' d='M252.359 140.875V140.5' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M250.367 140.5H254.352' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M252.359 140.875V141.254' fill='#000067'/>
+<path clip-path='url(#clip3)' d='M252.359 140.875V141.254' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M254.352 141.254H250.367' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M311.027 140.625V140.375' fill='#000067'/>
+<path clip-path='url(#clip3)' d='M311.027 140.625V140.375' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M309.035 140.375H313.02' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M311.027 140.625V140.875' fill='#000067'/>
+<path clip-path='url(#clip3)' d='M311.027 140.625V140.875' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M313.02 140.875H309.035' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M369.699 141.066V140.812' fill='#000067'/>
+<path clip-path='url(#clip3)' d='M369.699 141.066V140.812' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M367.703 140.812H371.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M369.699 141.066V141.316' fill='#000067'/>
+<path clip-path='url(#clip3)' d='M369.699 141.066V141.316' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M371.692 141.316H367.707' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M428.367 142.629V140.062' fill='#000067'/>
+<path clip-path='url(#clip3)' d='M428.367 142.629V140.062' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M426.375 140.063H430.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M428.367 142.629V145.199' fill='#000067'/>
+<path clip-path='url(#clip3)' d='M428.367 142.629V145.199' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip3)' d='M430.359 145.199H426.375' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(0 -1 1 0 -94.367 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -35.698 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 22.971 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 81.64 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 140.309 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 198.978 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -89.137 248.024)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -30.468 247.085)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 28.201 249.903)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 86.87 247.147)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 145.539 246.333)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 204.208 238.318)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -83.906 247.21)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -25.237 244.329)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-57' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 33.432 249.84)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 92.101 248.713)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 150.77 239.382)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-55' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 209.439 223.977)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-52' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -78.676 238.756)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -20.007 241.637)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 38.662 249.527)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 97.331 247.774)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 156 245.143)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-55' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 214.669 205.253)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-55' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -73.445 247.085)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -14.776 246.145)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 43.893 249.715)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 102.562 240.321)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 161.231 241.449)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 219.9 233.308)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -68.215 244.642)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -9.546 241.637)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 49.123 249.402)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 107.792 242.513)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 166.461 217.589)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 225.13 231.93)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -62.984 247.523)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -4.315 241.637)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 54.354 249.402)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 113.023 240.196)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 171.692 241.699)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 230.361 233.245)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -57.754 236.063)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 0.915 240.259)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 59.584 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 118.253 248.462)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 176.922 218.153)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 235.591 213.769)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-55' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -52.524 225.292)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-51' y='183.949'/>
+<use x='121.25' xlink:href='#g1-57' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 6.145 235.249)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 64.814 252.658)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-57' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 182.152 226.043)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-51' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 240.821 187.155)'>
+<use x='109.598' xlink:href='#g3-1' y='183.949'/>
+<use x='113.103' xlink:href='#g3-1' y='183.949'/>
+<use x='116.608' xlink:href='#g3-1' y='183.949'/>
+<use x='120.114' xlink:href='#g1-50' y='183.949'/>
+<use x='122.76' xlink:href='#g1-46' y='183.949'/>
+<use x='124.23' xlink:href='#g1-52' y='183.949'/>
+<use x='126.877' xlink:href='#g1-52' y='183.949'/>
+<use x='129.523' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -47.293 248.963)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 11.376 247.085)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 70.045 249.339)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 128.714 249.089)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 187.383 249.527)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 246.052 251.093)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-57' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -124.589 311.062)'>
+<use x='114.487' xlink:href='#g0-82' y='183.949'/>
+<use x='120.457' xlink:href='#g0-101' y='183.949'/>
+<use x='124.553' xlink:href='#g0-108' y='183.949'/>
+<use x='126.753' xlink:href='#g0-97' y='183.949'/>
+<use x='131.181' xlink:href='#g0-116' y='183.949'/>
+<use x='134.509' xlink:href='#g0-105' y='183.949'/>
+<use x='136.709' xlink:href='#g0-118' y='183.949'/>
+<use x='140.957' xlink:href='#g0-101' y='183.949'/>
+<use x='148.124' xlink:href='#g0-116' y='183.949'/>
+<use x='151.452' xlink:href='#g0-105' y='183.949'/>
+<use x='153.652' xlink:href='#g0-109' y='183.949'/>
+<use x='160.972' xlink:href='#g0-101' y='183.949'/>
+<use x='168.139' xlink:href='#g2-40' y='183.949'/>
+<use x='171.432' xlink:href='#g2-108' y='183.949'/>
+<use x='173.453' xlink:href='#g2-111' y='183.949'/>
+<use x='177.452' xlink:href='#g2-119' y='183.949'/>
+<use x='183' xlink:href='#g2-101' y='183.949'/>
+<use x='186.764' xlink:href='#g2-114' y='183.949'/>
+<use x='192.479' xlink:href='#g2-105' y='183.949'/>
+<use x='194.499' xlink:href='#g2-115' y='183.949'/>
+<use x='200.568' xlink:href='#g2-98' y='183.949'/>
+<use x='205.176' xlink:href='#g2-101' y='183.949'/>
+<use x='208.94' xlink:href='#g2-116' y='183.949'/>
+<use x='211.998' xlink:href='#g2-116' y='183.949'/>
+<use x='215.056' xlink:href='#g2-101' y='183.949'/>
+<use x='218.819' xlink:href='#g2-114' y='183.949'/>
+<use x='221.711' xlink:href='#g2-41' y='183.949'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='243.704pt' version='1.1' viewBox='106.737 54.995 381.623 243.704' width='381.623pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip4'>
+<path d='M135.949 249.281H487.961V81.515H135.949Z'/>
+</clipPath>
+<use id='g2-40' transform='scale(1.6)' xlink:href='#g1-40'/>
+<use id='g2-41' transform='scale(1.6)' xlink:href='#g1-41'/>
+<use id='g2-45' transform='scale(1.6)' xlink:href='#g1-45'/>
+<use id='g2-49' transform='scale(1.6)' xlink:href='#g1-49'/>
+<use id='g2-54' transform='scale(1.6)' xlink:href='#g1-54'/>
+<use id='g2-56' transform='scale(1.6)' xlink:href='#g1-56'/>
+<use id='g2-78' transform='scale(1.6)' xlink:href='#g1-78'/>
+<use id='g2-97' transform='scale(1.6)' xlink:href='#g1-97'/>
+<use id='g2-98' transform='scale(1.6)' xlink:href='#g1-98'/>
+<use id='g2-99' transform='scale(1.6)' xlink:href='#g1-99'/>
+<use id='g2-100' transform='scale(1.6)' xlink:href='#g1-100'/>
+<use id='g2-101' transform='scale(1.6)' xlink:href='#g1-101'/>
+<use id='g2-103' transform='scale(1.6)' xlink:href='#g1-103'/>
+<use id='g2-104' transform='scale(1.6)' xlink:href='#g1-104'/>
+<use id='g2-105' transform='scale(1.6)' xlink:href='#g1-105'/>
+<use id='g2-106' transform='scale(1.6)' xlink:href='#g1-106'/>
+<use id='g2-108' transform='scale(1.6)' xlink:href='#g1-108'/>
+<use id='g2-109' transform='scale(1.6)' xlink:href='#g1-109'/>
+<use id='g2-110' transform='scale(1.6)' xlink:href='#g1-110'/>
+<use id='g2-111' transform='scale(1.6)' xlink:href='#g1-111'/>
+<use id='g2-112' transform='scale(1.6)' xlink:href='#g1-112'/>
+<use id='g2-114' transform='scale(1.6)' xlink:href='#g1-114'/>
+<use id='g2-115' transform='scale(1.6)' xlink:href='#g1-115'/>
+<use id='g2-116' transform='scale(1.6)' xlink:href='#g1-116'/>
+<use id='g2-119' transform='scale(1.6)' xlink:href='#g1-119'/>
+<use id='g2-120' transform='scale(1.6)' xlink:href='#g1-120'/>
+<path d='M1.519 -3.736C1.435 -3.736 1.425 -3.736 1.365 -3.681C0.737 -3.133 0.418 -2.247 0.418 -1.245C0.418 -0.304 0.702 0.603 1.36 1.181C1.425 1.245 1.435 1.245 1.519 1.245H1.758C1.743 1.235 1.26 0.822 1.031 0.045C0.912 -0.344 0.852 -0.752 0.852 -1.245C0.852 -2.969 1.659 -3.651 1.758 -3.736H1.519Z' id='g1-40'/>
+<path d='M0.533 1.245C0.618 1.245 0.628 1.245 0.687 1.191C1.315 0.643 1.634 -0.244 1.634 -1.245C1.634 -2.187 1.35 -3.093 0.692 -3.671C0.628 -3.736 0.618 -3.736 0.533 -3.736H0.294C0.309 -3.726 0.792 -3.313 1.021 -2.535C1.141 -2.147 1.2 -1.738 1.2 -1.245C1.2 0.478 0.394 1.161 0.294 1.245H0.533Z' id='g1-41'/>
+<path d='M0.956 -0.005V-0.448H0.508V0H0.648L0.503 0.638H0.727L0.956 -0.005Z' id='g1-44'/>
+<path d='M1.465 -0.951V-1.265H0.06V-0.951H1.465Z' id='g1-45'/>
+<path d='M0.956 -0.448H0.508V0H0.956V-0.448Z' id='g1-46'/>
+<path d='M2.431 -1.619C2.431 -1.858 2.431 -2.441 2.197 -2.849C1.943 -3.298 1.559 -3.372 1.32 -3.372C1.096 -3.372 0.707 -3.303 0.458 -2.874C0.219 -2.476 0.209 -1.933 0.209 -1.619C0.209 -1.25 0.229 -0.797 0.438 -0.418C0.658 -0.015 1.026 0.105 1.32 0.105C1.818 0.105 2.092 -0.184 2.242 -0.498C2.416 -0.852 2.431 -1.31 2.431 -1.619ZM1.32 -0.224C1.111 -0.224 0.872 -0.344 0.747 -0.702C0.648 -1.006 0.643 -1.32 0.643 -1.684C0.643 -2.142 0.643 -3.044 1.32 -3.044S1.998 -2.142 1.998 -1.684C1.998 -1.355 1.998 -0.981 1.878 -0.663C1.738 -0.304 1.484 -0.224 1.32 -0.224Z' id='g1-48'/>
+<path d='M1.599 -3.372H1.489C1.166 -3.073 0.757 -3.054 0.458 -3.044V-2.73C0.653 -2.735 0.902 -2.745 1.151 -2.844V-0.314H0.488V0H2.262V-0.314H1.599V-3.372Z' id='g1-49'/>
+<path d='M1.41 -0.384C1.35 -0.384 1.29 -0.379 1.23 -0.379H0.663L1.435 -1.061C1.524 -1.141 1.768 -1.325 1.863 -1.405C2.082 -1.604 2.376 -1.863 2.376 -2.296C2.376 -2.859 1.958 -3.372 1.245 -3.372C0.717 -3.372 0.389 -3.088 0.219 -2.58L0.453 -2.286C0.568 -2.705 0.742 -3.029 1.176 -3.029C1.594 -3.029 1.913 -2.735 1.913 -2.286C1.913 -1.873 1.669 -1.639 1.37 -1.355C1.27 -1.255 1.001 -1.031 0.897 -0.932C0.752 -0.802 0.408 -0.468 0.264 -0.344V0H2.376V-0.384H1.41Z' id='g1-50'/>
+<path d='M0.498 -2.555C0.702 -2.954 1.061 -3.064 1.3 -3.064C1.594 -3.064 1.813 -2.894 1.813 -2.61C1.813 -2.346 1.634 -2.022 1.255 -1.958C1.23 -1.953 1.21 -1.953 0.882 -1.928V-1.599H1.27C1.743 -1.599 1.918 -1.225 1.918 -0.912C1.918 -0.523 1.679 -0.224 1.29 -0.224C0.936 -0.224 0.533 -0.394 0.284 -0.712L0.219 -0.389C0.508 -0.065 0.912 0.105 1.3 0.105C1.953 0.105 2.421 -0.384 2.421 -0.907C2.421 -1.315 2.092 -1.644 1.699 -1.758C2.077 -1.953 2.271 -2.286 2.271 -2.61C2.271 -3.034 1.838 -3.372 1.305 -3.372C0.867 -3.372 0.503 -3.143 0.294 -2.844L0.498 -2.555Z' id='g1-51'/>
+<path d='M1.973 -0.832H2.491V-1.161H1.973V-3.268H1.479L0.149 -1.161V-0.832H1.544V0H1.973V-0.832ZM0.573 -1.161C0.722 -1.395 1.579 -2.725 1.579 -3.024V-1.161H0.573Z' id='g1-52'/>
+<path d='M0.817 -2.924H2.197V-3.268H0.418V-1.405H0.782C0.902 -1.674 1.136 -1.793 1.36 -1.793C1.564 -1.793 1.873 -1.654 1.873 -1.021C1.873 -0.369 1.46 -0.224 1.205 -0.224C0.877 -0.224 0.558 -0.399 0.389 -0.682L0.199 -0.384C0.443 -0.08 0.812 0.105 1.205 0.105C1.863 0.105 2.376 -0.403 2.376 -1.011C2.376 -1.629 1.918 -2.122 1.37 -2.122C1.156 -2.122 0.966 -2.047 0.817 -1.923V-2.924Z' id='g1-53'/>
+<path d='M2.187 -3.273C1.918 -3.372 1.729 -3.372 1.634 -3.372C0.877 -3.372 0.219 -2.66 0.219 -1.609C0.219 -0.259 0.827 0.105 1.33 0.105C1.733 0.105 1.943 -0.085 2.097 -0.244C2.416 -0.583 2.421 -0.936 2.421 -1.111C2.421 -1.763 2.067 -2.306 1.584 -2.306C1.096 -2.306 0.832 -2.052 0.687 -1.908C0.752 -2.59 1.101 -3.064 1.639 -3.064C1.738 -3.064 1.938 -3.054 2.187 -2.959V-3.273ZM0.692 -1.096C0.692 -1.126 0.692 -1.2 0.697 -1.225C0.697 -1.564 0.912 -1.978 1.355 -1.978C1.963 -1.978 1.963 -1.28 1.963 -1.111C1.963 -0.922 1.963 -0.712 1.828 -0.503C1.709 -0.324 1.559 -0.224 1.33 -0.224C0.802 -0.224 0.717 -0.877 0.692 -1.096Z' id='g1-54'/>
+<path d='M1.23 -2.884C1.29 -2.884 1.35 -2.889 1.41 -2.889H2.037C1.28 -2.147 0.797 -1.106 0.797 0.05H1.265C1.265 -1.405 1.973 -2.451 2.421 -2.919V-3.268H0.219V-2.884H1.23Z' id='g1-55'/>
+<path d='M1.704 -1.763C2.032 -1.868 2.346 -2.132 2.346 -2.501C2.346 -2.954 1.913 -3.372 1.32 -3.372S0.294 -2.954 0.294 -2.501C0.294 -2.127 0.618 -1.863 0.936 -1.763C0.498 -1.629 0.219 -1.29 0.219 -0.907C0.219 -0.374 0.692 0.105 1.32 0.105S2.421 -0.374 2.421 -0.907C2.421 -1.29 2.137 -1.629 1.704 -1.763ZM1.32 -1.928C0.966 -1.928 0.677 -2.132 0.677 -2.496C0.677 -2.814 0.902 -3.064 1.32 -3.064C1.733 -3.064 1.963 -2.814 1.963 -2.496C1.963 -2.142 1.684 -1.928 1.32 -1.928ZM1.32 -0.224C0.976 -0.224 0.672 -0.443 0.672 -0.912C0.672 -1.36 0.961 -1.599 1.32 -1.599S1.968 -1.355 1.968 -0.912C1.968 -0.443 1.659 -0.224 1.32 -0.224Z' id='g1-56'/>
+<path d='M0.384 -0.125C0.628 0.055 0.852 0.105 1.086 0.105C1.783 0.105 2.421 -0.598 2.421 -1.669C2.421 -3.029 1.818 -3.372 1.34 -3.372C0.897 -3.372 0.692 -3.163 0.548 -3.019C0.229 -2.695 0.219 -2.351 0.219 -2.157C0.219 -1.514 0.568 -0.961 1.056 -0.961C1.619 -0.961 1.928 -1.335 1.953 -1.365C1.883 -0.573 1.494 -0.224 1.086 -0.224C0.827 -0.224 0.667 -0.319 0.553 -0.413L0.384 -0.125ZM1.938 -2.162C1.943 -2.132 1.943 -2.082 1.943 -2.052C1.943 -1.684 1.719 -1.29 1.285 -1.29C1.096 -1.29 0.946 -1.345 0.817 -1.549C0.687 -1.743 0.677 -1.933 0.677 -2.157C0.677 -2.351 0.677 -2.575 0.832 -2.795C0.936 -2.944 1.086 -3.064 1.335 -3.064C1.818 -3.064 1.923 -2.481 1.938 -2.162Z' id='g1-57'/>
+<path d='M2.959 -0.438C2.884 -0.438 2.874 -0.438 2.834 -0.418C2.59 -0.334 2.336 -0.279 2.072 -0.279C1.27 -0.279 0.697 -0.956 0.697 -1.729C0.697 -2.565 1.345 -3.178 2.042 -3.178C2.182 -3.178 2.511 -3.143 2.675 -2.745C2.535 -2.824 2.381 -2.859 2.252 -2.859C1.719 -2.859 1.27 -2.361 1.27 -1.729C1.27 -1.081 1.733 -0.598 2.247 -0.598C2.635 -0.598 3.228 -0.912 3.228 -1.798C3.228 -2.301 3.193 -3.507 2.047 -3.507C1.101 -3.507 0.294 -2.725 0.294 -1.729C0.294 -0.742 1.091 0.05 2.052 0.05C2.511 0.05 2.939 -0.139 3.228 -0.438H2.959ZM2.252 -0.927C1.943 -0.927 1.674 -1.27 1.674 -1.729C1.674 -2.202 1.953 -2.531 2.247 -2.531C2.555 -2.531 2.824 -2.187 2.824 -1.729C2.824 -1.255 2.545 -0.927 2.252 -0.927Z' id='g1-64'/>
+<path d='M3.083 -0.608C2.735 -0.394 2.575 -0.299 2.077 -0.299C1.305 -0.299 0.837 -1.021 0.837 -1.738C0.837 -2.476 1.35 -3.168 2.077 -3.168C2.406 -3.168 2.745 -3.064 2.974 -2.889L3.054 -3.342C2.705 -3.472 2.426 -3.512 2.062 -3.512C1.076 -3.512 0.339 -2.695 0.339 -1.733C0.339 -0.707 1.121 0.05 2.092 0.05C2.58 0.05 2.785 -0.05 3.113 -0.229L3.083 -0.608Z' id='g1-67'/>
+<path d='M2.725 -1.624V-1.953H0.986V-3.098H1.714C1.773 -3.098 1.833 -3.093 1.893 -3.093H2.874V-3.442H0.483V0H2.949V-0.389H2.501C2.082 -0.389 1.664 -0.379 1.245 -0.379H0.986V-1.624H2.725Z' id='g1-69'/>
+<path d='M3.173 -1.489H2.057V-1.161H2.735V-0.399C2.516 -0.344 2.301 -0.299 2.077 -0.299C1.31 -0.299 0.837 -1.021 0.837 -1.733C0.837 -2.416 1.3 -3.168 2.052 -3.168C2.511 -3.168 2.8 -3.029 3.049 -2.819L3.128 -3.273C2.785 -3.437 2.481 -3.517 2.102 -3.517C1.096 -3.517 0.339 -2.73 0.339 -1.733C0.339 -0.762 1.091 0.05 2.072 0.05C2.431 0.05 2.854 -0.03 3.173 -0.194V-1.489Z' id='g1-71'/>
+<path d='M3.248 -3.457H2.745V-1.963H0.986V-3.457H0.483V0H0.986V-1.634H2.745V0H3.248V-3.457Z' id='g1-72'/>
+<path d='M0.986 -3.457H0.483V0H0.986V-3.457Z' id='g1-73'/>
+<path d='M1.176 -3.457H0.498V0H0.917V-3.064H0.922L2.555 0H3.233V-3.457H2.814V-0.394H2.809L1.176 -3.457Z' id='g1-78'/>
+<path d='M3.611 -1.714C3.611 -2.745 2.854 -3.562 1.953 -3.562S0.294 -2.745 0.294 -1.714S1.061 0.105 1.953 0.105C2.849 0.105 3.611 -0.687 3.611 -1.714ZM1.953 -0.249C1.35 -0.249 0.797 -0.852 0.797 -1.793C0.797 -2.675 1.355 -3.218 1.953 -3.218S3.108 -2.675 3.108 -1.793C3.108 -0.847 2.555 -0.249 1.953 -0.249Z' id='g1-79'/>
+<path d='M1.868 -1.42C2.511 -1.42 3.083 -1.873 3.083 -2.446C3.083 -2.979 2.555 -3.457 1.833 -3.457H0.488V0H0.991V-1.42H1.868ZM1.709 -3.163C2.271 -3.163 2.63 -2.864 2.63 -2.446C2.63 -2.037 2.291 -1.729 1.709 -1.729H0.976V-3.163H1.709Z' id='g1-80'/>
+<path d='M3.143 -3.457H2.71V-1.161C2.71 -0.493 2.262 -0.189 1.833 -0.189S0.986 -0.498 0.986 -1.156V-3.457H0.483V-1.166C0.483 -0.433 1.111 0.105 1.828 0.105C2.54 0.105 3.143 -0.438 3.143 -1.166V-3.457Z' id='g1-85'/>
+<path d='M1.968 -1.823L3.228 -3.457H2.685L1.724 -2.182L0.742 -3.457H0.149L1.479 -1.823L0.075 0H0.618L1.724 -1.499L2.854 0H3.447L1.968 -1.823Z' id='g1-88'/>
+<path d='M2.934 -3.238V-3.457H0.369V-3.123H1.41C1.479 -3.123 1.539 -3.128 1.609 -3.128H2.291L0.294 -0.229V0H2.964V-0.354H2.466C1.958 -0.354 1.45 -0.344 0.941 -0.344L2.934 -3.238Z' id='g1-90'/>
+<path d='M2.122 -1.435C2.122 -1.943 1.733 -2.286 1.24 -2.286C0.927 -2.286 0.687 -2.222 0.408 -2.072L0.438 -1.709C0.603 -1.818 0.847 -1.968 1.24 -1.968C1.46 -1.968 1.689 -1.803 1.689 -1.43V-1.23C0.951 -1.205 0.224 -1.051 0.224 -0.588C0.224 -0.339 0.394 0.05 0.832 0.05C1.046 0.05 1.44 0.005 1.704 -0.189V0H2.122V-1.435ZM1.689 -0.707C1.689 -0.608 1.689 -0.478 1.514 -0.374C1.355 -0.284 1.161 -0.279 1.106 -0.279C0.832 -0.279 0.623 -0.403 0.623 -0.593C0.623 -0.912 1.465 -0.941 1.689 -0.951V-0.707Z' id='g1-97'/>
+<path d='M0.842 -3.457H0.423V0H0.857V-0.234C0.966 -0.139 1.205 0.05 1.569 0.05C2.112 0.05 2.55 -0.458 2.55 -1.111C2.55 -1.714 2.207 -2.262 1.709 -2.262C1.395 -2.262 1.091 -2.162 0.842 -1.978V-3.457ZM0.857 -1.569C0.857 -1.649 0.857 -1.709 1.031 -1.823C1.106 -1.868 1.24 -1.933 1.41 -1.933C1.743 -1.933 2.117 -1.709 2.117 -1.111C2.117 -0.503 1.704 -0.279 1.355 -0.279C1.171 -0.279 0.996 -0.364 0.857 -0.588V-1.569Z' id='g1-98'/>
+<path d='M2.167 -0.543C1.918 -0.384 1.649 -0.294 1.34 -0.294C0.882 -0.294 0.613 -0.663 0.613 -1.111C0.613 -1.494 0.812 -1.943 1.355 -1.943C1.694 -1.943 1.853 -1.873 2.107 -1.714L2.172 -2.072C1.873 -2.222 1.743 -2.286 1.355 -2.286C0.608 -2.286 0.179 -1.684 0.179 -1.106C0.179 -0.498 0.658 0.05 1.335 0.05C1.684 0.05 1.983 -0.055 2.197 -0.179L2.167 -0.543Z' id='g1-99'/>
+<path d='M2.306 -3.457H1.888V-1.998C1.569 -2.232 1.245 -2.262 1.101 -2.262C0.578 -2.262 0.179 -1.738 0.179 -1.106S0.573 0.05 1.086 0.05C1.395 0.05 1.684 -0.09 1.873 -0.259V0H2.306V-3.457ZM1.873 -0.618C1.748 -0.413 1.559 -0.279 1.32 -0.279C0.971 -0.279 0.613 -0.523 0.613 -1.101C0.613 -1.724 1.036 -1.933 1.375 -1.933C1.574 -1.933 1.743 -1.848 1.873 -1.679V-0.618Z' id='g1-100'/>
+<path d='M2.142 -0.543C1.863 -0.344 1.549 -0.279 1.335 -0.279C0.902 -0.279 0.573 -0.633 0.558 -1.091H2.192C2.192 -1.32 2.167 -1.654 1.973 -1.938C1.793 -2.192 1.494 -2.286 1.25 -2.286C0.643 -2.286 0.174 -1.753 0.174 -1.121C0.174 -0.483 0.672 0.05 1.33 0.05C1.619 0.05 1.918 -0.035 2.172 -0.189L2.142 -0.543ZM0.593 -1.39C0.707 -1.788 1.001 -1.958 1.25 -1.958C1.469 -1.958 1.793 -1.853 1.888 -1.39H0.593Z' id='g1-101'/>
+<path d='M2.506 -2.262C2.396 -2.262 2.062 -2.257 1.684 -2.112L1.674 -2.107C1.494 -2.227 1.32 -2.262 1.176 -2.262C0.687 -2.262 0.324 -1.878 0.324 -1.45C0.324 -1.275 0.384 -1.096 0.498 -0.956C0.428 -0.872 0.354 -0.732 0.354 -0.543C0.354 -0.349 0.433 -0.224 0.478 -0.164C0.204 -0.005 0.149 0.224 0.149 0.344C0.149 0.722 0.672 1.021 1.32 1.021C1.973 1.021 2.491 0.722 2.491 0.344C2.491 -0.359 1.619 -0.359 1.405 -0.359H0.941C0.862 -0.359 0.648 -0.359 0.648 -0.618C0.648 -0.717 0.682 -0.767 0.687 -0.777C0.862 -0.667 1.036 -0.633 1.171 -0.633C1.659 -0.633 2.022 -1.016 2.022 -1.445C2.022 -1.604 1.978 -1.748 1.888 -1.883C1.868 -1.913 1.868 -1.918 1.868 -1.923C1.868 -1.943 2.167 -1.943 2.192 -1.943C2.197 -1.943 2.386 -1.943 2.565 -1.923L2.506 -2.262ZM1.176 -0.941C0.907 -0.941 0.707 -1.111 0.707 -1.445C0.707 -1.833 0.956 -1.953 1.171 -1.953C1.44 -1.953 1.639 -1.783 1.639 -1.45C1.639 -1.061 1.39 -0.941 1.176 -0.941ZM1.41 0.03C1.524 0.03 2.112 0.03 2.112 0.349C2.112 0.563 1.738 0.712 1.32 0.712S0.528 0.563 0.528 0.349C0.528 0.324 0.528 0.03 0.932 0.03H1.41Z' id='g1-103'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.161 -2.262 0.932 -2.012 0.832 -1.908V-3.457H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-104'/>
+<path d='M0.877 -3.417H0.374V-2.914H0.877V-3.417ZM0.837 -2.212H0.418V0H0.837V-2.212Z' id='g1-105'/>
+<path d='M0.986 -3.417H0.483V-2.914H0.986V-3.417ZM-0.324 0.847C-0.095 0.971 0.13 1.016 0.319 1.016C0.663 1.016 0.986 0.752 0.986 0.294V-2.212H0.568V0.329C0.568 0.418 0.568 0.498 0.463 0.583C0.349 0.667 0.209 0.667 0.164 0.667C-0.045 0.667 -0.174 0.573 -0.234 0.518L-0.324 0.847Z' id='g1-106'/>
+<path d='M0.837 -3.457H0.418V0H0.837V-3.457Z' id='g1-108'/>
+<path d='M3.786 -1.474C3.786 -1.863 3.671 -2.262 3.059 -2.262C2.64 -2.262 2.381 -2.017 2.262 -1.858C2.212 -1.993 2.087 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.21C2.316 -1.539 2.456 -1.933 2.839 -1.933C3.352 -1.933 3.352 -1.584 3.352 -1.44V0H3.786V-1.474Z' id='g1-109'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-110'/>
+<path d='M2.491 -1.091C2.491 -1.748 1.968 -2.286 1.32 -2.286S0.149 -1.743 0.149 -1.091C0.149 -0.458 0.677 0.05 1.32 0.05C1.968 0.05 2.491 -0.458 2.491 -1.091ZM1.32 -0.294C0.927 -0.294 0.583 -0.583 0.583 -1.146S0.951 -1.958 1.32 -1.958C1.694 -1.958 2.057 -1.699 2.057 -1.146C2.057 -0.578 1.704 -0.294 1.32 -0.294Z' id='g1-111'/>
+<path d='M0.857 -0.234C1.121 0.005 1.405 0.05 1.574 0.05C2.102 0.05 2.55 -0.453 2.55 -1.111C2.55 -1.709 2.222 -2.262 1.729 -2.262C1.504 -2.262 1.131 -2.197 0.842 -1.973V-2.212H0.423V0.966H0.857V-0.234ZM0.857 -1.654C0.971 -1.793 1.166 -1.918 1.405 -1.918C1.803 -1.918 2.117 -1.549 2.117 -1.111C2.117 -0.618 1.743 -0.279 1.355 -0.279C1.28 -0.279 1.156 -0.289 1.026 -0.394C0.877 -0.508 0.857 -0.583 0.857 -0.677V-1.654Z' id='g1-112'/>
+<path d='M0.842 -1.061C0.842 -1.599 1.29 -1.888 1.729 -1.893V-2.262C1.31 -2.257 1.006 -2.052 0.807 -1.788V-2.247H0.423V0H0.842V-1.061Z' id='g1-114'/>
+<path d='M1.818 -2.132C1.479 -2.271 1.23 -2.286 1.051 -2.286C0.927 -2.286 0.174 -2.286 0.174 -1.624C0.174 -1.39 0.304 -1.26 0.369 -1.2C0.543 -1.026 0.752 -0.986 1.016 -0.936C1.25 -0.892 1.519 -0.842 1.519 -0.603C1.519 -0.289 1.106 -0.289 1.036 -0.289C0.717 -0.289 0.418 -0.403 0.219 -0.543L0.149 -0.169C0.319 -0.085 0.623 0.05 1.036 0.05C1.26 0.05 1.479 0.015 1.664 -0.12C1.848 -0.259 1.908 -0.478 1.908 -0.648C1.908 -0.737 1.898 -0.932 1.689 -1.121C1.504 -1.285 1.325 -1.32 1.086 -1.365C0.792 -1.42 0.563 -1.465 0.563 -1.684C0.563 -1.968 0.927 -1.968 1.001 -1.968C1.285 -1.968 1.504 -1.908 1.753 -1.778L1.818 -2.132Z' id='g1-115'/>
+<path d='M0.936 -1.898H1.674V-2.212H0.936V-2.844H0.553V-2.212H0.1V-1.898H0.538V-0.638C0.538 -0.304 0.623 0.05 0.981 0.05S1.614 -0.065 1.763 -0.134L1.679 -0.453C1.514 -0.334 1.34 -0.294 1.2 -0.294C0.991 -0.294 0.936 -0.498 0.936 -0.727V-1.898Z' id='g1-116'/>
+<path d='M2.316 -2.212H1.883V-0.767C1.883 -0.369 1.544 -0.244 1.255 -0.244C0.887 -0.244 0.847 -0.344 0.847 -0.573V-2.212H0.413V-0.543C0.413 -0.1 0.608 0.05 0.956 0.05C1.161 0.05 1.599 0.01 1.898 -0.229V0H2.316V-2.212Z' id='g1-117'/>
+<path d='M3.537 -2.212H3.148C3.103 -2.072 2.824 -1.23 2.67 -0.712C2.63 -0.568 2.58 -0.408 2.565 -0.294H2.56C2.531 -0.498 2.356 -1.036 2.346 -1.071L1.978 -2.212H1.599C1.455 -1.783 1.081 -0.667 1.041 -0.304H1.036C0.996 -0.658 0.628 -1.758 0.548 -1.998C0.508 -2.117 0.508 -2.127 0.483 -2.212H0.075L0.802 0H1.22C1.225 -0.02 1.36 -0.413 1.534 -0.966C1.609 -1.21 1.758 -1.689 1.783 -1.908L1.788 -1.913C1.798 -1.808 1.828 -1.699 1.863 -1.574S1.933 -1.315 1.968 -1.2L2.351 0H2.809L3.537 -2.212Z' id='g1-119'/>
+<path d='M1.38 -1.141L2.346 -2.212H1.908L1.2 -1.395L0.478 -2.212H0.03L1.026 -1.141L0 0H0.443L1.2 -0.936L1.988 0H2.436L1.38 -1.141Z' id='g1-120'/>
+<path d='M2.112 -2.002V-2.212H0.219V-1.893H0.951C1.011 -1.893 1.071 -1.898 1.131 -1.898H1.519L0.149 -0.219V0H2.127V-0.334H1.355C1.295 -0.334 1.235 -0.329 1.176 -0.329H0.742L2.112 -2.002Z' id='g1-122'/>
+<path d='M1.445 -1.245C1.445 -1.41 1.305 -1.549 1.141 -1.549S0.837 -1.41 0.837 -1.245S0.976 -0.941 1.141 -0.941S1.445 -1.081 1.445 -1.245Z' id='g3-1'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g0-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g0-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g0-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g0-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g0-108'/>
+<path d='M6.581 -2.663C6.581 -3.327 6.402 -4.08 5.317 -4.08C4.564 -4.08 4.142 -3.622 3.927 -3.344C3.865 -3.524 3.676 -4.08 2.762 -4.08C2.053 -4.08 1.623 -3.667 1.417 -3.398V-4.035H0.726V0H1.47V-2.188C1.47 -2.78 1.704 -3.497 2.385 -3.497C3.282 -3.497 3.282 -2.86 3.282 -2.6V0H4.026V-2.188C4.026 -2.78 4.259 -3.497 4.94 -3.497C5.837 -3.497 5.837 -2.86 5.837 -2.6V0H6.581V-2.663Z' id='g0-109'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g0-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g0-118'/>
+</defs>
+<g id='page4'>
+<path d='M194.617 258.136V249.281M253.285 258.136V249.281M311.953 258.136V249.281M370.625 258.136V249.281M429.293 258.136V249.281M194.617 72.66V81.515M253.285 72.66V81.515M311.953 72.66V81.515M370.625 72.66V81.515M429.293 72.66V81.515' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M165.281 253.535V249.281M223.953 253.535V249.281M282.621 253.535V249.281M341.289 253.535V249.281M399.957 253.535V249.281M458.629 253.535V249.281M165.281 77.265V81.515M223.953 77.265V81.515M282.621 77.265V81.515M341.289 77.265V81.515M399.957 77.265V81.515M458.629 77.265V81.515' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 249.281H140.199M135.949 215.73H140.199M135.949 182.176H140.199M135.949 148.625H140.199M135.949 115.07H140.199M135.949 81.515H140.199M487.961 249.281H483.711M487.961 215.73H483.711M487.961 182.176H483.711M487.961 148.625H483.711M487.961 115.07H483.711M487.961 81.515H483.711' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 249.281V81.515H487.961V249.281H135.949Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -21.265 61.588)'>
+<use x='168.285' xlink:href='#g2-97' y='201.694'/>
+<use x='172.353' xlink:href='#g2-108' y='201.694'/>
+<use x='174.373' xlink:href='#g2-108' y='201.694'/>
+<use x='176.393' xlink:href='#g2-111' y='201.694'/>
+<use x='180.863' xlink:href='#g2-99' y='201.694'/>
+<use x='184.627' xlink:href='#g2-45' y='201.694'/>
+<use x='187.449' xlink:href='#g2-116' y='201.694'/>
+<use x='190.507' xlink:href='#g2-101' y='201.694'/>
+<use x='194.271' xlink:href='#g2-115' y='201.694'/>
+<use x='197.517' xlink:href='#g2-116' y='201.694'/>
+<use x='200.575' xlink:href='#g2-49' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 36.532 61.588)'>
+<use x='168.285' xlink:href='#g2-97' y='201.694'/>
+<use x='172.353' xlink:href='#g2-108' y='201.694'/>
+<use x='174.373' xlink:href='#g2-108' y='201.694'/>
+<use x='176.393' xlink:href='#g2-111' y='201.694'/>
+<use x='180.863' xlink:href='#g2-99' y='201.694'/>
+<use x='184.627' xlink:href='#g2-45' y='201.694'/>
+<use x='187.449' xlink:href='#g2-116' y='201.694'/>
+<use x='190.507' xlink:href='#g2-101' y='201.694'/>
+<use x='194.271' xlink:href='#g2-115' y='201.694'/>
+<use x='197.517' xlink:href='#g2-116' y='201.694'/>
+<use x='200.575' xlink:href='#g2-78' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 94.98 61.588)'>
+<use x='168.285' xlink:href='#g2-115' y='201.694'/>
+<use x='171.531' xlink:href='#g2-104' y='201.694'/>
+<use x='175.904' xlink:href='#g2-54' y='201.694'/>
+<use x='180.138' xlink:href='#g2-98' y='201.694'/>
+<use x='184.746' xlink:href='#g2-101' y='201.694'/>
+<use x='188.509' xlink:href='#g2-110' y='201.694'/>
+<use x='192.882' xlink:href='#g2-99' y='201.694'/>
+<use x='196.646' xlink:href='#g2-104' y='201.694'/>
+<use x='201.018' xlink:href='#g2-78' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 153.649 61.588)'>
+<use x='168.285' xlink:href='#g2-115' y='201.694'/>
+<use x='171.531' xlink:href='#g2-104' y='201.694'/>
+<use x='175.904' xlink:href='#g2-56' y='201.694'/>
+<use x='180.138' xlink:href='#g2-98' y='201.694'/>
+<use x='184.746' xlink:href='#g2-101' y='201.694'/>
+<use x='188.509' xlink:href='#g2-110' y='201.694'/>
+<use x='192.882' xlink:href='#g2-99' y='201.694'/>
+<use x='196.646' xlink:href='#g2-104' y='201.694'/>
+<use x='201.018' xlink:href='#g2-78' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 207.225 61.588)'>
+<use x='168.285' xlink:href='#g2-120' y='201.694'/>
+<use x='172.187' xlink:href='#g2-109' y='201.694'/>
+<use x='178.912' xlink:href='#g2-97' y='201.694'/>
+<use x='182.98' xlink:href='#g2-108' y='201.694'/>
+<use x='185' xlink:href='#g2-108' y='201.694'/>
+<use x='187.02' xlink:href='#g2-111' y='201.694'/>
+<use x='191.49' xlink:href='#g2-99' y='201.694'/>
+<use x='195.254' xlink:href='#g2-45' y='201.694'/>
+<use x='198.076' xlink:href='#g2-116' y='201.694'/>
+<use x='201.134' xlink:href='#g2-101' y='201.694'/>
+<use x='204.898' xlink:href='#g2-115' y='201.694'/>
+<use x='208.144' xlink:href='#g2-116' y='201.694'/>
+<use x='211.202' xlink:href='#g2-78' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 263.494 61.588)'>
+<use x='168.285' xlink:href='#g2-99' y='201.694'/>
+<use x='172.049' xlink:href='#g2-97' y='201.694'/>
+<use x='176.117' xlink:href='#g2-99' y='201.694'/>
+<use x='179.88' xlink:href='#g2-104' y='201.694'/>
+<use x='184.253' xlink:href='#g2-101' y='201.694'/>
+<use x='188.017' xlink:href='#g2-45' y='201.694'/>
+<use x='190.839' xlink:href='#g2-115' y='201.694'/>
+<use x='194.086' xlink:href='#g2-99' y='201.694'/>
+<use x='197.849' xlink:href='#g2-114' y='201.694'/>
+<use x='200.741' xlink:href='#g2-97' y='201.694'/>
+<use x='204.81' xlink:href='#g2-116' y='201.694'/>
+<use x='207.868' xlink:href='#g2-99' y='201.694'/>
+<use x='211.631' xlink:href='#g2-104' y='201.694'/>
+<use x='216.004' xlink:href='#g2-78' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 49.223)'>
+<use x='168.285' xlink:href='#g1-48' y='201.694'/>
+<use x='170.931' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 15.67)'>
+<use x='168.285' xlink:href='#g1-48' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-53' y='201.694'/>
+<use x='175.048' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -17.883)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -51.436)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-53' y='201.694'/>
+<use x='175.048' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -84.989)'>
+<use x='168.285' xlink:href='#g1-50' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -118.543)'>
+<use x='168.285' xlink:href='#g1-50' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-53' y='201.694'/>
+<use x='175.048' xlink:href='#g1-120' y='201.694'/>
+</g>
+<path clip-path='url(#clip4)' d='M135.949 182.176H487.961' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M140.125 249.281H143.363V182.176H140.125ZM198.797 249.281H202.035V182.176H198.797ZM257.465 249.281H260.703V182.176H257.465ZM316.133 249.281H319.371V182.176H316.133ZM374.801 249.281H378.039V182.176H374.801ZM433.473 249.281H436.711V182.176H433.473Z' fill='#e0e0f0'/>
+<path clip-path='url(#clip4)' d='M140.125 249.281H143.363V182.176H140.125ZM198.797 249.281H202.035V182.176H198.797ZM257.465 249.281H260.703V182.176H257.465ZM316.133 249.281H319.371V182.176H316.133ZM374.801 249.281H378.039V182.176H374.801ZM433.473 249.281H436.711V182.176H433.473Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M141.746 182.176V181.906' fill='#e0e0f0'/>
+<path clip-path='url(#clip4)' d='M141.746 182.176V181.906' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M139.754 181.907H143.738' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M141.746 182.176V182.445' fill='#e0e0f0'/>
+<path clip-path='url(#clip4)' d='M141.746 182.176V182.445' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M143.739 182.446H139.754' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M200.414 182.176V181.773' fill='#e0e0f0'/>
+<path clip-path='url(#clip4)' d='M200.414 182.176V181.773' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M198.422 181.774H202.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M200.414 182.176V182.578' fill='#e0e0f0'/>
+<path clip-path='url(#clip4)' d='M200.414 182.176V182.578' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M202.406 182.578H198.422' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M259.082 182.176V180.297' fill='#e0e0f0'/>
+<path clip-path='url(#clip4)' d='M259.082 182.176V180.297' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M257.09 180.297H261.078' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M259.082 182.176V184.054' fill='#e0e0f0'/>
+<path clip-path='url(#clip4)' d='M259.082 182.176V184.054' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M261.074 184.055H257.09' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M317.754 182.176V181.57' fill='#e0e0f0'/>
+<path clip-path='url(#clip4)' d='M317.754 182.176V181.57' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M315.762 181.57H319.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M317.754 182.176V182.781' fill='#e0e0f0'/>
+<path clip-path='url(#clip4)' d='M317.754 182.176V182.781' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M319.746 182.781H315.758' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M376.422 182.176V181.035' fill='#e0e0f0'/>
+<path clip-path='url(#clip4)' d='M376.422 182.176V181.035' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M374.43 181.035H378.414' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M376.422 182.176V183.316' fill='#e0e0f0'/>
+<path clip-path='url(#clip4)' d='M376.422 182.176V183.316' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M378.414 183.316H374.43' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M435.09 182.176V179.156' fill='#e0e0f0'/>
+<path clip-path='url(#clip4)' d='M435.09 182.176V179.156' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M433.098 179.156H437.082' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M435.09 182.176V185.195' fill='#e0e0f0'/>
+<path clip-path='url(#clip4)' d='M435.09 182.176V185.195' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M437.082 185.195H433.098' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M145.356 249.281H148.594V181.371H145.356ZM204.028 249.281H207.266V179.492H204.028ZM262.695 249.281H265.934V149.898H262.695ZM321.363 249.281H324.602V81.515H321.363ZM380.031 249.281H383.27V81.515H380.031ZM438.703 249.281H441.942V81.515H438.703Z' fill='#c2c2e1'/>
+<path clip-path='url(#clip4)' d='M145.356 249.281H148.594V181.371H145.356ZM204.028 249.281H207.266V179.492H204.028ZM262.695 249.281H265.934V149.898H262.695ZM321.363 249.281H324.602V81.515H321.363ZM380.031 249.281H383.27V81.515H380.031ZM438.703 249.281H441.942V81.515H438.703Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M146.977 181.371V181.238' fill='#c2c2e1'/>
+<path clip-path='url(#clip4)' d='M146.977 181.371V181.238' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M144.984 181.239H148.968' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M146.977 181.371V181.504' fill='#c2c2e1'/>
+<path clip-path='url(#clip4)' d='M146.977 181.371V181.504' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M148.969 181.504H144.984' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M205.645 179.492V179.359' fill='#c2c2e1'/>
+<path clip-path='url(#clip4)' d='M205.645 179.492V179.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M203.652 179.36H207.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M205.645 179.492V179.625' fill='#c2c2e1'/>
+<path clip-path='url(#clip4)' d='M205.645 179.492V179.625' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M207.636 179.625H203.652' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M264.313 149.898V149.359' fill='#c2c2e1'/>
+<path clip-path='url(#clip4)' d='M264.313 149.898V149.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M262.32 149.359H266.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M264.313 149.898V150.433' fill='#c2c2e1'/>
+<path clip-path='url(#clip4)' d='M264.313 149.898V150.433' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M266.304 150.434H262.32' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M322.984 81.515V81.515' fill='#c2c2e1'/>
+<path clip-path='url(#clip4)' d='M320.992 81.516H324.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M322.984 81.515V81.515' fill='#c2c2e1'/>
+<path clip-path='url(#clip4)' d='M320.992 81.516H324.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M381.652 81.515V81.515' fill='#c2c2e1'/>
+<path clip-path='url(#clip4)' d='M379.66 81.516H383.644' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M381.652 81.515V81.515' fill='#c2c2e1'/>
+<path clip-path='url(#clip4)' d='M379.66 81.516H383.644' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M440.32 81.515V81.515' fill='#c2c2e1'/>
+<path clip-path='url(#clip4)' d='M438.328 81.516H442.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M440.32 81.515V81.515' fill='#c2c2e1'/>
+<path clip-path='url(#clip4)' d='M438.328 81.516H442.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M150.586 249.281H153.824V178.687H150.586ZM209.258 249.281H212.496V177.679H209.258ZM267.926 249.281H271.164V81.515H267.926ZM326.594 249.281H329.832V133.39H326.594ZM385.262 249.281H388.5V110.91H385.262ZM443.934 249.281H447.172V81.515H443.934Z' fill='#a3a3d1'/>
+<path clip-path='url(#clip4)' d='M150.586 249.281H153.824V178.687H150.586ZM209.258 249.281H212.496V177.679H209.258ZM267.926 249.281H271.164V81.515H267.926ZM326.594 249.281H329.832V133.39H326.594ZM385.262 249.281H388.5V110.91H385.262ZM443.934 249.281H447.172V81.515H443.934Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M152.207 178.687V178.418' fill='#a3a3d1'/>
+<path clip-path='url(#clip4)' d='M152.207 178.687V178.418' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M150.215 178.418H154.199' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M152.207 178.687V178.953' fill='#a3a3d1'/>
+<path clip-path='url(#clip4)' d='M152.207 178.687V178.953' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M154.2 178.953H150.215' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M210.875 177.679V177.547' fill='#a3a3d1'/>
+<path clip-path='url(#clip4)' d='M210.875 177.679V177.547' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M208.883 177.547H212.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M210.875 177.679V177.812' fill='#a3a3d1'/>
+<path clip-path='url(#clip4)' d='M210.875 177.679V177.812' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M212.867 177.812H208.883' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M269.543 81.515V81.515' fill='#a3a3d1'/>
+<path clip-path='url(#clip4)' d='M267.551 81.516H271.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M269.543 81.515V81.515' fill='#a3a3d1'/>
+<path clip-path='url(#clip4)' d='M267.551 81.516H271.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M328.215 133.39V131.176' fill='#a3a3d1'/>
+<path clip-path='url(#clip4)' d='M328.215 133.39V131.176' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M326.223 131.176H330.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M328.215 133.39V135.605' fill='#a3a3d1'/>
+<path clip-path='url(#clip4)' d='M328.215 133.39V135.605' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M330.207 135.606H326.219' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M386.883 110.91V110.371' fill='#a3a3d1'/>
+<path clip-path='url(#clip4)' d='M386.883 110.91V110.371' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M384.891 110.371H388.875' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M386.883 110.91V111.445' fill='#a3a3d1'/>
+<path clip-path='url(#clip4)' d='M386.883 110.91V111.445' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M388.875 111.445H384.891' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M445.551 81.515V81.515' fill='#a3a3d1'/>
+<path clip-path='url(#clip4)' d='M443.559 81.516H447.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M445.551 81.515V81.515' fill='#a3a3d1'/>
+<path clip-path='url(#clip4)' d='M443.559 81.516H447.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M155.817 249.281H159.055V161.91H155.817ZM214.488 249.281H217.727V158.219H214.488ZM273.156 249.281H276.395V81.515H273.156ZM331.824 249.281H335.063V150.097H331.824ZM390.492 249.281H393.731V162.445H390.492ZM449.164 249.281H452.402V181.101H449.164Z' fill='#8585c2'/>
+<path clip-path='url(#clip4)' d='M155.817 249.281H159.055V161.91H155.817ZM214.488 249.281H217.727V158.219H214.488ZM273.156 249.281H276.395V81.515H273.156ZM331.824 249.281H335.063V150.097H331.824ZM390.492 249.281H393.731V162.445H390.492ZM449.164 249.281H452.402V181.101H449.164Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M157.438 161.91V161.777' fill='#8585c2'/>
+<path clip-path='url(#clip4)' d='M157.438 161.91V161.777' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M155.445 161.777H159.429' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M157.438 161.91V162.043' fill='#8585c2'/>
+<path clip-path='url(#clip4)' d='M157.438 161.91V162.043' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M159.43 162.043H155.445' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M216.106 158.219V158.019' fill='#8585c2'/>
+<path clip-path='url(#clip4)' d='M216.106 158.219V158.019' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M214.113 158.02H218.097' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M216.106 158.219V158.422' fill='#8585c2'/>
+<path clip-path='url(#clip4)' d='M216.106 158.219V158.422' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M218.098 158.422H214.113' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M274.774 81.515V81.515' fill='#8585c2'/>
+<path clip-path='url(#clip4)' d='M272.781 81.516H276.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M274.774 81.515V81.515' fill='#8585c2'/>
+<path clip-path='url(#clip4)' d='M272.781 81.516H276.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M333.445 150.097V149.496' fill='#8585c2'/>
+<path clip-path='url(#clip4)' d='M333.445 150.097V149.496' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M331.453 149.496H335.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M333.445 150.097V150.703' fill='#8585c2'/>
+<path clip-path='url(#clip4)' d='M333.445 150.097V150.703' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M335.437 150.703H331.449' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M392.113 162.445V162.179' fill='#8585c2'/>
+<path clip-path='url(#clip4)' d='M392.113 162.445V162.179' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M390.121 162.18H394.105' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M392.113 162.445V162.715' fill='#8585c2'/>
+<path clip-path='url(#clip4)' d='M392.113 162.445V162.715' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M394.105 162.714H390.121' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M450.781 181.101V177.41' fill='#8585c2'/>
+<path clip-path='url(#clip4)' d='M450.781 181.101V177.41' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M448.789 177.411H452.773' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M450.781 181.101V184.793' fill='#8585c2'/>
+<path clip-path='url(#clip4)' d='M450.781 181.101V184.793' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M452.773 184.793H448.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M161.047 249.281H164.285V169.023H161.047ZM219.719 249.281H222.957V168.152H219.719ZM278.387 249.281H281.625V81.515H278.387ZM337.055 249.281H340.293V81.515H337.055ZM395.723 249.281H398.961V81.515H395.723ZM454.395 249.281H457.633V81.515H454.395Z' fill='#6666b3'/>
+<path clip-path='url(#clip4)' d='M161.047 249.281H164.285V169.023H161.047ZM219.719 249.281H222.957V168.152H219.719ZM278.387 249.281H281.625V81.515H278.387ZM337.055 249.281H340.293V81.515H337.055ZM395.723 249.281H398.961V81.515H395.723ZM454.395 249.281H457.633V81.515H454.395Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M162.668 169.023V168.957' fill='#6666b3'/>
+<path clip-path='url(#clip4)' d='M162.668 169.023V168.957' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M160.676 168.957H164.66' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M162.668 169.023V169.09' fill='#6666b3'/>
+<path clip-path='url(#clip4)' d='M162.668 169.023V169.09' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M164.661 169.089H160.676' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M221.336 168.152V168.015' fill='#6666b3'/>
+<path clip-path='url(#clip4)' d='M221.336 168.152V168.015' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M219.344 168.016H223.328' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M221.336 168.152V168.285' fill='#6666b3'/>
+<path clip-path='url(#clip4)' d='M221.336 168.152V168.285' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M223.329 168.285H219.344' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M280.004 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip4)' d='M278.012 81.516H282' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M280.004 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip4)' d='M278.012 81.516H282' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M338.676 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip4)' d='M336.684 81.516H340.668' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M338.676 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip4)' d='M336.684 81.516H340.668' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M397.344 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip4)' d='M395.352 81.516H399.336' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M397.344 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip4)' d='M395.352 81.516H399.336' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M456.012 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip4)' d='M454.02 81.516H458.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M456.012 81.515V81.515' fill='#6666b3'/>
+<path clip-path='url(#clip4)' d='M454.02 81.516H458.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M166.278 249.281H169.516V175.734H166.278ZM224.949 249.281H228.188V169.023H224.949ZM283.617 249.281H286.856V107.488H283.617ZM342.285 249.281H345.524V81.515H342.285ZM400.953 249.281H404.192V81.515H400.953ZM459.625 249.281H462.863V81.515H459.625Z' fill='#4747a4'/>
+<path clip-path='url(#clip4)' d='M166.278 249.281H169.516V175.734H166.278ZM224.949 249.281H228.188V169.023H224.949ZM283.617 249.281H286.856V107.488H283.617ZM342.285 249.281H345.524V81.515H342.285ZM400.953 249.281H404.192V81.515H400.953ZM459.625 249.281H462.863V81.515H459.625Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M167.899 175.734V175.465' fill='#4747a4'/>
+<path clip-path='url(#clip4)' d='M167.899 175.734V175.465' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M165.906 175.465H169.89' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M167.899 175.734V176.004' fill='#4747a4'/>
+<path clip-path='url(#clip4)' d='M167.899 175.734V176.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M169.891 176.004H165.906' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M226.567 169.023V168.554' fill='#4747a4'/>
+<path clip-path='url(#clip4)' d='M226.567 169.023V168.554' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M224.574 168.555H228.558' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M226.567 169.023V169.492' fill='#4747a4'/>
+<path clip-path='url(#clip4)' d='M226.567 169.023V169.492' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M228.559 169.492H224.574' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M285.234 107.488V107.015' fill='#4747a4'/>
+<path clip-path='url(#clip4)' d='M285.234 107.488V107.015' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M283.242 107.015H287.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M285.234 107.488V107.957' fill='#4747a4'/>
+<path clip-path='url(#clip4)' d='M285.234 107.488V107.957' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M287.227 107.957H283.242' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M343.906 81.515V81.515' fill='#4747a4'/>
+<path clip-path='url(#clip4)' d='M341.914 81.516H345.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M343.906 81.515V81.515' fill='#4747a4'/>
+<path clip-path='url(#clip4)' d='M341.914 81.516H345.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M402.574 81.515V81.515' fill='#4747a4'/>
+<path clip-path='url(#clip4)' d='M400.582 81.516H404.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M402.574 81.515V81.515' fill='#4747a4'/>
+<path clip-path='url(#clip4)' d='M400.582 81.516H404.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M461.242 81.515V81.515' fill='#4747a4'/>
+<path clip-path='url(#clip4)' d='M459.25 81.516H463.234' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M461.242 81.515V81.515' fill='#4747a4'/>
+<path clip-path='url(#clip4)' d='M459.25 81.516H463.234' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M171.508 249.281H174.746V168.82H171.508ZM230.18 249.281H233.414V168.621H230.18ZM288.848 249.281H292.086V81.515H288.848ZM347.516 249.281H350.754V81.515H347.516ZM406.184 249.281H409.422V81.515H406.184ZM464.856 249.281H468.094V81.515H464.856Z' fill='#292994'/>
+<path clip-path='url(#clip4)' d='M171.508 249.281H174.746V168.82H171.508ZM230.18 249.281H233.414V168.621H230.18ZM288.848 249.281H292.086V81.515H288.848ZM347.516 249.281H350.754V81.515H347.516ZM406.184 249.281H409.422V81.515H406.184ZM464.856 249.281H468.094V81.515H464.856Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M173.129 168.82V168.554' fill='#292994'/>
+<path clip-path='url(#clip4)' d='M173.129 168.82V168.554' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M171.137 168.555H175.121' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M173.129 168.82V169.09' fill='#292994'/>
+<path clip-path='url(#clip4)' d='M173.129 168.82V169.09' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M175.122 169.089H171.137' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M231.797 168.621V168.554' fill='#292994'/>
+<path clip-path='url(#clip4)' d='M231.797 168.621V168.554' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M229.805 168.555H233.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M231.797 168.621V168.687' fill='#292994'/>
+<path clip-path='url(#clip4)' d='M231.797 168.621V168.687' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M233.79 168.688H229.805' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M290.465 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip4)' d='M288.473 81.516H292.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M290.465 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip4)' d='M288.473 81.516H292.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M349.137 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip4)' d='M347.145 81.516H351.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M349.137 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip4)' d='M347.145 81.516H351.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M407.805 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip4)' d='M405.813 81.516H409.797' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M407.805 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip4)' d='M405.813 81.516H409.797' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M466.473 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip4)' d='M464.481 81.516H468.465' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M466.473 81.515V81.515' fill='#292994'/>
+<path clip-path='url(#clip4)' d='M464.481 81.516H468.465' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M411.414 249.281H414.652V133.59H411.414ZM176.738 249.281H179.977V162.113H176.738ZM235.41 249.281H238.645V162.648H235.41ZM294.078 249.281H297.317V81.515H294.078ZM352.746 249.281H355.984V125.875H352.746ZM470.086 249.281H473.324V181.035H470.086Z' fill='#0a0a85'/>
+<path clip-path='url(#clip4)' d='M411.414 249.281H414.652V133.59H411.414ZM176.738 249.281H179.977V162.113H176.738ZM235.41 249.281H238.645V162.648H235.41ZM294.078 249.281H297.317V81.515H294.078ZM352.746 249.281H355.984V125.875H352.746ZM470.086 249.281H473.324V181.035H470.086Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M413.035 133.59V126.211' fill='#0a0a85'/>
+<path clip-path='url(#clip4)' d='M413.035 133.59V126.211' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M411.043 126.211H415.027' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M413.035 133.59V140.972' fill='#0a0a85'/>
+<path clip-path='url(#clip4)' d='M413.035 133.59V140.972' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M415.027 140.973H411.043' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M178.36 162.113V161.777' fill='#0a0a85'/>
+<path clip-path='url(#clip4)' d='M178.36 162.113V161.777' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M176.367 161.777H180.351' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M178.36 162.113V162.445' fill='#0a0a85'/>
+<path clip-path='url(#clip4)' d='M178.36 162.113V162.445' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M180.352 162.445H176.367' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M237.027 162.648V162.515' fill='#0a0a85'/>
+<path clip-path='url(#clip4)' d='M237.027 162.648V162.515' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M235.035 162.516H239.019' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M237.027 162.648V162.781' fill='#0a0a85'/>
+<path clip-path='url(#clip4)' d='M237.027 162.648V162.781' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M239.02 162.781H235.035' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M295.695 81.515V81.515' fill='#0a0a85'/>
+<path clip-path='url(#clip4)' d='M293.703 81.516H297.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M295.695 81.515V81.515' fill='#0a0a85'/>
+<path clip-path='url(#clip4)' d='M293.703 81.516H297.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M354.367 125.875V125.672' fill='#0a0a85'/>
+<path clip-path='url(#clip4)' d='M354.367 125.875V125.672' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M352.375 125.672H356.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M354.367 125.875V126.074' fill='#0a0a85'/>
+<path clip-path='url(#clip4)' d='M354.367 125.875V126.074' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M356.359 126.074H352.371' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M471.703 181.035V178.219' fill='#0a0a85'/>
+<path clip-path='url(#clip4)' d='M471.703 181.035V178.219' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M469.711 178.219H473.695' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M471.703 181.035V183.855' fill='#0a0a85'/>
+<path clip-path='url(#clip4)' d='M471.703 181.035V183.855' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M473.695 183.856H469.711' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M181.969 249.281H185.207V154.594H181.969ZM240.641 249.281H243.875V149.695H240.641ZM299.309 249.281H302.547V81.515H299.309ZM357.977 249.281H361.215V81.515H357.977ZM475.317 249.281H478.555V81.515H475.317Z' fill='#000076'/>
+<path clip-path='url(#clip4)' d='M181.969 249.281H185.207V154.594H181.969ZM240.641 249.281H243.875V149.695H240.641ZM299.309 249.281H302.547V81.515H299.309ZM357.977 249.281H361.215V81.515H357.977ZM475.317 249.281H478.555V81.515H475.317Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M183.59 154.594V154.461' fill='#000076'/>
+<path clip-path='url(#clip4)' d='M183.59 154.594V154.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M181.598 154.461H185.583' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M183.59 154.594V154.73' fill='#000076'/>
+<path clip-path='url(#clip4)' d='M183.59 154.594V154.73' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M185.582 154.731H181.597' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M242.258 149.695V149.562' fill='#000076'/>
+<path clip-path='url(#clip4)' d='M242.258 149.695V149.562' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M240.266 149.563H244.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M242.258 149.695V149.832' fill='#000076'/>
+<path clip-path='url(#clip4)' d='M242.258 149.695V149.832' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M244.25 149.832H240.265' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M300.926 81.515V81.515' fill='#000076'/>
+<path clip-path='url(#clip4)' d='M298.933 81.516H302.921' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M300.926 81.515V81.515' fill='#000076'/>
+<path clip-path='url(#clip4)' d='M298.933 81.516H302.921' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M359.598 81.515V81.515' fill='#000076'/>
+<path clip-path='url(#clip4)' d='M357.606 81.516H361.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M359.598 81.515V81.515' fill='#000076'/>
+<path clip-path='url(#clip4)' d='M357.606 81.516H361.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M476.934 81.515V81.515' fill='#000076'/>
+<path clip-path='url(#clip4)' d='M474.942 81.516H478.926' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M476.934 81.515V81.515' fill='#000076'/>
+<path clip-path='url(#clip4)' d='M474.942 81.516H478.926' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M187.199 249.281H190.438V181.64H187.199ZM245.871 249.281H249.106V181.64H245.871ZM304.539 249.281H307.777V164.258H304.539ZM363.207 249.281H366.445V151.105H363.207ZM421.875 249.281H425.113V179.828H421.875ZM480.547 249.281H483.785V181.371H480.547Z' fill='#000067'/>
+<path clip-path='url(#clip4)' d='M187.199 249.281H190.438V181.64H187.199ZM245.871 249.281H249.106V181.64H245.871ZM304.539 249.281H307.777V164.258H304.539ZM363.207 249.281H366.445V151.105H363.207ZM421.875 249.281H425.113V179.828H421.875ZM480.547 249.281H483.785V181.371H480.547Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M188.82 181.64V181.57' fill='#000067'/>
+<path clip-path='url(#clip4)' d='M188.82 181.64V181.57' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M186.828 181.57H190.813' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M188.82 181.64V181.707' fill='#000067'/>
+<path clip-path='url(#clip4)' d='M188.82 181.64V181.707' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M190.813 181.707H186.828' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M247.488 181.64V181.437' fill='#000067'/>
+<path clip-path='url(#clip4)' d='M247.488 181.64V181.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M245.496 181.437H249.481' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M247.488 181.64V181.84' fill='#000067'/>
+<path clip-path='url(#clip4)' d='M247.488 181.64V181.84' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M249.481 181.84H245.496' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M306.156 164.258V163.992' fill='#000067'/>
+<path clip-path='url(#clip4)' d='M306.156 164.258V163.992' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M304.164 163.992H308.152' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M306.156 164.258V164.527' fill='#000067'/>
+<path clip-path='url(#clip4)' d='M306.156 164.258V164.527' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M308.149 164.527H304.164' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M364.828 151.105V150.168' fill='#000067'/>
+<path clip-path='url(#clip4)' d='M364.828 151.105V150.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M362.836 150.168H366.82' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M364.828 151.105V152.047' fill='#000067'/>
+<path clip-path='url(#clip4)' d='M364.828 151.105V152.047' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M366.82 152.047H362.832' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M423.496 179.828V179.09' fill='#000067'/>
+<path clip-path='url(#clip4)' d='M423.496 179.828V179.09' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M421.504 179.09H425.488' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M423.496 179.828V180.566' fill='#000067'/>
+<path clip-path='url(#clip4)' d='M423.496 179.828V180.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M425.488 180.567H421.504' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M482.164 181.371V178.148' fill='#000067'/>
+<path clip-path='url(#clip4)' d='M482.164 181.371V178.148' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M480.172 178.149H484.156' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M482.164 181.371V184.594' fill='#000067'/>
+<path clip-path='url(#clip4)' d='M482.164 181.371V184.594' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M484.156 184.594H480.172' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip4)' d='M419.477 298.301H487.762V276.324H419.477Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 254.512 92.648)'>
+<use x='168.285' xlink:href='#g1-72' y='190.457'/>
+<use x='172.021' xlink:href='#g1-80' y='190.457'/>
+<use x='175.402' xlink:href='#g1-45' y='190.457'/>
+<use x='177.167' xlink:href='#g1-90' y='190.457'/>
+<use x='180.401' xlink:href='#g1-52' y='190.457'/>
+<use x='183.047' xlink:href='#g1-45' y='190.457'/>
+<use x='184.812' xlink:href='#g1-71' y='190.457'/>
+<use x='188.34' xlink:href='#g1-52' y='190.457'/>
+<use x='190.987' xlink:href='#g1-44' y='190.457'/>
+<use x='194.221' xlink:href='#g1-49' y='190.457'/>
+<use x='196.867' xlink:href='#g1-54' y='190.457'/>
+<use x='199.514' xlink:href='#g1-71' y='190.457'/>
+<use x='203.042' xlink:href='#g1-98' y='190.457'/>
+<use x='168.285' xlink:href='#g1-56' y='196.075'/>
+<use x='170.931' xlink:href='#g1-45' y='196.075'/>
+<use x='172.695' xlink:href='#g1-99' y='196.075'/>
+<use x='175.048' xlink:href='#g1-111' y='196.075'/>
+<use x='177.547' xlink:href='#g1-114' y='196.075'/>
+<use x='179.355' xlink:href='#g1-101' y='196.075'/>
+<use x='183.471' xlink:href='#g1-73' y='196.075'/>
+<use x='184.941' xlink:href='#g1-110' y='196.075'/>
+<use x='187.674' xlink:href='#g1-116' y='196.075'/>
+<use x='189.585' xlink:href='#g1-101' y='196.075'/>
+<use x='191.938' xlink:href='#g1-108' y='196.075'/>
+<use x='194.965' xlink:href='#g1-88' y='196.075'/>
+<use x='198.493' xlink:href='#g1-69' y='196.075'/>
+<use x='201.667' xlink:href='#g1-79' y='196.075'/>
+<use x='205.576' xlink:href='#g1-78' y='196.075'/>
+<use x='211.076' xlink:href='#g1-64' y='196.075'/>
+<use x='214.605' xlink:href='#g1-50' y='196.075'/>
+<use x='217.251' xlink:href='#g1-46' y='196.075'/>
+<use x='218.721' xlink:href='#g1-55' y='196.075'/>
+<use x='221.368' xlink:href='#g1-71' y='196.075'/>
+<use x='224.896' xlink:href='#g1-104' y='196.075'/>
+<use x='227.629' xlink:href='#g1-122' y='196.075'/>
+<use x='168.285' xlink:href='#g1-85' y='201.694'/>
+<use x='171.917' xlink:href='#g1-98' y='201.694'/>
+<use x='174.65' xlink:href='#g1-117' y='201.694'/>
+<use x='177.383' xlink:href='#g1-110' y='201.694'/>
+<use x='180.116' xlink:href='#g1-116' y='201.694'/>
+<use x='182.027' xlink:href='#g1-117' y='201.694'/>
+<use x='186.524' xlink:href='#g1-49' y='201.694'/>
+<use x='189.17' xlink:href='#g1-56' y='201.694'/>
+<use x='191.817' xlink:href='#g1-46' y='201.694'/>
+<use x='193.287' xlink:href='#g1-48' y='201.694'/>
+<use x='195.933' xlink:href='#g1-52' y='201.694'/>
+<use x='198.58' xlink:href='#g1-46' y='201.694'/>
+<use x='200.05' xlink:href='#g1-49' y='201.694'/>
+<use x='202.696' xlink:href='#g1-44' y='201.694'/>
+<use x='205.931' xlink:href='#g1-71' y='201.694'/>
+<use x='209.459' xlink:href='#g1-67' y='201.694'/>
+<use x='212.841' xlink:href='#g1-67' y='201.694'/>
+<use x='217.986' xlink:href='#g1-55' y='201.694'/>
+<use x='220.633' xlink:href='#g1-46' y='201.694'/>
+<use x='222.103' xlink:href='#g1-52' y='201.694'/>
+<use x='224.749' xlink:href='#g1-46' y='201.694'/>
+<use x='226.219' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -58.314 344.257)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 0.355 344.257)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 59.024 344.257)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 117.693 344.257)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 176.362 344.257)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 235.031 344.257)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -53.084 343.452)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-49' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 5.585 341.573)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-52' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 64.254 311.979)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-52' y='201.694'/>
+<use x='175.048' xlink:href='#g1-56' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 122.923 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-50' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-54' y='201.694'/>
+<use x='180.675' xlink:href='#g1-53' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 181.592 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-53' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-48' y='201.694'/>
+<use x='180.675' xlink:href='#g1-54' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 240.261 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-49' y='201.694'/>
+<use x='176.558' xlink:href='#g1-49' y='201.694'/>
+<use x='179.205' xlink:href='#g1-46' y='201.694'/>
+<use x='180.675' xlink:href='#g1-56' y='201.694'/>
+<use x='183.321' xlink:href='#g1-51' y='201.694'/>
+<use x='185.968' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -47.853 340.768)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-53' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 10.816 339.761)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-55' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 69.485 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-51' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-51' y='201.694'/>
+<use x='180.675' xlink:href='#g1-54' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 128.154 295.471)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-55' y='201.694'/>
+<use x='175.048' xlink:href='#g1-51' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 186.823 272.99)'>
+<use x='168.285' xlink:href='#g1-50' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-54' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 245.492 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-49' y='201.694'/>
+<use x='176.558' xlink:href='#g1-50' y='201.694'/>
+<use x='179.205' xlink:href='#g1-46' y='201.694'/>
+<use x='180.675' xlink:href='#g1-48' y='201.694'/>
+<use x='183.321' xlink:href='#g1-55' y='201.694'/>
+<use x='185.968' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -42.623 323.991)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-51' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 16.046 320.3)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-51' y='201.694'/>
+<use x='175.048' xlink:href='#g1-54' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 74.715 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-50' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-56' y='201.694'/>
+<use x='180.675' xlink:href='#g1-57' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 133.384 312.18)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-52' y='201.694'/>
+<use x='175.048' xlink:href='#g1-56' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 192.053 324.528)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-50' y='201.694'/>
+<use x='175.048' xlink:href='#g1-57' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 250.722 343.183)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-50' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -37.392 331.104)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-50' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 21.277 330.232)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-50' y='201.694'/>
+<use x='175.048' xlink:href='#g1-49' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 79.946 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-50' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-54' y='201.694'/>
+<use x='180.675' xlink:href='#g1-53' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 138.615 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-51' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-53' y='201.694'/>
+<use x='180.675' xlink:href='#g1-49' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 197.284 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-51' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-53' y='201.694'/>
+<use x='180.675' xlink:href='#g1-49' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 255.953 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-54' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-49' y='201.694'/>
+<use x='180.675' xlink:href='#g1-56' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -32.162 337.815)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-49' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 26.507 331.104)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-50' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 85.176 269.568)'>
+<use x='168.285' xlink:href='#g1-50' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-49' y='201.694'/>
+<use x='175.048' xlink:href='#g1-49' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 143.845 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-49' y='201.694'/>
+<use x='176.558' xlink:href='#g1-48' y='201.694'/>
+<use x='179.205' xlink:href='#g1-46' y='201.694'/>
+<use x='180.675' xlink:href='#g1-50' y='201.694'/>
+<use x='183.321' xlink:href='#g1-50' y='201.694'/>
+<use x='185.968' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 202.514 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-51' y='201.694'/>
+<use x='176.558' xlink:href='#g1-49' y='201.694'/>
+<use x='179.205' xlink:href='#g1-46' y='201.694'/>
+<use x='180.675' xlink:href='#g1-52' y='201.694'/>
+<use x='183.321' xlink:href='#g1-50' y='201.694'/>
+<use x='185.968' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 261.183 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-55' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-48' y='201.694'/>
+<use x='180.675' xlink:href='#g1-49' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -26.931 330.903)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-50' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 31.738 330.702)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-50' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 90.407 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-50' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-54' y='201.694'/>
+<use x='180.675' xlink:href='#g1-55' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 149.076 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-51' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-54' y='201.694'/>
+<use x='180.675' xlink:href='#g1-48' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 207.745 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-51' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-53' y='201.694'/>
+<use x='180.675' xlink:href='#g1-49' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 266.414 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-53' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-55' y='201.694'/>
+<use x='180.675' xlink:href='#g1-54' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 212.975 295.672)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-55' y='201.694'/>
+<use x='175.048' xlink:href='#g1-50' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -21.701 324.192)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-51' y='201.694'/>
+<use x='175.048' xlink:href='#g1-48' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 36.968 324.729)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-50' y='201.694'/>
+<use x='175.048' xlink:href='#g1-57' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 95.637 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-51' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-51' y='201.694'/>
+<use x='180.675' xlink:href='#g1-55' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 154.306 287.955)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-56' y='201.694'/>
+<use x='175.048' xlink:href='#g1-52' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 271.644 343.116)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-50' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -16.471 316.676)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-52' y='201.694'/>
+<use x='175.048' xlink:href='#g1-49' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 42.198 311.778)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-52' y='201.694'/>
+<use x='175.048' xlink:href='#g1-56' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 100.867 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-54' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-48' y='201.694'/>
+<use x='180.675' xlink:href='#g1-54' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 159.536 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-51' y='201.694'/>
+<use x='176.558' xlink:href='#g1-46' y='201.694'/>
+<use x='178.029' xlink:href='#g1-49' y='201.694'/>
+<use x='180.675' xlink:href='#g1-52' y='201.694'/>
+<use x='183.321' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 276.874 243.598)'>
+<use x='163.396' xlink:href='#g3-1' y='201.694'/>
+<use x='166.901' xlink:href='#g3-1' y='201.694'/>
+<use x='170.407' xlink:href='#g3-1' y='201.694'/>
+<use x='173.912' xlink:href='#g1-49' y='201.694'/>
+<use x='176.558' xlink:href='#g1-49' y='201.694'/>
+<use x='179.205' xlink:href='#g1-46' y='201.694'/>
+<use x='180.675' xlink:href='#g1-56' y='201.694'/>
+<use x='183.321' xlink:href='#g1-56' y='201.694'/>
+<use x='185.968' xlink:href='#g1-120' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -11.24 343.72)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-49' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 47.429 343.72)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-49' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 106.098 326.34)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-50' y='201.694'/>
+<use x='175.048' xlink:href='#g1-55' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 164.767 313.187)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-52' y='201.694'/>
+<use x='175.048' xlink:href='#g1-54' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 223.436 341.908)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-51' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 282.105 343.452)'>
+<use x='168.285' xlink:href='#g1-49' y='201.694'/>
+<use x='170.931' xlink:href='#g1-46' y='201.694'/>
+<use x='172.401' xlink:href='#g1-48' y='201.694'/>
+<use x='175.048' xlink:href='#g1-49' y='201.694'/>
+</g>
+<g transform='matrix(0 -1 1 0 -88.536 388.944)'>
+<use x='168.285' xlink:href='#g0-82' y='201.694'/>
+<use x='174.255' xlink:href='#g0-101' y='201.694'/>
+<use x='178.351' xlink:href='#g0-108' y='201.694'/>
+<use x='180.551' xlink:href='#g0-97' y='201.694'/>
+<use x='184.979' xlink:href='#g0-116' y='201.694'/>
+<use x='188.307' xlink:href='#g0-105' y='201.694'/>
+<use x='190.507' xlink:href='#g0-118' y='201.694'/>
+<use x='194.755' xlink:href='#g0-101' y='201.694'/>
+<use x='201.922' xlink:href='#g0-116' y='201.694'/>
+<use x='205.25' xlink:href='#g0-105' y='201.694'/>
+<use x='207.45' xlink:href='#g0-109' y='201.694'/>
+<use x='214.77' xlink:href='#g0-101' y='201.694'/>
+<use x='221.937' xlink:href='#g2-40' y='201.694'/>
+<use x='225.231' xlink:href='#g2-108' y='201.694'/>
+<use x='227.251' xlink:href='#g2-111' y='201.694'/>
+<use x='231.25' xlink:href='#g2-119' y='201.694'/>
+<use x='236.799' xlink:href='#g2-101' y='201.694'/>
+<use x='240.562' xlink:href='#g2-114' y='201.694'/>
+<use x='246.277' xlink:href='#g2-105' y='201.694'/>
+<use x='248.297' xlink:href='#g2-115' y='201.694'/>
+<use x='254.366' xlink:href='#g2-98' y='201.694'/>
+<use x='258.974' xlink:href='#g2-101' y='201.694'/>
+<use x='262.738' xlink:href='#g2-116' y='201.694'/>
+<use x='265.796' xlink:href='#g2-116' y='201.694'/>
+<use x='268.854' xlink:href='#g2-101' y='201.694'/>
+<use x='272.618' xlink:href='#g2-114' y='201.694'/>
+<use x='275.51' xlink:href='#g2-41' y='201.694'/>
+</g>
+<path d='M136.149 296.672H402.137V279.679H136.149Z' fill='#ffffff'/>
+<path d='M136.149 296.672H402.137V279.679H136.149Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path d='M139.336 290.168H142.324V282.199H139.336ZM145.313 290.168H148.301V284.191H145.313Z' fill='#e0e0f0'/>
+<path d='M139.336 290.168H142.324V282.199H139.336ZM145.313 290.168H148.301V284.191H145.313Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -33.522 88.465)'>
+<use x='185.011' xlink:href='#g2-109' y='201.694'/>
+<use x='191.736' xlink:href='#g2-105' y='201.694'/>
+</g>
+<path d='M163.422 290.168H166.41V282.199H163.422ZM169.398 290.168H172.391V284.191H169.398Z' fill='#c2c2e1'/>
+<path d='M163.422 290.168H166.41V282.199H163.422ZM169.398 290.168H172.391V284.191H169.398Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.56 88.011)'>
+<use x='208.137' xlink:href='#g2-116' y='201.694'/>
+<use x='211.195' xlink:href='#g2-99' y='201.694'/>
+</g>
+<path d='M185.586 290.168H188.574V282.199H185.586ZM191.563 290.168H194.555V284.191H191.563Z' fill='#a3a3d1'/>
+<path d='M185.586 290.168H188.574V282.199H185.586ZM191.563 290.168H194.555V284.191H191.563Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.159 88.465)'>
+<use x='229.9' xlink:href='#g2-106' y='201.694'/>
+<use x='232.155' xlink:href='#g2-101' y='201.694'/>
+</g>
+<path d='M206.949 290.168H209.938V282.199H206.949ZM212.926 290.168H215.914V284.191H212.926Z' fill='#8585c2'/>
+<path d='M206.949 290.168H209.938V282.199H206.949ZM212.926 290.168H215.914V284.191H212.926Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.958 87.505)'>
+<use x='252.061' xlink:href='#g2-115' y='201.694'/>
+<use x='255.307' xlink:href='#g2-110' y='201.694'/>
+</g>
+<path d='M229.91 290.168H232.898V282.199H229.91ZM235.887 290.168H238.875V284.191H235.887Z' fill='#6666b3'/>
+<path d='M229.91 290.168H232.898V282.199H229.91ZM235.887 290.168H238.875V284.191H235.887Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.781 87.505)'>
+<use x='274.845' xlink:href='#g2-114' y='201.694'/>
+<use x='277.737' xlink:href='#g2-112' y='201.694'/>
+</g>
+<path d='M252.516 290.168H255.504V282.199H252.516ZM258.496 290.168H261.484V284.191H258.496Z' fill='#4747a4'/>
+<path d='M252.516 290.168H255.504V282.199H252.516ZM258.496 290.168H261.484V284.191H258.496Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -39.001 88.501)'>
+<use x='303.672' xlink:href='#g2-104' y='201.694'/>
+<use x='308.045' xlink:href='#g2-111' y='201.694'/>
+<use x='312.279' xlink:href='#g2-97' y='201.694'/>
+<use x='316.112' xlink:href='#g2-114' y='201.694'/>
+<use x='319.004' xlink:href='#g2-100' y='201.694'/>
+</g>
+<path d='M287.563 290.168H290.551V282.199H287.563ZM293.543 290.168H296.531V284.191H293.543Z' fill='#292994'/>
+<path d='M287.563 290.168H290.551V282.199H287.563ZM293.543 290.168H296.531V284.191H293.543Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.472 88.501)'>
+<use x='337.19' xlink:href='#g2-103' y='201.694'/>
+<use x='341.424' xlink:href='#g2-108' y='201.694'/>
+<use x='343.444' xlink:href='#g2-105' y='201.694'/>
+<use x='345.464' xlink:href='#g2-98' y='201.694'/>
+<use x='350.072' xlink:href='#g2-99' y='201.694'/>
+</g>
+<path d='M319.551 290.168H322.539V282.199H319.551ZM325.527 290.168H328.52V284.191H325.527Z' fill='#0a0a85'/>
+<path d='M319.551 290.168H322.539V282.199H319.551ZM325.527 290.168H328.52V284.191H325.527Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -35.051 88.501)'>
+<use x='366.757' xlink:href='#g2-116' y='201.694'/>
+<use x='369.815' xlink:href='#g2-98' y='201.694'/>
+<use x='374.187' xlink:href='#g2-98' y='201.694'/>
+</g>
+<path d='M346.699 290.168H349.688V282.199H346.699ZM352.676 290.168H355.664V284.191H352.676Z' fill='#000076'/>
+<path d='M346.699 290.168H349.688V282.199H346.699ZM352.676 290.168H355.664V284.191H352.676Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -34.135 87.505)'>
+<use x='392.986' xlink:href='#g2-115' y='201.694'/>
+<use x='396.233' xlink:href='#g2-109' y='201.694'/>
+</g>
+<path d='M372.012 290.168H375V282.199H372.012ZM377.988 290.168H380.977V284.191H377.988Z' fill='#000067'/>
+<path d='M372.012 290.168H375V282.199H372.012ZM377.988 290.168H380.977V284.191H377.988Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -35.145 88.465)'>
+<use x='419.31' xlink:href='#g2-115' y='201.694'/>
+<use x='422.556' xlink:href='#g2-109' y='201.694'/>
+<use x='429.281' xlink:href='#g2-105' y='201.694'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='164.687pt' version='1.1' viewBox='52.938 54.996 381.625 164.687' width='381.625pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip9'>
+<path d='M82.148 203.937H434.164V78.691H82.148Z'/>
+</clipPath>
+<use id='g2-40' transform='scale(1.6)' xlink:href='#g1-40'/>
+<use id='g2-41' transform='scale(1.6)' xlink:href='#g1-41'/>
+<use id='g2-78' transform='scale(1.6)' xlink:href='#g1-78'/>
+<use id='g2-97' transform='scale(1.6)' xlink:href='#g1-97'/>
+<use id='g2-98' transform='scale(1.6)' xlink:href='#g1-98'/>
+<use id='g2-99' transform='scale(1.6)' xlink:href='#g1-99'/>
+<use id='g2-100' transform='scale(1.6)' xlink:href='#g1-100'/>
+<use id='g2-101' transform='scale(1.6)' xlink:href='#g1-101'/>
+<use id='g2-102' transform='scale(1.6)' xlink:href='#g1-102'/>
+<use id='g2-105' transform='scale(1.6)' xlink:href='#g1-105'/>
+<use id='g2-108' transform='scale(1.6)' xlink:href='#g1-108'/>
+<use id='g2-110' transform='scale(1.6)' xlink:href='#g1-110'/>
+<use id='g2-111' transform='scale(1.6)' xlink:href='#g1-111'/>
+<use id='g2-112' transform='scale(1.6)' xlink:href='#g1-112'/>
+<use id='g2-114' transform='scale(1.6)' xlink:href='#g1-114'/>
+<use id='g2-115' transform='scale(1.6)' xlink:href='#g1-115'/>
+<use id='g2-116' transform='scale(1.6)' xlink:href='#g1-116'/>
+<use id='g2-119' transform='scale(1.6)' xlink:href='#g1-119'/>
+<path d='M1.519 -3.736C1.435 -3.736 1.425 -3.736 1.365 -3.681C0.737 -3.133 0.418 -2.247 0.418 -1.245C0.418 -0.304 0.702 0.603 1.36 1.181C1.425 1.245 1.435 1.245 1.519 1.245H1.758C1.743 1.235 1.26 0.822 1.031 0.045C0.912 -0.344 0.852 -0.752 0.852 -1.245C0.852 -2.969 1.659 -3.651 1.758 -3.736H1.519Z' id='g1-40'/>
+<path d='M0.533 1.245C0.618 1.245 0.628 1.245 0.687 1.191C1.315 0.643 1.634 -0.244 1.634 -1.245C1.634 -2.187 1.35 -3.093 0.692 -3.671C0.628 -3.736 0.618 -3.736 0.533 -3.736H0.294C0.309 -3.726 0.792 -3.313 1.021 -2.535C1.141 -2.147 1.2 -1.738 1.2 -1.245C1.2 0.478 0.394 1.161 0.294 1.245H0.533Z' id='g1-41'/>
+<path d='M0.956 -0.448H0.508V0H0.956V-0.448Z' id='g1-46'/>
+<path d='M2.431 -1.619C2.431 -1.858 2.431 -2.441 2.197 -2.849C1.943 -3.298 1.559 -3.372 1.32 -3.372C1.096 -3.372 0.707 -3.303 0.458 -2.874C0.219 -2.476 0.209 -1.933 0.209 -1.619C0.209 -1.25 0.229 -0.797 0.438 -0.418C0.658 -0.015 1.026 0.105 1.32 0.105C1.818 0.105 2.092 -0.184 2.242 -0.498C2.416 -0.852 2.431 -1.31 2.431 -1.619ZM1.32 -0.224C1.111 -0.224 0.872 -0.344 0.747 -0.702C0.648 -1.006 0.643 -1.32 0.643 -1.684C0.643 -2.142 0.643 -3.044 1.32 -3.044S1.998 -2.142 1.998 -1.684C1.998 -1.355 1.998 -0.981 1.878 -0.663C1.738 -0.304 1.484 -0.224 1.32 -0.224Z' id='g1-48'/>
+<path d='M1.599 -3.372H1.489C1.166 -3.073 0.757 -3.054 0.458 -3.044V-2.73C0.653 -2.735 0.902 -2.745 1.151 -2.844V-0.314H0.488V0H2.262V-0.314H1.599V-3.372Z' id='g1-49'/>
+<path d='M1.41 -0.384C1.35 -0.384 1.29 -0.379 1.23 -0.379H0.663L1.435 -1.061C1.524 -1.141 1.768 -1.325 1.863 -1.405C2.082 -1.604 2.376 -1.863 2.376 -2.296C2.376 -2.859 1.958 -3.372 1.245 -3.372C0.717 -3.372 0.389 -3.088 0.219 -2.58L0.453 -2.286C0.568 -2.705 0.742 -3.029 1.176 -3.029C1.594 -3.029 1.913 -2.735 1.913 -2.286C1.913 -1.873 1.669 -1.639 1.37 -1.355C1.27 -1.255 1.001 -1.031 0.897 -0.932C0.752 -0.802 0.408 -0.468 0.264 -0.344V0H2.376V-0.384H1.41Z' id='g1-50'/>
+<path d='M0.498 -2.555C0.702 -2.954 1.061 -3.064 1.3 -3.064C1.594 -3.064 1.813 -2.894 1.813 -2.61C1.813 -2.346 1.634 -2.022 1.255 -1.958C1.23 -1.953 1.21 -1.953 0.882 -1.928V-1.599H1.27C1.743 -1.599 1.918 -1.225 1.918 -0.912C1.918 -0.523 1.679 -0.224 1.29 -0.224C0.936 -0.224 0.533 -0.394 0.284 -0.712L0.219 -0.389C0.508 -0.065 0.912 0.105 1.3 0.105C1.953 0.105 2.421 -0.384 2.421 -0.907C2.421 -1.315 2.092 -1.644 1.699 -1.758C2.077 -1.953 2.271 -2.286 2.271 -2.61C2.271 -3.034 1.838 -3.372 1.305 -3.372C0.867 -3.372 0.503 -3.143 0.294 -2.844L0.498 -2.555Z' id='g1-51'/>
+<path d='M1.973 -0.832H2.491V-1.161H1.973V-3.268H1.479L0.149 -1.161V-0.832H1.544V0H1.973V-0.832ZM0.573 -1.161C0.722 -1.395 1.579 -2.725 1.579 -3.024V-1.161H0.573Z' id='g1-52'/>
+<path d='M0.817 -2.924H2.197V-3.268H0.418V-1.405H0.782C0.902 -1.674 1.136 -1.793 1.36 -1.793C1.564 -1.793 1.873 -1.654 1.873 -1.021C1.873 -0.369 1.46 -0.224 1.205 -0.224C0.877 -0.224 0.558 -0.399 0.389 -0.682L0.199 -0.384C0.443 -0.08 0.812 0.105 1.205 0.105C1.863 0.105 2.376 -0.403 2.376 -1.011C2.376 -1.629 1.918 -2.122 1.37 -2.122C1.156 -2.122 0.966 -2.047 0.817 -1.923V-2.924Z' id='g1-53'/>
+<path d='M2.187 -3.273C1.918 -3.372 1.729 -3.372 1.634 -3.372C0.877 -3.372 0.219 -2.66 0.219 -1.609C0.219 -0.259 0.827 0.105 1.33 0.105C1.733 0.105 1.943 -0.085 2.097 -0.244C2.416 -0.583 2.421 -0.936 2.421 -1.111C2.421 -1.763 2.067 -2.306 1.584 -2.306C1.096 -2.306 0.832 -2.052 0.687 -1.908C0.752 -2.59 1.101 -3.064 1.639 -3.064C1.738 -3.064 1.938 -3.054 2.187 -2.959V-3.273ZM0.692 -1.096C0.692 -1.126 0.692 -1.2 0.697 -1.225C0.697 -1.564 0.912 -1.978 1.355 -1.978C1.963 -1.978 1.963 -1.28 1.963 -1.111C1.963 -0.922 1.963 -0.712 1.828 -0.503C1.709 -0.324 1.559 -0.224 1.33 -0.224C0.802 -0.224 0.717 -0.877 0.692 -1.096Z' id='g1-54'/>
+<path d='M1.23 -2.884C1.29 -2.884 1.35 -2.889 1.41 -2.889H2.037C1.28 -2.147 0.797 -1.106 0.797 0.05H1.265C1.265 -1.405 1.973 -2.451 2.421 -2.919V-3.268H0.219V-2.884H1.23Z' id='g1-55'/>
+<path d='M1.704 -1.763C2.032 -1.868 2.346 -2.132 2.346 -2.501C2.346 -2.954 1.913 -3.372 1.32 -3.372S0.294 -2.954 0.294 -2.501C0.294 -2.127 0.618 -1.863 0.936 -1.763C0.498 -1.629 0.219 -1.29 0.219 -0.907C0.219 -0.374 0.692 0.105 1.32 0.105S2.421 -0.374 2.421 -0.907C2.421 -1.29 2.137 -1.629 1.704 -1.763ZM1.32 -1.928C0.966 -1.928 0.677 -2.132 0.677 -2.496C0.677 -2.814 0.902 -3.064 1.32 -3.064C1.733 -3.064 1.963 -2.814 1.963 -2.496C1.963 -2.142 1.684 -1.928 1.32 -1.928ZM1.32 -0.224C0.976 -0.224 0.672 -0.443 0.672 -0.912C0.672 -1.36 0.961 -1.599 1.32 -1.599S1.968 -1.355 1.968 -0.912C1.968 -0.443 1.659 -0.224 1.32 -0.224Z' id='g1-56'/>
+<path d='M0.384 -0.125C0.628 0.055 0.852 0.105 1.086 0.105C1.783 0.105 2.421 -0.598 2.421 -1.669C2.421 -3.029 1.818 -3.372 1.34 -3.372C0.897 -3.372 0.692 -3.163 0.548 -3.019C0.229 -2.695 0.219 -2.351 0.219 -2.157C0.219 -1.514 0.568 -0.961 1.056 -0.961C1.619 -0.961 1.928 -1.335 1.953 -1.365C1.883 -0.573 1.494 -0.224 1.086 -0.224C0.827 -0.224 0.667 -0.319 0.553 -0.413L0.384 -0.125ZM1.938 -2.162C1.943 -2.132 1.943 -2.082 1.943 -2.052C1.943 -1.684 1.719 -1.29 1.285 -1.29C1.096 -1.29 0.946 -1.345 0.817 -1.549C0.687 -1.743 0.677 -1.933 0.677 -2.157C0.677 -2.351 0.677 -2.575 0.832 -2.795C0.936 -2.944 1.086 -3.064 1.335 -3.064C1.818 -3.064 1.923 -2.481 1.938 -2.162Z' id='g1-57'/>
+<path d='M1.176 -3.457H0.498V0H0.917V-3.064H0.922L2.555 0H3.233V-3.457H2.814V-0.394H2.809L1.176 -3.457Z' id='g1-78'/>
+<path d='M2.122 -1.435C2.122 -1.943 1.733 -2.286 1.24 -2.286C0.927 -2.286 0.687 -2.222 0.408 -2.072L0.438 -1.709C0.603 -1.818 0.847 -1.968 1.24 -1.968C1.46 -1.968 1.689 -1.803 1.689 -1.43V-1.23C0.951 -1.205 0.224 -1.051 0.224 -0.588C0.224 -0.339 0.394 0.05 0.832 0.05C1.046 0.05 1.44 0.005 1.704 -0.189V0H2.122V-1.435ZM1.689 -0.707C1.689 -0.608 1.689 -0.478 1.514 -0.374C1.355 -0.284 1.161 -0.279 1.106 -0.279C0.832 -0.279 0.623 -0.403 0.623 -0.593C0.623 -0.912 1.465 -0.941 1.689 -0.951V-0.707Z' id='g1-97'/>
+<path d='M0.842 -3.457H0.423V0H0.857V-0.234C0.966 -0.139 1.205 0.05 1.569 0.05C2.112 0.05 2.55 -0.458 2.55 -1.111C2.55 -1.714 2.207 -2.262 1.709 -2.262C1.395 -2.262 1.091 -2.162 0.842 -1.978V-3.457ZM0.857 -1.569C0.857 -1.649 0.857 -1.709 1.031 -1.823C1.106 -1.868 1.24 -1.933 1.41 -1.933C1.743 -1.933 2.117 -1.709 2.117 -1.111C2.117 -0.503 1.704 -0.279 1.355 -0.279C1.171 -0.279 0.996 -0.364 0.857 -0.588V-1.569Z' id='g1-98'/>
+<path d='M2.167 -0.543C1.918 -0.384 1.649 -0.294 1.34 -0.294C0.882 -0.294 0.613 -0.663 0.613 -1.111C0.613 -1.494 0.812 -1.943 1.355 -1.943C1.694 -1.943 1.853 -1.873 2.107 -1.714L2.172 -2.072C1.873 -2.222 1.743 -2.286 1.355 -2.286C0.608 -2.286 0.179 -1.684 0.179 -1.106C0.179 -0.498 0.658 0.05 1.335 0.05C1.684 0.05 1.983 -0.055 2.197 -0.179L2.167 -0.543Z' id='g1-99'/>
+<path d='M2.306 -3.457H1.888V-1.998C1.569 -2.232 1.245 -2.262 1.101 -2.262C0.578 -2.262 0.179 -1.738 0.179 -1.106S0.573 0.05 1.086 0.05C1.395 0.05 1.684 -0.09 1.873 -0.259V0H2.306V-3.457ZM1.873 -0.618C1.748 -0.413 1.559 -0.279 1.32 -0.279C0.971 -0.279 0.613 -0.523 0.613 -1.101C0.613 -1.724 1.036 -1.933 1.375 -1.933C1.574 -1.933 1.743 -1.848 1.873 -1.679V-0.618Z' id='g1-100'/>
+<path d='M2.142 -0.543C1.863 -0.344 1.549 -0.279 1.335 -0.279C0.902 -0.279 0.573 -0.633 0.558 -1.091H2.192C2.192 -1.32 2.167 -1.654 1.973 -1.938C1.793 -2.192 1.494 -2.286 1.25 -2.286C0.643 -2.286 0.174 -1.753 0.174 -1.121C0.174 -0.483 0.672 0.05 1.33 0.05C1.619 0.05 1.918 -0.035 2.172 -0.189L2.142 -0.543ZM0.593 -1.39C0.707 -1.788 1.001 -1.958 1.25 -1.958C1.469 -1.958 1.793 -1.853 1.888 -1.39H0.593Z' id='g1-101'/>
+<path d='M0.946 -1.898H1.514V-2.212H0.932V-2.785C0.932 -3.128 1.245 -3.178 1.41 -3.178C1.514 -3.178 1.649 -3.163 1.833 -3.093V-3.457C1.704 -3.487 1.549 -3.507 1.415 -3.507C0.902 -3.507 0.528 -3.138 0.528 -2.645V-2.212H0.144V-1.898H0.528V0H0.946V-1.898Z' id='g1-102'/>
+<path d='M0.877 -3.417H0.374V-2.914H0.877V-3.417ZM0.837 -2.212H0.418V0H0.837V-2.212Z' id='g1-105'/>
+<path d='M0.837 -3.457H0.418V0H0.837V-3.457Z' id='g1-108'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-110'/>
+<path d='M2.491 -1.091C2.491 -1.748 1.968 -2.286 1.32 -2.286S0.149 -1.743 0.149 -1.091C0.149 -0.458 0.677 0.05 1.32 0.05C1.968 0.05 2.491 -0.458 2.491 -1.091ZM1.32 -0.294C0.927 -0.294 0.583 -0.583 0.583 -1.146S0.951 -1.958 1.32 -1.958C1.694 -1.958 2.057 -1.699 2.057 -1.146C2.057 -0.578 1.704 -0.294 1.32 -0.294Z' id='g1-111'/>
+<path d='M0.857 -0.234C1.121 0.005 1.405 0.05 1.574 0.05C2.102 0.05 2.55 -0.453 2.55 -1.111C2.55 -1.709 2.222 -2.262 1.729 -2.262C1.504 -2.262 1.131 -2.197 0.842 -1.973V-2.212H0.423V0.966H0.857V-0.234ZM0.857 -1.654C0.971 -1.793 1.166 -1.918 1.405 -1.918C1.803 -1.918 2.117 -1.549 2.117 -1.111C2.117 -0.618 1.743 -0.279 1.355 -0.279C1.28 -0.279 1.156 -0.289 1.026 -0.394C0.877 -0.508 0.857 -0.583 0.857 -0.677V-1.654Z' id='g1-112'/>
+<path d='M0.842 -1.061C0.842 -1.599 1.29 -1.888 1.729 -1.893V-2.262C1.31 -2.257 1.006 -2.052 0.807 -1.788V-2.247H0.423V0H0.842V-1.061Z' id='g1-114'/>
+<path d='M1.818 -2.132C1.479 -2.271 1.23 -2.286 1.051 -2.286C0.927 -2.286 0.174 -2.286 0.174 -1.624C0.174 -1.39 0.304 -1.26 0.369 -1.2C0.543 -1.026 0.752 -0.986 1.016 -0.936C1.25 -0.892 1.519 -0.842 1.519 -0.603C1.519 -0.289 1.106 -0.289 1.036 -0.289C0.717 -0.289 0.418 -0.403 0.219 -0.543L0.149 -0.169C0.319 -0.085 0.623 0.05 1.036 0.05C1.26 0.05 1.479 0.015 1.664 -0.12C1.848 -0.259 1.908 -0.478 1.908 -0.648C1.908 -0.737 1.898 -0.932 1.689 -1.121C1.504 -1.285 1.325 -1.32 1.086 -1.365C0.792 -1.42 0.563 -1.465 0.563 -1.684C0.563 -1.968 0.927 -1.968 1.001 -1.968C1.285 -1.968 1.504 -1.908 1.753 -1.778L1.818 -2.132Z' id='g1-115'/>
+<path d='M0.936 -1.898H1.674V-2.212H0.936V-2.844H0.553V-2.212H0.1V-1.898H0.538V-0.638C0.538 -0.304 0.623 0.05 0.981 0.05S1.614 -0.065 1.763 -0.134L1.679 -0.453C1.514 -0.334 1.34 -0.294 1.2 -0.294C0.991 -0.294 0.936 -0.498 0.936 -0.727V-1.898Z' id='g1-116'/>
+<path d='M3.537 -2.212H3.148C3.103 -2.072 2.824 -1.23 2.67 -0.712C2.63 -0.568 2.58 -0.408 2.565 -0.294H2.56C2.531 -0.498 2.356 -1.036 2.346 -1.071L1.978 -2.212H1.599C1.455 -1.783 1.081 -0.667 1.041 -0.304H1.036C0.996 -0.658 0.628 -1.758 0.548 -1.998C0.508 -2.117 0.508 -2.127 0.483 -2.212H0.075L0.802 0H1.22C1.225 -0.02 1.36 -0.413 1.534 -0.966C1.609 -1.21 1.758 -1.689 1.783 -1.908L1.788 -1.913C1.798 -1.808 1.828 -1.699 1.863 -1.574S1.933 -1.315 1.968 -1.2L2.351 0H2.809L3.537 -2.212Z' id='g1-119'/>
+<path d='M1.38 -1.141L2.346 -2.212H1.908L1.2 -1.395L0.478 -2.212H0.03L1.026 -1.141L0 0H0.443L1.2 -0.936L1.988 0H2.436L1.38 -1.141Z' id='g1-120'/>
+<path d='M1.445 -1.245C1.445 -1.41 1.305 -1.549 1.141 -1.549S0.837 -1.41 0.837 -1.245S0.976 -0.941 1.141 -0.941S1.445 -1.081 1.445 -1.245Z' id='g3-1'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g0-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g0-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g0-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g0-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g0-108'/>
+<path d='M1.462 -1.91C1.462 -2.851 2.197 -3.425 3.013 -3.434V-4.08C2.367 -4.071 1.775 -3.748 1.408 -3.219V-4.035H0.744V0H1.462V-1.91Z' id='g0-114'/>
+<path d='M3.165 -3.847C2.609 -4.098 2.197 -4.133 1.829 -4.133C1.623 -4.133 0.305 -4.133 0.305 -2.95C0.305 -2.52 0.565 -2.251 0.664 -2.152C1.004 -1.856 1.237 -1.811 1.847 -1.695C2.134 -1.641 2.645 -1.542 2.645 -1.085C2.645 -0.502 1.919 -0.502 1.802 -0.502C1.273 -0.502 0.762 -0.681 0.377 -0.95L0.26 -0.296C0.798 -0.009 1.345 0.099 1.802 0.099C2.385 0.099 3.318 -0.09 3.318 -1.157C3.318 -1.47 3.192 -1.784 2.878 -2.053C2.573 -2.313 2.304 -2.367 1.748 -2.475C1.426 -2.537 0.977 -2.618 0.977 -3.04C0.977 -3.569 1.614 -3.569 1.748 -3.569C2.403 -3.569 2.789 -3.362 3.049 -3.219L3.165 -3.847Z' id='g0-115'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g0-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g0-118'/>
+</defs>
+<g id='page9'>
+<path d='M140.82 212.793V203.937M199.488 212.793V203.937M258.156 212.793V203.937M316.824 212.793V203.937M375.496 212.793V203.937M140.82 69.836V78.691M199.488 69.836V78.691M258.156 69.836V78.691M316.824 69.836V78.691M375.496 69.836V78.691' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M111.484 208.191V203.937M170.152 208.191V203.937M228.824 208.191V203.937M287.492 208.191V203.937M346.16 208.191V203.937M404.828 208.191V203.937M111.484 74.441V78.691M170.152 74.441V78.691M228.824 74.441V78.691M287.492 74.441V78.691M346.16 74.441V78.691M404.828 74.441V78.691' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 203.937H86.402M82.148 172.625H86.402M82.148 141.316H86.402M82.148 110.004H86.402M82.148 78.691H86.402M434.164 203.937H429.91M434.164 172.625H429.91M434.164 141.316H429.91M434.164 110.004H429.91M434.164 78.691H429.91' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M82.148 203.937V78.691H434.164V203.937H82.148Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -11.54 33.989)'>
+<use x='114.487' xlink:href='#g2-99' y='183.949'/>
+<use x='118.25' xlink:href='#g2-102' y='183.949'/>
+<use x='120.838' xlink:href='#g2-114' y='183.949'/>
+<use x='123.73' xlink:href='#g2-97' y='183.949'/>
+<use x='127.798' xlink:href='#g2-99' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 41.401 33.989)'>
+<use x='114.487' xlink:href='#g2-101' y='183.949'/>
+<use x='118.25' xlink:href='#g2-115' y='183.949'/>
+<use x='121.497' xlink:href='#g2-112' y='183.949'/>
+<use x='125.634' xlink:href='#g2-114' y='183.949'/>
+<use x='128.526' xlink:href='#g2-101' y='183.949'/>
+<use x='132.29' xlink:href='#g2-115' y='183.949'/>
+<use x='135.536' xlink:href='#g2-115' y='183.949'/>
+<use x='138.782' xlink:href='#g2-111' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 103.095 33.989)'>
+<use x='114.487' xlink:href='#g2-98' y='183.949'/>
+<use x='118.859' xlink:href='#g2-97' y='183.949'/>
+<use x='122.692' xlink:href='#g2-114' y='183.949'/>
+<use x='125.584' xlink:href='#g2-110' y='183.949'/>
+<use x='129.957' xlink:href='#g2-101' y='183.949'/>
+<use x='133.72' xlink:href='#g2-115' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 162.903 33.989)'>
+<use x='114.487' xlink:href='#g2-108' y='183.949'/>
+<use x='116.507' xlink:href='#g2-101' y='183.949'/>
+<use x='120.271' xlink:href='#g2-97' y='183.949'/>
+<use x='124.339' xlink:href='#g2-110' y='183.949'/>
+<use x='128.711' xlink:href='#g2-78' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 223.526 33.989)'>
+<use x='114.487' xlink:href='#g2-114' y='183.949'/>
+<use x='117.379' xlink:href='#g2-101' y='183.949'/>
+<use x='121.142' xlink:href='#g2-100' y='183.949'/>
+<use x='125.515' xlink:href='#g2-105' y='183.949'/>
+<use x='127.535' xlink:href='#g2-115' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 277.054 33.989)'>
+<use x='114.487' xlink:href='#g2-108' y='183.949'/>
+<use x='116.507' xlink:href='#g2-97' y='183.949'/>
+<use x='120.34' xlink:href='#g2-114' y='183.949'/>
+<use x='123.232' xlink:href='#g2-115' y='183.949'/>
+<use x='126.478' xlink:href='#g2-111' y='183.949'/>
+<use x='130.712' xlink:href='#g2-110' y='183.949'/>
+<use x='135.085' xlink:href='#g2-78' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 21.624)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -9.688)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -40.999)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -72.311)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -103.622)'>
+<use x='114.487' xlink:href='#g1-50' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-120' y='183.949'/>
+</g>
+<path clip-path='url(#clip9)' d='M82.148 141.316H434.164' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M86.328 203.937H89.566V141.316H86.328ZM144.996 203.937H148.234V141.316H144.996ZM203.668 203.937H206.902V141.316H203.668ZM262.336 203.937H265.574V141.316H262.336ZM321.004 203.937H324.242V141.316H321.004ZM379.672 203.937H382.91V141.316H379.672Z' fill='#f0e0f0'/>
+<path clip-path='url(#clip9)' d='M86.328 203.937H89.566V141.316H86.328ZM144.996 203.937H148.234V141.316H144.996ZM203.668 203.937H206.902V141.316H203.668ZM262.336 203.937H265.574V141.316H262.336ZM321.004 203.937H324.242V141.316H321.004ZM379.672 203.937H382.91V141.316H379.672Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M87.949 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip9)' d='M85.953 141.316H89.938' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M87.949 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip9)' d='M85.953 141.316H89.938' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M146.617 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip9)' d='M144.625 141.316H148.61' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M146.617 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip9)' d='M144.625 141.316H148.61' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M205.285 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip9)' d='M203.293 141.316H207.277' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M205.285 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip9)' d='M203.293 141.316H207.277' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M263.953 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip9)' d='M261.961 141.316H265.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M263.953 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip9)' d='M261.961 141.316H265.945' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M322.625 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip9)' d='M320.629 141.316H324.617' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M322.625 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip9)' d='M320.629 141.316H324.617' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M381.293 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip9)' d='M379.301 141.316H383.285' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M381.293 141.316V141.316' fill='#f0e0f0'/>
+<path clip-path='url(#clip9)' d='M379.301 141.316H383.285' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M91.559 203.937H94.797V93.156H91.559ZM150.227 203.937H153.465V78.691H150.227ZM208.899 203.937H212.133V138.246H208.899ZM267.567 203.937H270.805V145.949H267.567ZM326.234 203.937H329.473V130.23H326.234ZM384.902 203.937H388.141V140.25H384.902Z' fill='#e1c2e1'/>
+<path clip-path='url(#clip9)' d='M91.559 203.937H94.797V93.156H91.559ZM150.227 203.937H153.465V78.691H150.227ZM208.899 203.937H212.133V138.246H208.899ZM267.567 203.937H270.805V145.949H267.567ZM326.234 203.937H329.473V130.23H326.234ZM384.902 203.937H388.141V140.25H384.902Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M93.18 93.156V93.156' fill='#e1c2e1'/>
+<path clip-path='url(#clip9)' d='M91.184 93.156H95.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M93.18 93.156V93.156' fill='#e1c2e1'/>
+<path clip-path='url(#clip9)' d='M91.184 93.156H95.168' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M151.848 78.691V78.691' fill='#e1c2e1'/>
+<path clip-path='url(#clip9)' d='M149.855 78.691H153.84' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M151.848 78.691V78.691' fill='#e1c2e1'/>
+<path clip-path='url(#clip9)' d='M149.855 78.691H153.84' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M210.516 138.246V138.246' fill='#e1c2e1'/>
+<path clip-path='url(#clip9)' d='M208.523 138.246H212.507' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M210.516 138.246V138.246' fill='#e1c2e1'/>
+<path clip-path='url(#clip9)' d='M208.523 138.246H212.507' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M269.184 145.949V145.949' fill='#e1c2e1'/>
+<path clip-path='url(#clip9)' d='M267.191 145.949H271.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M269.184 145.949V145.949' fill='#e1c2e1'/>
+<path clip-path='url(#clip9)' d='M267.191 145.949H271.175' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M327.856 130.23V130.23' fill='#e1c2e1'/>
+<path clip-path='url(#clip9)' d='M325.859 130.23H329.847' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M327.856 130.23V130.23' fill='#e1c2e1'/>
+<path clip-path='url(#clip9)' d='M325.859 130.23H329.847' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M386.524 140.25V140.25' fill='#e1c2e1'/>
+<path clip-path='url(#clip9)' d='M384.531 140.25H388.515' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M386.524 140.25V140.25' fill='#e1c2e1'/>
+<path clip-path='url(#clip9)' d='M384.531 140.25H388.515' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M96.789 203.937H100.027V110.816H96.789ZM155.457 203.937H158.695V104.679H155.457ZM214.129 203.937H217.363V139.75H214.129ZM272.797 203.937H276.035V141.254H272.797ZM331.465 203.937H334.703V137.496H331.465ZM390.133 203.937H393.371V132.109H390.133Z' fill='#d1a3d1'/>
+<path clip-path='url(#clip9)' d='M96.789 203.937H100.027V110.816H96.789ZM155.457 203.937H158.695V104.679H155.457ZM214.129 203.937H217.363V139.75H214.129ZM272.797 203.937H276.035V141.254H272.797ZM331.465 203.937H334.703V137.496H331.465ZM390.133 203.937H393.371V132.109H390.133Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M98.41 110.816V110.816' fill='#d1a3d1'/>
+<path clip-path='url(#clip9)' d='M96.414 110.817H100.399' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M98.41 110.816V110.816' fill='#d1a3d1'/>
+<path clip-path='url(#clip9)' d='M96.414 110.817H100.399' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M157.078 104.679V104.679' fill='#d1a3d1'/>
+<path clip-path='url(#clip9)' d='M155.086 104.68H159.071' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M157.078 104.679V104.679' fill='#d1a3d1'/>
+<path clip-path='url(#clip9)' d='M155.086 104.68H159.071' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M215.746 139.75V139.75' fill='#d1a3d1'/>
+<path clip-path='url(#clip9)' d='M213.754 139.75H217.739' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M215.746 139.75V139.75' fill='#d1a3d1'/>
+<path clip-path='url(#clip9)' d='M213.754 139.75H217.739' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M274.414 141.254V141.254' fill='#d1a3d1'/>
+<path clip-path='url(#clip9)' d='M272.422 141.254H276.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M274.414 141.254V141.254' fill='#d1a3d1'/>
+<path clip-path='url(#clip9)' d='M272.422 141.254H276.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M333.086 137.496V137.496' fill='#d1a3d1'/>
+<path clip-path='url(#clip9)' d='M331.09 137.496H335.078' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M333.086 137.496V137.496' fill='#d1a3d1'/>
+<path clip-path='url(#clip9)' d='M331.09 137.496H335.078' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M391.754 132.109V132.109' fill='#d1a3d1'/>
+<path clip-path='url(#clip9)' d='M389.762 132.11H393.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M391.754 132.109V132.109' fill='#d1a3d1'/>
+<path clip-path='url(#clip9)' d='M389.762 132.11H393.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M102.02 203.937H105.258V121.777H102.02ZM160.688 203.937H163.926V110.066H160.688ZM219.359 203.937H222.594V140.312H219.359ZM278.027 203.937H281.266V140.437H278.027ZM336.695 203.937H339.934V138.058H336.695ZM395.363 203.937H398.602V125.16H395.363Z' fill='#c285c2'/>
+<path clip-path='url(#clip9)' d='M102.02 203.937H105.258V121.777H102.02ZM160.688 203.937H163.926V110.066H160.688ZM219.359 203.937H222.594V140.312H219.359ZM278.027 203.937H281.266V140.437H278.027ZM336.695 203.937H339.934V138.058H336.695ZM395.363 203.937H398.602V125.16H395.363Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M103.641 121.777V121.777' fill='#c285c2'/>
+<path clip-path='url(#clip9)' d='M101.644 121.777H105.629' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M103.641 121.777V121.777' fill='#c285c2'/>
+<path clip-path='url(#clip9)' d='M101.644 121.777H105.629' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M162.309 110.066V110.066' fill='#c285c2'/>
+<path clip-path='url(#clip9)' d='M160.316 110.066H164.301' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M162.309 110.066V110.066' fill='#c285c2'/>
+<path clip-path='url(#clip9)' d='M160.316 110.066H164.301' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M220.977 140.312V140.312' fill='#c285c2'/>
+<path clip-path='url(#clip9)' d='M218.984 140.312H222.969' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M220.977 140.312V140.312' fill='#c285c2'/>
+<path clip-path='url(#clip9)' d='M218.984 140.312H222.969' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M279.645 140.437V140.437' fill='#c285c2'/>
+<path clip-path='url(#clip9)' d='M277.652 140.438H281.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M279.645 140.437V140.437' fill='#c285c2'/>
+<path clip-path='url(#clip9)' d='M277.652 140.438H281.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M338.317 138.058V138.058' fill='#c285c2'/>
+<path clip-path='url(#clip9)' d='M336.32 138.059H340.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M338.317 138.058V138.058' fill='#c285c2'/>
+<path clip-path='url(#clip9)' d='M336.32 138.059H340.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M396.984 125.16V125.16' fill='#c285c2'/>
+<path clip-path='url(#clip9)' d='M394.992 125.161H398.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M396.984 125.16V125.16' fill='#c285c2'/>
+<path clip-path='url(#clip9)' d='M394.992 125.161H398.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M107.25 203.937H110.488V138.433H107.25ZM165.918 203.937H169.156V141.754H165.918ZM224.59 203.937H227.824V141.379H224.59ZM283.258 203.937H286.496V131.172H283.258ZM341.926 203.937H345.164V140.5H341.926ZM400.594 203.937H403.832V134.238H400.594Z' fill='#b366b3'/>
+<path clip-path='url(#clip9)' d='M107.25 203.937H110.488V138.433H107.25ZM165.918 203.937H169.156V141.754H165.918ZM224.59 203.937H227.824V141.379H224.59ZM283.258 203.937H286.496V131.172H283.258ZM341.926 203.937H345.164V140.5H341.926ZM400.594 203.937H403.832V134.238H400.594Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M108.871 138.433V138.433' fill='#b366b3'/>
+<path clip-path='url(#clip9)' d='M106.875 138.434H110.86' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M108.871 138.433V138.433' fill='#b366b3'/>
+<path clip-path='url(#clip9)' d='M106.875 138.434H110.86' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M167.539 141.754V141.754' fill='#b366b3'/>
+<path clip-path='url(#clip9)' d='M165.547 141.754H169.532' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M167.539 141.754V141.754' fill='#b366b3'/>
+<path clip-path='url(#clip9)' d='M165.547 141.754H169.532' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M226.207 141.379V141.379' fill='#b366b3'/>
+<path clip-path='url(#clip9)' d='M224.215 141.379H228.2' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M226.207 141.379V141.379' fill='#b366b3'/>
+<path clip-path='url(#clip9)' d='M224.215 141.379H228.2' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M284.875 131.172V131.172' fill='#b366b3'/>
+<path clip-path='url(#clip9)' d='M282.883 131.172H286.868' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M284.875 131.172V131.172' fill='#b366b3'/>
+<path clip-path='url(#clip9)' d='M282.883 131.172H286.868' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M343.547 140.5V140.5' fill='#b366b3'/>
+<path clip-path='url(#clip9)' d='M341.551 140.5H345.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M343.547 140.5V140.5' fill='#b366b3'/>
+<path clip-path='url(#clip9)' d='M341.551 140.5H345.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M402.215 134.238V134.238' fill='#b366b3'/>
+<path clip-path='url(#clip9)' d='M400.223 134.238H404.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M402.215 134.238V134.238' fill='#b366b3'/>
+<path clip-path='url(#clip9)' d='M400.223 134.238H404.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M112.481 203.937H115.719V108.687H112.481ZM171.149 203.937H174.387V78.691H171.149ZM229.82 203.937H233.055V139.812H229.82ZM288.488 203.937H291.727V125.785H288.488ZM347.156 203.937H350.395V125.41H347.156ZM405.824 203.937H409.063V131.672H405.824Z' fill='#a447a4'/>
+<path clip-path='url(#clip9)' d='M112.481 203.937H115.719V108.687H112.481ZM171.149 203.937H174.387V78.691H171.149ZM229.82 203.937H233.055V139.812H229.82ZM288.488 203.937H291.727V125.785H288.488ZM347.156 203.937H350.395V125.41H347.156ZM405.824 203.937H409.063V131.672H405.824Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M114.098 108.687V108.687' fill='#a447a4'/>
+<path clip-path='url(#clip9)' d='M112.105 108.688H116.09' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M114.098 108.687V108.687' fill='#a447a4'/>
+<path clip-path='url(#clip9)' d='M112.105 108.688H116.09' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M172.77 78.691V78.691' fill='#a447a4'/>
+<path clip-path='url(#clip9)' d='M170.777 78.691H174.762' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M172.77 78.691V78.691' fill='#a447a4'/>
+<path clip-path='url(#clip9)' d='M170.777 78.691H174.762' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M231.438 139.812V139.812' fill='#a447a4'/>
+<path clip-path='url(#clip9)' d='M229.445 139.812H233.43' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M231.438 139.812V139.812' fill='#a447a4'/>
+<path clip-path='url(#clip9)' d='M229.445 139.812H233.43' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M290.106 125.785V125.785' fill='#a447a4'/>
+<path clip-path='url(#clip9)' d='M288.113 125.785H292.098' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M290.106 125.785V125.785' fill='#a447a4'/>
+<path clip-path='url(#clip9)' d='M288.113 125.785H292.098' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M348.777 125.41V125.41' fill='#a447a4'/>
+<path clip-path='url(#clip9)' d='M346.781 125.41H350.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M348.777 125.41V125.41' fill='#a447a4'/>
+<path clip-path='url(#clip9)' d='M346.781 125.41H350.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M407.445 131.672V131.672' fill='#a447a4'/>
+<path clip-path='url(#clip9)' d='M405.453 131.672H409.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M407.445 131.672V131.672' fill='#a447a4'/>
+<path clip-path='url(#clip9)' d='M405.453 131.672H409.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M117.711 203.937H120.949V141.316H117.711ZM176.379 203.937H179.617V153.402H176.379ZM235.051 203.937H238.285V141.566H235.051ZM293.719 203.937H296.957V131.922H293.719ZM352.387 203.937H355.625V140.562H352.387ZM411.055 203.937H414.293V134.613H411.055Z' fill='#942994'/>
+<path clip-path='url(#clip9)' d='M117.711 203.937H120.949V141.316H117.711ZM176.379 203.937H179.617V153.402H176.379ZM235.051 203.937H238.285V141.566H235.051ZM293.719 203.937H296.957V131.922H293.719ZM352.387 203.937H355.625V140.562H352.387ZM411.055 203.937H414.293V134.613H411.055Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M119.328 141.316V141.316' fill='#942994'/>
+<path clip-path='url(#clip9)' d='M117.336 141.316H121.321' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M119.328 141.316V141.316' fill='#942994'/>
+<path clip-path='url(#clip9)' d='M117.336 141.316H121.321' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M178 153.402V153.402' fill='#942994'/>
+<path clip-path='url(#clip9)' d='M176.008 153.403H179.993' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M178 153.402V153.402' fill='#942994'/>
+<path clip-path='url(#clip9)' d='M176.008 153.403H179.993' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M236.668 141.566V141.566' fill='#942994'/>
+<path clip-path='url(#clip9)' d='M234.676 141.567H238.661' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M236.668 141.566V141.566' fill='#942994'/>
+<path clip-path='url(#clip9)' d='M234.676 141.567H238.661' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M295.336 131.922V131.922' fill='#942994'/>
+<path clip-path='url(#clip9)' d='M293.344 131.922H297.329' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M295.336 131.922V131.922' fill='#942994'/>
+<path clip-path='url(#clip9)' d='M293.344 131.922H297.329' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M354.008 140.562V140.562' fill='#942994'/>
+<path clip-path='url(#clip9)' d='M352.012 140.563H356' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M354.008 140.562V140.562' fill='#942994'/>
+<path clip-path='url(#clip9)' d='M352.012 140.563H356' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M412.676 134.613V134.613' fill='#942994'/>
+<path clip-path='url(#clip9)' d='M410.684 134.614H414.668' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M412.676 134.613V134.613' fill='#942994'/>
+<path clip-path='url(#clip9)' d='M410.684 134.614H414.668' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M122.941 203.937H126.18V116.765H122.941ZM181.609 203.937H184.848V112.32H181.609ZM240.281 203.937H243.516V140.125H240.281ZM298.949 203.937H302.188V131.734H298.949ZM357.617 203.937H360.856V97.168H357.617ZM416.285 203.937H419.524V136.804H416.285Z' fill='#850a85'/>
+<path clip-path='url(#clip9)' d='M122.941 203.937H126.18V116.765H122.941ZM181.609 203.937H184.848V112.32H181.609ZM240.281 203.937H243.516V140.125H240.281ZM298.949 203.937H302.188V131.734H298.949ZM357.617 203.937H360.856V97.168H357.617ZM416.285 203.937H419.524V136.804H416.285Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M124.559 116.765V116.765' fill='#850a85'/>
+<path clip-path='url(#clip9)' d='M122.566 116.766H126.551' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M124.559 116.765V116.765' fill='#850a85'/>
+<path clip-path='url(#clip9)' d='M122.566 116.766H126.551' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M183.231 112.32V112.32' fill='#850a85'/>
+<path clip-path='url(#clip9)' d='M181.238 112.32H185.223' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M183.231 112.32V112.32' fill='#850a85'/>
+<path clip-path='url(#clip9)' d='M181.238 112.32H185.223' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M241.899 140.125V140.125' fill='#850a85'/>
+<path clip-path='url(#clip9)' d='M239.906 140.125H243.891' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M241.899 140.125V140.125' fill='#850a85'/>
+<path clip-path='url(#clip9)' d='M239.906 140.125H243.891' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M300.567 131.734V131.734' fill='#850a85'/>
+<path clip-path='url(#clip9)' d='M298.574 131.734H302.559' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M300.567 131.734V131.734' fill='#850a85'/>
+<path clip-path='url(#clip9)' d='M298.574 131.734H302.559' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M359.238 97.168V97.168' fill='#850a85'/>
+<path clip-path='url(#clip9)' d='M357.242 97.168H361.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M359.238 97.168V97.168' fill='#850a85'/>
+<path clip-path='url(#clip9)' d='M357.242 97.168H361.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M417.906 136.804V136.804' fill='#850a85'/>
+<path clip-path='url(#clip9)' d='M415.914 136.804H419.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M417.906 136.804V136.804' fill='#850a85'/>
+<path clip-path='url(#clip9)' d='M415.914 136.804H419.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M128.172 203.937H131.41V104.992H128.172ZM186.84 203.937H190.078V90.09H186.84ZM245.512 203.937H248.746V133.238H245.512ZM362.848 203.937H366.086V124.906H362.848ZM421.516 203.937H424.754V148.453H421.516Z' fill='#760076'/>
+<path clip-path='url(#clip9)' d='M128.172 203.937H131.41V104.992H128.172ZM186.84 203.937H190.078V90.09H186.84ZM245.512 203.937H248.746V133.238H245.512ZM362.848 203.937H366.086V124.906H362.848ZM421.516 203.937H424.754V148.453H421.516Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M129.789 104.992V104.992' fill='#760076'/>
+<path clip-path='url(#clip9)' d='M127.797 104.992H131.782' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M129.789 104.992V104.992' fill='#760076'/>
+<path clip-path='url(#clip9)' d='M127.797 104.992H131.782' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M188.461 90.09V90.09' fill='#760076'/>
+<path clip-path='url(#clip9)' d='M186.469 90.09H190.454' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M188.461 90.09V90.09' fill='#760076'/>
+<path clip-path='url(#clip9)' d='M186.469 90.09H190.454' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M247.129 133.238V133.238' fill='#760076'/>
+<path clip-path='url(#clip9)' d='M245.137 133.238H249.122' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M247.129 133.238V133.238' fill='#760076'/>
+<path clip-path='url(#clip9)' d='M245.137 133.238H249.122' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M364.469 124.906V124.906' fill='#760076'/>
+<path clip-path='url(#clip9)' d='M362.473 124.906H366.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M364.469 124.906V124.906' fill='#760076'/>
+<path clip-path='url(#clip9)' d='M362.473 124.906H366.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M423.137 148.453V148.453' fill='#760076'/>
+<path clip-path='url(#clip9)' d='M421.144 148.453H425.128' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M423.137 148.453V148.453' fill='#760076'/>
+<path clip-path='url(#clip9)' d='M421.144 148.453H425.128' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M133.402 203.937H136.641V138.746H133.402ZM192.07 203.937H195.309V107.058H192.07ZM250.742 203.937H253.977V141.316H250.742ZM309.41 203.937H312.649V129.355H309.41ZM368.078 203.937H371.317V140.125H368.078ZM426.746 203.937H429.984V140.375H426.746Z' fill='#670067'/>
+<path clip-path='url(#clip9)' d='M133.402 203.937H136.641V138.746H133.402ZM192.07 203.937H195.309V107.058H192.07ZM250.742 203.937H253.977V141.316H250.742ZM309.41 203.937H312.649V129.355H309.41ZM368.078 203.937H371.317V140.125H368.078ZM426.746 203.937H429.984V140.375H426.746Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M135.02 138.746V138.746' fill='#670067'/>
+<path clip-path='url(#clip9)' d='M133.027 138.746H137.012' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M135.02 138.746V138.746' fill='#670067'/>
+<path clip-path='url(#clip9)' d='M133.027 138.746H137.012' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M193.692 107.058V107.058' fill='#670067'/>
+<path clip-path='url(#clip9)' d='M191.699 107.058H195.684' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M193.692 107.058V107.058' fill='#670067'/>
+<path clip-path='url(#clip9)' d='M191.699 107.058H195.684' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M252.359 141.316V141.316' fill='#670067'/>
+<path clip-path='url(#clip9)' d='M250.367 141.316H254.352' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M252.359 141.316V141.316' fill='#670067'/>
+<path clip-path='url(#clip9)' d='M250.367 141.316H254.352' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M311.027 129.355V129.355' fill='#670067'/>
+<path clip-path='url(#clip9)' d='M309.035 129.355H313.02' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M311.027 129.355V129.355' fill='#670067'/>
+<path clip-path='url(#clip9)' d='M309.035 129.355H313.02' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M369.699 140.125V140.125' fill='#670067'/>
+<path clip-path='url(#clip9)' d='M367.703 140.125H371.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M369.699 140.125V140.125' fill='#670067'/>
+<path clip-path='url(#clip9)' d='M367.703 140.125H371.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M428.367 140.375V140.375' fill='#670067'/>
+<path clip-path='url(#clip9)' d='M426.375 140.375H430.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip9)' d='M428.367 140.375V140.375' fill='#670067'/>
+<path clip-path='url(#clip9)' d='M426.375 140.375H430.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(0 -1 1 0 -94.367 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -35.698 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 22.971 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 81.64 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 140.309 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 198.978 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -89.137 201.62)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-55' y='183.949'/>
+<use x='121.25' xlink:href='#g1-55' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -30.468 187.155)'>
+<use x='109.598' xlink:href='#g3-1' y='183.949'/>
+<use x='113.103' xlink:href='#g3-1' y='183.949'/>
+<use x='116.608' xlink:href='#g3-1' y='183.949'/>
+<use x='120.114' xlink:href='#g1-50' y='183.949'/>
+<use x='122.76' xlink:href='#g1-46' y='183.949'/>
+<use x='124.23' xlink:href='#g1-49' y='183.949'/>
+<use x='126.877' xlink:href='#g1-52' y='183.949'/>
+<use x='129.523' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 28.201 246.709)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 86.87 254.412)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-57' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 145.539 238.693)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 204.208 248.713)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -83.906 219.28)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-52' y='183.949'/>
+<use x='121.25' xlink:href='#g1-57' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -25.237 213.143)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-57' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 33.432 248.212)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 92.101 249.715)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 150.77 245.958)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 209.439 240.572)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -78.676 230.239)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-51' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -20.007 218.529)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 38.662 248.776)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 97.331 248.901)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 156 246.521)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 214.669 233.621)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -73.445 246.897)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -14.776 250.216)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-57' y='183.949'/>
+<use x='121.25' xlink:href='#g1-57' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 43.893 249.84)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 102.562 239.633)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 161.231 248.963)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 219.9 242.701)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -68.215 217.151)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -9.546 187.155)'>
+<use x='109.598' xlink:href='#g3-1' y='183.949'/>
+<use x='113.103' xlink:href='#g3-1' y='183.949'/>
+<use x='116.608' xlink:href='#g3-1' y='183.949'/>
+<use x='120.114' xlink:href='#g1-52' y='183.949'/>
+<use x='122.76' xlink:href='#g1-46' y='183.949'/>
+<use x='124.23' xlink:href='#g1-53' y='183.949'/>
+<use x='126.877' xlink:href='#g1-50' y='183.949'/>
+<use x='129.523' xlink:href='#g1-120' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 49.123 248.275)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 107.792 234.247)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 166.461 233.871)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 225.13 240.134)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -62.984 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -4.315 261.864)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-56' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 54.354 250.028)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 113.023 240.384)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 171.692 249.026)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 230.361 243.077)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -57.754 225.229)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-51' y='183.949'/>
+<use x='121.25' xlink:href='#g1-57' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 0.915 220.783)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-52' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 59.584 248.588)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 118.253 240.196)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 176.922 205.628)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-55' y='183.949'/>
+<use x='121.25' xlink:href='#g1-49' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 235.591 245.269)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-55' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -52.524 213.456)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-56' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 6.145 198.552)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-56' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 64.814 241.699)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-51' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 182.152 233.37)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-50' y='183.949'/>
+<use x='121.25' xlink:href='#g1-54' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 240.821 256.917)'>
+<use x='114.487' xlink:href='#g1-48' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-56' y='183.949'/>
+<use x='121.25' xlink:href='#g1-57' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -47.293 247.21)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-52' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 11.376 215.523)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-53' y='183.949'/>
+<use x='121.25' xlink:href='#g1-53' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 70.045 249.778)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-48' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 128.714 237.817)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-49' y='183.949'/>
+<use x='121.25' xlink:href='#g1-57' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 187.383 248.588)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 246.052 248.838)'>
+<use x='114.487' xlink:href='#g1-49' y='183.949'/>
+<use x='117.133' xlink:href='#g1-46' y='183.949'/>
+<use x='118.603' xlink:href='#g1-48' y='183.949'/>
+<use x='121.25' xlink:href='#g1-50' y='183.949'/>
+</g>
+<g transform='matrix(0 -1 1 0 -124.589 307.697)'>
+<use x='114.487' xlink:href='#g0-82' y='183.949'/>
+<use x='120.457' xlink:href='#g0-101' y='183.949'/>
+<use x='124.553' xlink:href='#g0-108' y='183.949'/>
+<use x='126.753' xlink:href='#g0-97' y='183.949'/>
+<use x='131.181' xlink:href='#g0-116' y='183.949'/>
+<use x='134.509' xlink:href='#g0-105' y='183.949'/>
+<use x='136.709' xlink:href='#g0-118' y='183.949'/>
+<use x='140.957' xlink:href='#g0-101' y='183.949'/>
+<use x='148.124' xlink:href='#g0-114' y='183.949'/>
+<use x='151.272' xlink:href='#g0-115' y='183.949'/>
+<use x='154.805' xlink:href='#g0-115' y='183.949'/>
+<use x='161.409' xlink:href='#g2-40' y='183.949'/>
+<use x='164.702' xlink:href='#g2-108' y='183.949'/>
+<use x='166.722' xlink:href='#g2-111' y='183.949'/>
+<use x='170.721' xlink:href='#g2-119' y='183.949'/>
+<use x='176.27' xlink:href='#g2-101' y='183.949'/>
+<use x='180.034' xlink:href='#g2-114' y='183.949'/>
+<use x='185.749' xlink:href='#g2-105' y='183.949'/>
+<use x='187.769' xlink:href='#g2-115' y='183.949'/>
+<use x='193.838' xlink:href='#g2-98' y='183.949'/>
+<use x='198.446' xlink:href='#g2-101' y='183.949'/>
+<use x='202.209' xlink:href='#g2-116' y='183.949'/>
+<use x='205.267' xlink:href='#g2-116' y='183.949'/>
+<use x='208.325' xlink:href='#g2-101' y='183.949'/>
+<use x='212.089' xlink:href='#g2-114' y='183.949'/>
+<use x='214.981' xlink:href='#g2-41' y='183.949'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- This file was generated by dvisvgm 2.4.2 -->
+<svg height='182.025pt' version='1.1' viewBox='106.736 51.674 381.623 182.025' width='381.623pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<clipPath id='clip10'>
+<path d='M135.949 186.074H487.961V60.828H135.949Z'/>
+</clipPath>
+<use id='g2-40' transform='scale(1.6)' xlink:href='#g1-40'/>
+<use id='g2-41' transform='scale(1.6)' xlink:href='#g1-41'/>
+<use id='g2-45' transform='scale(1.6)' xlink:href='#g1-45'/>
+<use id='g2-49' transform='scale(1.6)' xlink:href='#g1-49'/>
+<use id='g2-54' transform='scale(1.6)' xlink:href='#g1-54'/>
+<use id='g2-56' transform='scale(1.6)' xlink:href='#g1-56'/>
+<use id='g2-78' transform='scale(1.6)' xlink:href='#g1-78'/>
+<use id='g2-97' transform='scale(1.6)' xlink:href='#g1-97'/>
+<use id='g2-98' transform='scale(1.6)' xlink:href='#g1-98'/>
+<use id='g2-99' transform='scale(1.6)' xlink:href='#g1-99'/>
+<use id='g2-100' transform='scale(1.6)' xlink:href='#g1-100'/>
+<use id='g2-101' transform='scale(1.6)' xlink:href='#g1-101'/>
+<use id='g2-103' transform='scale(1.6)' xlink:href='#g1-103'/>
+<use id='g2-104' transform='scale(1.6)' xlink:href='#g1-104'/>
+<use id='g2-105' transform='scale(1.6)' xlink:href='#g1-105'/>
+<use id='g2-106' transform='scale(1.6)' xlink:href='#g1-106'/>
+<use id='g2-108' transform='scale(1.6)' xlink:href='#g1-108'/>
+<use id='g2-109' transform='scale(1.6)' xlink:href='#g1-109'/>
+<use id='g2-110' transform='scale(1.6)' xlink:href='#g1-110'/>
+<use id='g2-111' transform='scale(1.6)' xlink:href='#g1-111'/>
+<use id='g2-112' transform='scale(1.6)' xlink:href='#g1-112'/>
+<use id='g2-114' transform='scale(1.6)' xlink:href='#g1-114'/>
+<use id='g2-115' transform='scale(1.6)' xlink:href='#g1-115'/>
+<use id='g2-116' transform='scale(1.6)' xlink:href='#g1-116'/>
+<use id='g2-119' transform='scale(1.6)' xlink:href='#g1-119'/>
+<use id='g2-120' transform='scale(1.6)' xlink:href='#g1-120'/>
+<path d='M1.519 -3.736C1.435 -3.736 1.425 -3.736 1.365 -3.681C0.737 -3.133 0.418 -2.247 0.418 -1.245C0.418 -0.304 0.702 0.603 1.36 1.181C1.425 1.245 1.435 1.245 1.519 1.245H1.758C1.743 1.235 1.26 0.822 1.031 0.045C0.912 -0.344 0.852 -0.752 0.852 -1.245C0.852 -2.969 1.659 -3.651 1.758 -3.736H1.519Z' id='g1-40'/>
+<path d='M0.533 1.245C0.618 1.245 0.628 1.245 0.687 1.191C1.315 0.643 1.634 -0.244 1.634 -1.245C1.634 -2.187 1.35 -3.093 0.692 -3.671C0.628 -3.736 0.618 -3.736 0.533 -3.736H0.294C0.309 -3.726 0.792 -3.313 1.021 -2.535C1.141 -2.147 1.2 -1.738 1.2 -1.245C1.2 0.478 0.394 1.161 0.294 1.245H0.533Z' id='g1-41'/>
+<path d='M0.956 -0.005V-0.448H0.508V0H0.648L0.503 0.638H0.727L0.956 -0.005Z' id='g1-44'/>
+<path d='M1.465 -0.951V-1.265H0.06V-0.951H1.465Z' id='g1-45'/>
+<path d='M0.956 -0.448H0.508V0H0.956V-0.448Z' id='g1-46'/>
+<path d='M2.431 -1.619C2.431 -1.858 2.431 -2.441 2.197 -2.849C1.943 -3.298 1.559 -3.372 1.32 -3.372C1.096 -3.372 0.707 -3.303 0.458 -2.874C0.219 -2.476 0.209 -1.933 0.209 -1.619C0.209 -1.25 0.229 -0.797 0.438 -0.418C0.658 -0.015 1.026 0.105 1.32 0.105C1.818 0.105 2.092 -0.184 2.242 -0.498C2.416 -0.852 2.431 -1.31 2.431 -1.619ZM1.32 -0.224C1.111 -0.224 0.872 -0.344 0.747 -0.702C0.648 -1.006 0.643 -1.32 0.643 -1.684C0.643 -2.142 0.643 -3.044 1.32 -3.044S1.998 -2.142 1.998 -1.684C1.998 -1.355 1.998 -0.981 1.878 -0.663C1.738 -0.304 1.484 -0.224 1.32 -0.224Z' id='g1-48'/>
+<path d='M1.599 -3.372H1.489C1.166 -3.073 0.757 -3.054 0.458 -3.044V-2.73C0.653 -2.735 0.902 -2.745 1.151 -2.844V-0.314H0.488V0H2.262V-0.314H1.599V-3.372Z' id='g1-49'/>
+<path d='M1.41 -0.384C1.35 -0.384 1.29 -0.379 1.23 -0.379H0.663L1.435 -1.061C1.524 -1.141 1.768 -1.325 1.863 -1.405C2.082 -1.604 2.376 -1.863 2.376 -2.296C2.376 -2.859 1.958 -3.372 1.245 -3.372C0.717 -3.372 0.389 -3.088 0.219 -2.58L0.453 -2.286C0.568 -2.705 0.742 -3.029 1.176 -3.029C1.594 -3.029 1.913 -2.735 1.913 -2.286C1.913 -1.873 1.669 -1.639 1.37 -1.355C1.27 -1.255 1.001 -1.031 0.897 -0.932C0.752 -0.802 0.408 -0.468 0.264 -0.344V0H2.376V-0.384H1.41Z' id='g1-50'/>
+<path d='M0.498 -2.555C0.702 -2.954 1.061 -3.064 1.3 -3.064C1.594 -3.064 1.813 -2.894 1.813 -2.61C1.813 -2.346 1.634 -2.022 1.255 -1.958C1.23 -1.953 1.21 -1.953 0.882 -1.928V-1.599H1.27C1.743 -1.599 1.918 -1.225 1.918 -0.912C1.918 -0.523 1.679 -0.224 1.29 -0.224C0.936 -0.224 0.533 -0.394 0.284 -0.712L0.219 -0.389C0.508 -0.065 0.912 0.105 1.3 0.105C1.953 0.105 2.421 -0.384 2.421 -0.907C2.421 -1.315 2.092 -1.644 1.699 -1.758C2.077 -1.953 2.271 -2.286 2.271 -2.61C2.271 -3.034 1.838 -3.372 1.305 -3.372C0.867 -3.372 0.503 -3.143 0.294 -2.844L0.498 -2.555Z' id='g1-51'/>
+<path d='M1.973 -0.832H2.491V-1.161H1.973V-3.268H1.479L0.149 -1.161V-0.832H1.544V0H1.973V-0.832ZM0.573 -1.161C0.722 -1.395 1.579 -2.725 1.579 -3.024V-1.161H0.573Z' id='g1-52'/>
+<path d='M0.817 -2.924H2.197V-3.268H0.418V-1.405H0.782C0.902 -1.674 1.136 -1.793 1.36 -1.793C1.564 -1.793 1.873 -1.654 1.873 -1.021C1.873 -0.369 1.46 -0.224 1.205 -0.224C0.877 -0.224 0.558 -0.399 0.389 -0.682L0.199 -0.384C0.443 -0.08 0.812 0.105 1.205 0.105C1.863 0.105 2.376 -0.403 2.376 -1.011C2.376 -1.629 1.918 -2.122 1.37 -2.122C1.156 -2.122 0.966 -2.047 0.817 -1.923V-2.924Z' id='g1-53'/>
+<path d='M2.187 -3.273C1.918 -3.372 1.729 -3.372 1.634 -3.372C0.877 -3.372 0.219 -2.66 0.219 -1.609C0.219 -0.259 0.827 0.105 1.33 0.105C1.733 0.105 1.943 -0.085 2.097 -0.244C2.416 -0.583 2.421 -0.936 2.421 -1.111C2.421 -1.763 2.067 -2.306 1.584 -2.306C1.096 -2.306 0.832 -2.052 0.687 -1.908C0.752 -2.59 1.101 -3.064 1.639 -3.064C1.738 -3.064 1.938 -3.054 2.187 -2.959V-3.273ZM0.692 -1.096C0.692 -1.126 0.692 -1.2 0.697 -1.225C0.697 -1.564 0.912 -1.978 1.355 -1.978C1.963 -1.978 1.963 -1.28 1.963 -1.111C1.963 -0.922 1.963 -0.712 1.828 -0.503C1.709 -0.324 1.559 -0.224 1.33 -0.224C0.802 -0.224 0.717 -0.877 0.692 -1.096Z' id='g1-54'/>
+<path d='M1.23 -2.884C1.29 -2.884 1.35 -2.889 1.41 -2.889H2.037C1.28 -2.147 0.797 -1.106 0.797 0.05H1.265C1.265 -1.405 1.973 -2.451 2.421 -2.919V-3.268H0.219V-2.884H1.23Z' id='g1-55'/>
+<path d='M1.704 -1.763C2.032 -1.868 2.346 -2.132 2.346 -2.501C2.346 -2.954 1.913 -3.372 1.32 -3.372S0.294 -2.954 0.294 -2.501C0.294 -2.127 0.618 -1.863 0.936 -1.763C0.498 -1.629 0.219 -1.29 0.219 -0.907C0.219 -0.374 0.692 0.105 1.32 0.105S2.421 -0.374 2.421 -0.907C2.421 -1.29 2.137 -1.629 1.704 -1.763ZM1.32 -1.928C0.966 -1.928 0.677 -2.132 0.677 -2.496C0.677 -2.814 0.902 -3.064 1.32 -3.064C1.733 -3.064 1.963 -2.814 1.963 -2.496C1.963 -2.142 1.684 -1.928 1.32 -1.928ZM1.32 -0.224C0.976 -0.224 0.672 -0.443 0.672 -0.912C0.672 -1.36 0.961 -1.599 1.32 -1.599S1.968 -1.355 1.968 -0.912C1.968 -0.443 1.659 -0.224 1.32 -0.224Z' id='g1-56'/>
+<path d='M0.384 -0.125C0.628 0.055 0.852 0.105 1.086 0.105C1.783 0.105 2.421 -0.598 2.421 -1.669C2.421 -3.029 1.818 -3.372 1.34 -3.372C0.897 -3.372 0.692 -3.163 0.548 -3.019C0.229 -2.695 0.219 -2.351 0.219 -2.157C0.219 -1.514 0.568 -0.961 1.056 -0.961C1.619 -0.961 1.928 -1.335 1.953 -1.365C1.883 -0.573 1.494 -0.224 1.086 -0.224C0.827 -0.224 0.667 -0.319 0.553 -0.413L0.384 -0.125ZM1.938 -2.162C1.943 -2.132 1.943 -2.082 1.943 -2.052C1.943 -1.684 1.719 -1.29 1.285 -1.29C1.096 -1.29 0.946 -1.345 0.817 -1.549C0.687 -1.743 0.677 -1.933 0.677 -2.157C0.677 -2.351 0.677 -2.575 0.832 -2.795C0.936 -2.944 1.086 -3.064 1.335 -3.064C1.818 -3.064 1.923 -2.481 1.938 -2.162Z' id='g1-57'/>
+<path d='M2.959 -0.438C2.884 -0.438 2.874 -0.438 2.834 -0.418C2.59 -0.334 2.336 -0.279 2.072 -0.279C1.27 -0.279 0.697 -0.956 0.697 -1.729C0.697 -2.565 1.345 -3.178 2.042 -3.178C2.182 -3.178 2.511 -3.143 2.675 -2.745C2.535 -2.824 2.381 -2.859 2.252 -2.859C1.719 -2.859 1.27 -2.361 1.27 -1.729C1.27 -1.081 1.733 -0.598 2.247 -0.598C2.635 -0.598 3.228 -0.912 3.228 -1.798C3.228 -2.301 3.193 -3.507 2.047 -3.507C1.101 -3.507 0.294 -2.725 0.294 -1.729C0.294 -0.742 1.091 0.05 2.052 0.05C2.511 0.05 2.939 -0.139 3.228 -0.438H2.959ZM2.252 -0.927C1.943 -0.927 1.674 -1.27 1.674 -1.729C1.674 -2.202 1.953 -2.531 2.247 -2.531C2.555 -2.531 2.824 -2.187 2.824 -1.729C2.824 -1.255 2.545 -0.927 2.252 -0.927Z' id='g1-64'/>
+<path d='M3.083 -0.608C2.735 -0.394 2.575 -0.299 2.077 -0.299C1.305 -0.299 0.837 -1.021 0.837 -1.738C0.837 -2.476 1.35 -3.168 2.077 -3.168C2.406 -3.168 2.745 -3.064 2.974 -2.889L3.054 -3.342C2.705 -3.472 2.426 -3.512 2.062 -3.512C1.076 -3.512 0.339 -2.695 0.339 -1.733C0.339 -0.707 1.121 0.05 2.092 0.05C2.58 0.05 2.785 -0.05 3.113 -0.229L3.083 -0.608Z' id='g1-67'/>
+<path d='M2.725 -1.624V-1.953H0.986V-3.098H1.714C1.773 -3.098 1.833 -3.093 1.893 -3.093H2.874V-3.442H0.483V0H2.949V-0.389H2.501C2.082 -0.389 1.664 -0.379 1.245 -0.379H0.986V-1.624H2.725Z' id='g1-69'/>
+<path d='M3.173 -1.489H2.057V-1.161H2.735V-0.399C2.516 -0.344 2.301 -0.299 2.077 -0.299C1.31 -0.299 0.837 -1.021 0.837 -1.733C0.837 -2.416 1.3 -3.168 2.052 -3.168C2.511 -3.168 2.8 -3.029 3.049 -2.819L3.128 -3.273C2.785 -3.437 2.481 -3.517 2.102 -3.517C1.096 -3.517 0.339 -2.73 0.339 -1.733C0.339 -0.762 1.091 0.05 2.072 0.05C2.431 0.05 2.854 -0.03 3.173 -0.194V-1.489Z' id='g1-71'/>
+<path d='M3.248 -3.457H2.745V-1.963H0.986V-3.457H0.483V0H0.986V-1.634H2.745V0H3.248V-3.457Z' id='g1-72'/>
+<path d='M0.986 -3.457H0.483V0H0.986V-3.457Z' id='g1-73'/>
+<path d='M1.176 -3.457H0.498V0H0.917V-3.064H0.922L2.555 0H3.233V-3.457H2.814V-0.394H2.809L1.176 -3.457Z' id='g1-78'/>
+<path d='M3.611 -1.714C3.611 -2.745 2.854 -3.562 1.953 -3.562S0.294 -2.745 0.294 -1.714S1.061 0.105 1.953 0.105C2.849 0.105 3.611 -0.687 3.611 -1.714ZM1.953 -0.249C1.35 -0.249 0.797 -0.852 0.797 -1.793C0.797 -2.675 1.355 -3.218 1.953 -3.218S3.108 -2.675 3.108 -1.793C3.108 -0.847 2.555 -0.249 1.953 -0.249Z' id='g1-79'/>
+<path d='M1.868 -1.42C2.511 -1.42 3.083 -1.873 3.083 -2.446C3.083 -2.979 2.555 -3.457 1.833 -3.457H0.488V0H0.991V-1.42H1.868ZM1.709 -3.163C2.271 -3.163 2.63 -2.864 2.63 -2.446C2.63 -2.037 2.291 -1.729 1.709 -1.729H0.976V-3.163H1.709Z' id='g1-80'/>
+<path d='M3.143 -3.457H2.71V-1.161C2.71 -0.493 2.262 -0.189 1.833 -0.189S0.986 -0.498 0.986 -1.156V-3.457H0.483V-1.166C0.483 -0.433 1.111 0.105 1.828 0.105C2.54 0.105 3.143 -0.438 3.143 -1.166V-3.457Z' id='g1-85'/>
+<path d='M1.968 -1.823L3.228 -3.457H2.685L1.724 -2.182L0.742 -3.457H0.149L1.479 -1.823L0.075 0H0.618L1.724 -1.499L2.854 0H3.447L1.968 -1.823Z' id='g1-88'/>
+<path d='M2.934 -3.238V-3.457H0.369V-3.123H1.41C1.479 -3.123 1.539 -3.128 1.609 -3.128H2.291L0.294 -0.229V0H2.964V-0.354H2.466C1.958 -0.354 1.45 -0.344 0.941 -0.344L2.934 -3.238Z' id='g1-90'/>
+<path d='M2.122 -1.435C2.122 -1.943 1.733 -2.286 1.24 -2.286C0.927 -2.286 0.687 -2.222 0.408 -2.072L0.438 -1.709C0.603 -1.818 0.847 -1.968 1.24 -1.968C1.46 -1.968 1.689 -1.803 1.689 -1.43V-1.23C0.951 -1.205 0.224 -1.051 0.224 -0.588C0.224 -0.339 0.394 0.05 0.832 0.05C1.046 0.05 1.44 0.005 1.704 -0.189V0H2.122V-1.435ZM1.689 -0.707C1.689 -0.608 1.689 -0.478 1.514 -0.374C1.355 -0.284 1.161 -0.279 1.106 -0.279C0.832 -0.279 0.623 -0.403 0.623 -0.593C0.623 -0.912 1.465 -0.941 1.689 -0.951V-0.707Z' id='g1-97'/>
+<path d='M0.842 -3.457H0.423V0H0.857V-0.234C0.966 -0.139 1.205 0.05 1.569 0.05C2.112 0.05 2.55 -0.458 2.55 -1.111C2.55 -1.714 2.207 -2.262 1.709 -2.262C1.395 -2.262 1.091 -2.162 0.842 -1.978V-3.457ZM0.857 -1.569C0.857 -1.649 0.857 -1.709 1.031 -1.823C1.106 -1.868 1.24 -1.933 1.41 -1.933C1.743 -1.933 2.117 -1.709 2.117 -1.111C2.117 -0.503 1.704 -0.279 1.355 -0.279C1.171 -0.279 0.996 -0.364 0.857 -0.588V-1.569Z' id='g1-98'/>
+<path d='M2.167 -0.543C1.918 -0.384 1.649 -0.294 1.34 -0.294C0.882 -0.294 0.613 -0.663 0.613 -1.111C0.613 -1.494 0.812 -1.943 1.355 -1.943C1.694 -1.943 1.853 -1.873 2.107 -1.714L2.172 -2.072C1.873 -2.222 1.743 -2.286 1.355 -2.286C0.608 -2.286 0.179 -1.684 0.179 -1.106C0.179 -0.498 0.658 0.05 1.335 0.05C1.684 0.05 1.983 -0.055 2.197 -0.179L2.167 -0.543Z' id='g1-99'/>
+<path d='M2.306 -3.457H1.888V-1.998C1.569 -2.232 1.245 -2.262 1.101 -2.262C0.578 -2.262 0.179 -1.738 0.179 -1.106S0.573 0.05 1.086 0.05C1.395 0.05 1.684 -0.09 1.873 -0.259V0H2.306V-3.457ZM1.873 -0.618C1.748 -0.413 1.559 -0.279 1.32 -0.279C0.971 -0.279 0.613 -0.523 0.613 -1.101C0.613 -1.724 1.036 -1.933 1.375 -1.933C1.574 -1.933 1.743 -1.848 1.873 -1.679V-0.618Z' id='g1-100'/>
+<path d='M2.142 -0.543C1.863 -0.344 1.549 -0.279 1.335 -0.279C0.902 -0.279 0.573 -0.633 0.558 -1.091H2.192C2.192 -1.32 2.167 -1.654 1.973 -1.938C1.793 -2.192 1.494 -2.286 1.25 -2.286C0.643 -2.286 0.174 -1.753 0.174 -1.121C0.174 -0.483 0.672 0.05 1.33 0.05C1.619 0.05 1.918 -0.035 2.172 -0.189L2.142 -0.543ZM0.593 -1.39C0.707 -1.788 1.001 -1.958 1.25 -1.958C1.469 -1.958 1.793 -1.853 1.888 -1.39H0.593Z' id='g1-101'/>
+<path d='M2.506 -2.262C2.396 -2.262 2.062 -2.257 1.684 -2.112L1.674 -2.107C1.494 -2.227 1.32 -2.262 1.176 -2.262C0.687 -2.262 0.324 -1.878 0.324 -1.45C0.324 -1.275 0.384 -1.096 0.498 -0.956C0.428 -0.872 0.354 -0.732 0.354 -0.543C0.354 -0.349 0.433 -0.224 0.478 -0.164C0.204 -0.005 0.149 0.224 0.149 0.344C0.149 0.722 0.672 1.021 1.32 1.021C1.973 1.021 2.491 0.722 2.491 0.344C2.491 -0.359 1.619 -0.359 1.405 -0.359H0.941C0.862 -0.359 0.648 -0.359 0.648 -0.618C0.648 -0.717 0.682 -0.767 0.687 -0.777C0.862 -0.667 1.036 -0.633 1.171 -0.633C1.659 -0.633 2.022 -1.016 2.022 -1.445C2.022 -1.604 1.978 -1.748 1.888 -1.883C1.868 -1.913 1.868 -1.918 1.868 -1.923C1.868 -1.943 2.167 -1.943 2.192 -1.943C2.197 -1.943 2.386 -1.943 2.565 -1.923L2.506 -2.262ZM1.176 -0.941C0.907 -0.941 0.707 -1.111 0.707 -1.445C0.707 -1.833 0.956 -1.953 1.171 -1.953C1.44 -1.953 1.639 -1.783 1.639 -1.45C1.639 -1.061 1.39 -0.941 1.176 -0.941ZM1.41 0.03C1.524 0.03 2.112 0.03 2.112 0.349C2.112 0.563 1.738 0.712 1.32 0.712S0.528 0.563 0.528 0.349C0.528 0.324 0.528 0.03 0.932 0.03H1.41Z' id='g1-103'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.161 -2.262 0.932 -2.012 0.832 -1.908V-3.457H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-104'/>
+<path d='M0.877 -3.417H0.374V-2.914H0.877V-3.417ZM0.837 -2.212H0.418V0H0.837V-2.212Z' id='g1-105'/>
+<path d='M0.986 -3.417H0.483V-2.914H0.986V-3.417ZM-0.324 0.847C-0.095 0.971 0.13 1.016 0.319 1.016C0.663 1.016 0.986 0.752 0.986 0.294V-2.212H0.568V0.329C0.568 0.418 0.568 0.498 0.463 0.583C0.349 0.667 0.209 0.667 0.164 0.667C-0.045 0.667 -0.174 0.573 -0.234 0.518L-0.324 0.847Z' id='g1-106'/>
+<path d='M0.837 -3.457H0.418V0H0.837V-3.457Z' id='g1-108'/>
+<path d='M3.786 -1.474C3.786 -1.863 3.671 -2.262 3.059 -2.262C2.64 -2.262 2.381 -2.017 2.262 -1.858C2.212 -1.993 2.087 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.21C2.316 -1.539 2.456 -1.933 2.839 -1.933C3.352 -1.933 3.352 -1.584 3.352 -1.44V0H3.786V-1.474Z' id='g1-109'/>
+<path d='M2.316 -1.474C2.316 -1.863 2.202 -2.262 1.589 -2.262C1.305 -2.262 1.031 -2.147 0.812 -1.883V-2.247H0.413V0H0.847V-1.21C0.847 -1.539 0.986 -1.933 1.37 -1.933C1.883 -1.933 1.883 -1.584 1.883 -1.44V0H2.316V-1.474Z' id='g1-110'/>
+<path d='M2.491 -1.091C2.491 -1.748 1.968 -2.286 1.32 -2.286S0.149 -1.743 0.149 -1.091C0.149 -0.458 0.677 0.05 1.32 0.05C1.968 0.05 2.491 -0.458 2.491 -1.091ZM1.32 -0.294C0.927 -0.294 0.583 -0.583 0.583 -1.146S0.951 -1.958 1.32 -1.958C1.694 -1.958 2.057 -1.699 2.057 -1.146C2.057 -0.578 1.704 -0.294 1.32 -0.294Z' id='g1-111'/>
+<path d='M0.857 -0.234C1.121 0.005 1.405 0.05 1.574 0.05C2.102 0.05 2.55 -0.453 2.55 -1.111C2.55 -1.709 2.222 -2.262 1.729 -2.262C1.504 -2.262 1.131 -2.197 0.842 -1.973V-2.212H0.423V0.966H0.857V-0.234ZM0.857 -1.654C0.971 -1.793 1.166 -1.918 1.405 -1.918C1.803 -1.918 2.117 -1.549 2.117 -1.111C2.117 -0.618 1.743 -0.279 1.355 -0.279C1.28 -0.279 1.156 -0.289 1.026 -0.394C0.877 -0.508 0.857 -0.583 0.857 -0.677V-1.654Z' id='g1-112'/>
+<path d='M0.842 -1.061C0.842 -1.599 1.29 -1.888 1.729 -1.893V-2.262C1.31 -2.257 1.006 -2.052 0.807 -1.788V-2.247H0.423V0H0.842V-1.061Z' id='g1-114'/>
+<path d='M1.818 -2.132C1.479 -2.271 1.23 -2.286 1.051 -2.286C0.927 -2.286 0.174 -2.286 0.174 -1.624C0.174 -1.39 0.304 -1.26 0.369 -1.2C0.543 -1.026 0.752 -0.986 1.016 -0.936C1.25 -0.892 1.519 -0.842 1.519 -0.603C1.519 -0.289 1.106 -0.289 1.036 -0.289C0.717 -0.289 0.418 -0.403 0.219 -0.543L0.149 -0.169C0.319 -0.085 0.623 0.05 1.036 0.05C1.26 0.05 1.479 0.015 1.664 -0.12C1.848 -0.259 1.908 -0.478 1.908 -0.648C1.908 -0.737 1.898 -0.932 1.689 -1.121C1.504 -1.285 1.325 -1.32 1.086 -1.365C0.792 -1.42 0.563 -1.465 0.563 -1.684C0.563 -1.968 0.927 -1.968 1.001 -1.968C1.285 -1.968 1.504 -1.908 1.753 -1.778L1.818 -2.132Z' id='g1-115'/>
+<path d='M0.936 -1.898H1.674V-2.212H0.936V-2.844H0.553V-2.212H0.1V-1.898H0.538V-0.638C0.538 -0.304 0.623 0.05 0.981 0.05S1.614 -0.065 1.763 -0.134L1.679 -0.453C1.514 -0.334 1.34 -0.294 1.2 -0.294C0.991 -0.294 0.936 -0.498 0.936 -0.727V-1.898Z' id='g1-116'/>
+<path d='M2.316 -2.212H1.883V-0.767C1.883 -0.369 1.544 -0.244 1.255 -0.244C0.887 -0.244 0.847 -0.344 0.847 -0.573V-2.212H0.413V-0.543C0.413 -0.1 0.608 0.05 0.956 0.05C1.161 0.05 1.599 0.01 1.898 -0.229V0H2.316V-2.212Z' id='g1-117'/>
+<path d='M3.537 -2.212H3.148C3.103 -2.072 2.824 -1.23 2.67 -0.712C2.63 -0.568 2.58 -0.408 2.565 -0.294H2.56C2.531 -0.498 2.356 -1.036 2.346 -1.071L1.978 -2.212H1.599C1.455 -1.783 1.081 -0.667 1.041 -0.304H1.036C0.996 -0.658 0.628 -1.758 0.548 -1.998C0.508 -2.117 0.508 -2.127 0.483 -2.212H0.075L0.802 0H1.22C1.225 -0.02 1.36 -0.413 1.534 -0.966C1.609 -1.21 1.758 -1.689 1.783 -1.908L1.788 -1.913C1.798 -1.808 1.828 -1.699 1.863 -1.574S1.933 -1.315 1.968 -1.2L2.351 0H2.809L3.537 -2.212Z' id='g1-119'/>
+<path d='M1.38 -1.141L2.346 -2.212H1.908L1.2 -1.395L0.478 -2.212H0.03L1.026 -1.141L0 0H0.443L1.2 -0.936L1.988 0H2.436L1.38 -1.141Z' id='g1-120'/>
+<path d='M2.112 -2.002V-2.212H0.219V-1.893H0.951C1.011 -1.893 1.071 -1.898 1.131 -1.898H1.519L0.149 -0.219V0H2.127V-0.334H1.355C1.295 -0.334 1.235 -0.329 1.176 -0.329H0.742L2.112 -2.002Z' id='g1-122'/>
+<path d='M3.891 -2.914C4.806 -3.165 5.452 -3.811 5.452 -4.546C5.452 -5.469 4.411 -6.223 3.129 -6.223H0.87V0H1.704V-2.824H3.138L4.842 0H5.703L3.891 -2.914ZM1.704 -3.407V-5.694H3.022C4.062 -5.694 4.671 -5.192 4.671 -4.546C4.671 -3.963 4.125 -3.407 3.022 -3.407H1.704Z' id='g0-82'/>
+<path d='M3.694 -2.591C3.694 -3.479 3.04 -4.133 2.152 -4.133C1.569 -4.133 1.139 -3.981 0.708 -3.739L0.762 -3.102C1.21 -3.434 1.65 -3.569 2.143 -3.569C2.645 -3.569 2.95 -3.165 2.95 -2.582V-2.206C1.408 -2.17 0.395 -1.766 0.395 -1.04C0.395 -0.619 0.672 0.099 1.453 0.099C1.632 0.099 2.412 0.081 2.977 -0.341V0H3.694V-2.591ZM2.95 -1.255C2.95 -1.067 2.95 -0.843 2.627 -0.655C2.403 -0.52 2.107 -0.484 1.928 -0.484C1.47 -0.484 1.085 -0.699 1.085 -1.058C1.085 -1.695 2.833 -1.722 2.95 -1.722V-1.255Z' id='g0-97'/>
+<path d='M3.829 -1.964C3.829 -2.242 3.82 -2.923 3.47 -3.461C3.093 -4.026 2.52 -4.133 2.179 -4.133C1.139 -4.133 0.314 -3.174 0.314 -2.026C0.314 -0.843 1.193 0.099 2.313 0.099C2.744 0.099 3.264 -0.009 3.784 -0.341L3.73 -0.959C3.165 -0.556 2.636 -0.484 2.322 -0.484C1.578 -0.484 1.004 -1.139 0.977 -1.964H3.829ZM1.031 -2.493C1.175 -3.067 1.614 -3.551 2.179 -3.551C2.511 -3.551 3.12 -3.398 3.291 -2.493H1.031Z' id='g0-101'/>
+<path d='M1.524 -6.133H0.664V-5.272H1.524V-6.133ZM1.453 -3.981H0.735V0H1.453V-3.981Z' id='g0-105'/>
+<path d='M1.453 -6.223H0.735V0H1.453V-6.223Z' id='g0-108'/>
+<path d='M1.462 -1.91C1.462 -2.851 2.197 -3.425 3.013 -3.434V-4.08C2.367 -4.071 1.775 -3.748 1.408 -3.219V-4.035H0.744V0H1.462V-1.91Z' id='g0-114'/>
+<path d='M3.165 -3.847C2.609 -4.098 2.197 -4.133 1.829 -4.133C1.623 -4.133 0.305 -4.133 0.305 -2.95C0.305 -2.52 0.565 -2.251 0.664 -2.152C1.004 -1.856 1.237 -1.811 1.847 -1.695C2.134 -1.641 2.645 -1.542 2.645 -1.085C2.645 -0.502 1.919 -0.502 1.802 -0.502C1.273 -0.502 0.762 -0.681 0.377 -0.95L0.26 -0.296C0.798 -0.009 1.345 0.099 1.802 0.099C2.385 0.099 3.318 -0.09 3.318 -1.157C3.318 -1.47 3.192 -1.784 2.878 -2.053C2.573 -2.313 2.304 -2.367 1.748 -2.475C1.426 -2.537 0.977 -2.618 0.977 -3.04C0.977 -3.569 1.614 -3.569 1.748 -3.569C2.403 -3.569 2.789 -3.362 3.049 -3.219L3.165 -3.847Z' id='g0-115'/>
+<path d='M1.623 -3.425H2.914V-3.981H1.623V-5.12H0.959V-3.981H0.17V-3.425H0.933V-1.13C0.933 -0.601 1.049 0.099 1.704 0.099C2.098 0.099 2.564 0.018 3.067 -0.233L2.914 -0.798C2.681 -0.619 2.367 -0.511 2.089 -0.511C1.739 -0.511 1.623 -0.825 1.623 -1.291V-3.425Z' id='g0-116'/>
+<path d='M4.116 -3.981H3.407L2.699 -2.161C2.52 -1.695 2.188 -0.825 2.143 -0.493H2.125C2.107 -0.646 2.08 -0.816 1.587 -2.107C1.318 -2.833 0.879 -3.927 0.861 -3.981H0.126L1.704 0H2.537L4.116 -3.981Z' id='g0-118'/>
+</defs>
+<g id='page10'>
+<path d='M194.617 194.93V186.074M253.285 194.93V186.074M311.953 194.93V186.074M370.625 194.93V186.074M429.293 194.93V186.074M194.617 51.973V60.828M253.285 51.973V60.828M311.953 51.973V60.828M370.625 51.973V60.828M429.293 51.973V60.828' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M165.281 190.324V186.074M223.953 190.324V186.074M282.621 190.324V186.074M341.289 190.324V186.074M399.957 190.324V186.074M458.629 190.324V186.074M165.281 56.574V60.828M223.953 56.574V60.828M282.621 56.574V60.828M341.289 56.574V60.828M399.957 56.574V60.828M458.629 56.574V60.828' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 186.074H140.199M135.949 154.762H140.199M135.949 123.449H140.199M135.949 92.141H140.199M135.949 60.828H140.199M487.961 186.074H483.711M487.961 154.762H483.711M487.961 123.449H483.711M487.961 92.141H483.711M487.961 60.828H483.711' fill='none' stroke='#808080' stroke-miterlimit='10' stroke-width='0.199'/>
+<path d='M135.949 186.074V60.828H487.961V186.074H135.949Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -21.265 59.571)'>
+<use x='168.285' xlink:href='#g2-97' y='140.503'/>
+<use x='172.353' xlink:href='#g2-108' y='140.503'/>
+<use x='174.373' xlink:href='#g2-108' y='140.503'/>
+<use x='176.393' xlink:href='#g2-111' y='140.503'/>
+<use x='180.863' xlink:href='#g2-99' y='140.503'/>
+<use x='184.627' xlink:href='#g2-45' y='140.503'/>
+<use x='187.449' xlink:href='#g2-116' y='140.503'/>
+<use x='190.507' xlink:href='#g2-101' y='140.503'/>
+<use x='194.271' xlink:href='#g2-115' y='140.503'/>
+<use x='197.517' xlink:href='#g2-116' y='140.503'/>
+<use x='200.575' xlink:href='#g2-49' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 36.532 59.571)'>
+<use x='168.285' xlink:href='#g2-97' y='140.503'/>
+<use x='172.353' xlink:href='#g2-108' y='140.503'/>
+<use x='174.373' xlink:href='#g2-108' y='140.503'/>
+<use x='176.393' xlink:href='#g2-111' y='140.503'/>
+<use x='180.863' xlink:href='#g2-99' y='140.503'/>
+<use x='184.627' xlink:href='#g2-45' y='140.503'/>
+<use x='187.449' xlink:href='#g2-116' y='140.503'/>
+<use x='190.507' xlink:href='#g2-101' y='140.503'/>
+<use x='194.271' xlink:href='#g2-115' y='140.503'/>
+<use x='197.517' xlink:href='#g2-116' y='140.503'/>
+<use x='200.575' xlink:href='#g2-78' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 94.98 59.571)'>
+<use x='168.285' xlink:href='#g2-115' y='140.503'/>
+<use x='171.531' xlink:href='#g2-104' y='140.503'/>
+<use x='175.904' xlink:href='#g2-54' y='140.503'/>
+<use x='180.138' xlink:href='#g2-98' y='140.503'/>
+<use x='184.746' xlink:href='#g2-101' y='140.503'/>
+<use x='188.509' xlink:href='#g2-110' y='140.503'/>
+<use x='192.882' xlink:href='#g2-99' y='140.503'/>
+<use x='196.646' xlink:href='#g2-104' y='140.503'/>
+<use x='201.018' xlink:href='#g2-78' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 153.649 59.571)'>
+<use x='168.285' xlink:href='#g2-115' y='140.503'/>
+<use x='171.531' xlink:href='#g2-104' y='140.503'/>
+<use x='175.904' xlink:href='#g2-56' y='140.503'/>
+<use x='180.138' xlink:href='#g2-98' y='140.503'/>
+<use x='184.746' xlink:href='#g2-101' y='140.503'/>
+<use x='188.509' xlink:href='#g2-110' y='140.503'/>
+<use x='192.882' xlink:href='#g2-99' y='140.503'/>
+<use x='196.646' xlink:href='#g2-104' y='140.503'/>
+<use x='201.018' xlink:href='#g2-78' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 207.225 59.571)'>
+<use x='168.285' xlink:href='#g2-120' y='140.503'/>
+<use x='172.187' xlink:href='#g2-109' y='140.503'/>
+<use x='178.912' xlink:href='#g2-97' y='140.503'/>
+<use x='182.98' xlink:href='#g2-108' y='140.503'/>
+<use x='185' xlink:href='#g2-108' y='140.503'/>
+<use x='187.02' xlink:href='#g2-111' y='140.503'/>
+<use x='191.49' xlink:href='#g2-99' y='140.503'/>
+<use x='195.254' xlink:href='#g2-45' y='140.503'/>
+<use x='198.076' xlink:href='#g2-116' y='140.503'/>
+<use x='201.134' xlink:href='#g2-101' y='140.503'/>
+<use x='204.898' xlink:href='#g2-115' y='140.503'/>
+<use x='208.144' xlink:href='#g2-116' y='140.503'/>
+<use x='211.202' xlink:href='#g2-78' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 263.494 59.571)'>
+<use x='168.285' xlink:href='#g2-99' y='140.503'/>
+<use x='172.049' xlink:href='#g2-97' y='140.503'/>
+<use x='176.117' xlink:href='#g2-99' y='140.503'/>
+<use x='179.88' xlink:href='#g2-104' y='140.503'/>
+<use x='184.253' xlink:href='#g2-101' y='140.503'/>
+<use x='188.017' xlink:href='#g2-45' y='140.503'/>
+<use x='190.839' xlink:href='#g2-115' y='140.503'/>
+<use x='194.086' xlink:href='#g2-99' y='140.503'/>
+<use x='197.849' xlink:href='#g2-114' y='140.503'/>
+<use x='200.741' xlink:href='#g2-97' y='140.503'/>
+<use x='204.81' xlink:href='#g2-116' y='140.503'/>
+<use x='207.868' xlink:href='#g2-99' y='140.503'/>
+<use x='211.631' xlink:href='#g2-104' y='140.503'/>
+<use x='216.004' xlink:href='#g2-78' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -40.942 47.205)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-120' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 15.894)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-53' y='140.503'/>
+<use x='175.048' xlink:href='#g1-120' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -15.418)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-120' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -46.729)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-53' y='140.503'/>
+<use x='175.048' xlink:href='#g1-120' y='140.503'/>
+</g>
+<g transform='matrix(1 0 0 1 -45.059 -78.041)'>
+<use x='168.285' xlink:href='#g1-50' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-120' y='140.503'/>
+</g>
+<path clip-path='url(#clip10)' d='M135.949 123.449H487.961' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M140.125 186.074H143.363V123.449H140.125ZM198.797 186.074H202.035V123.449H198.797ZM257.465 186.074H260.703V123.449H257.465ZM316.133 186.074H319.371V123.449H316.133ZM374.801 186.074H378.039V123.449H374.801ZM433.473 186.074H436.711V123.449H433.473Z' fill='#f0e0f0'/>
+<path clip-path='url(#clip10)' d='M140.125 186.074H143.363V123.449H140.125ZM198.797 186.074H202.035V123.449H198.797ZM257.465 186.074H260.703V123.449H257.465ZM316.133 186.074H319.371V123.449H316.133ZM374.801 186.074H378.039V123.449H374.801ZM433.473 186.074H436.711V123.449H433.473Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M141.746 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip10)' d='M139.754 123.449H143.738' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M141.746 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip10)' d='M139.754 123.449H143.738' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M200.414 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip10)' d='M198.422 123.449H202.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M200.414 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip10)' d='M198.422 123.449H202.406' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M259.082 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip10)' d='M257.09 123.449H261.078' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M259.082 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip10)' d='M257.09 123.449H261.078' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M317.754 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip10)' d='M315.762 123.449H319.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M317.754 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip10)' d='M315.762 123.449H319.746' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M376.422 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip10)' d='M374.43 123.449H378.414' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M376.422 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip10)' d='M374.43 123.449H378.414' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M435.09 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip10)' d='M433.098 123.449H437.082' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M435.09 123.449V123.449' fill='#f0e0f0'/>
+<path clip-path='url(#clip10)' d='M433.098 123.449H437.082' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M145.356 186.074H148.594V120.071H145.356ZM204.028 186.074H207.266V121.571H204.028ZM262.695 186.074H265.934V122.699H262.695ZM321.363 186.074H324.602V125.895H321.363ZM380.031 186.074H383.27V173.172H380.031ZM438.703 186.074H441.942V93.391H438.703Z' fill='#e1c2e1'/>
+<path clip-path='url(#clip10)' d='M145.356 186.074H148.594V120.071H145.356ZM204.028 186.074H207.266V121.571H204.028ZM262.695 186.074H265.934V122.699H262.695ZM321.363 186.074H324.602V125.895H321.363ZM380.031 186.074H383.27V173.172H380.031ZM438.703 186.074H441.942V93.391H438.703Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M146.977 120.071V120.071' fill='#e1c2e1'/>
+<path clip-path='url(#clip10)' d='M144.984 120.07H148.968' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M146.977 120.071V120.071' fill='#e1c2e1'/>
+<path clip-path='url(#clip10)' d='M144.984 120.07H148.968' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M205.645 121.571V121.571' fill='#e1c2e1'/>
+<path clip-path='url(#clip10)' d='M203.652 121.57H207.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M205.645 121.571V121.571' fill='#e1c2e1'/>
+<path clip-path='url(#clip10)' d='M203.652 121.57H207.636' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M264.313 122.699V122.699' fill='#e1c2e1'/>
+<path clip-path='url(#clip10)' d='M262.32 122.699H266.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M264.313 122.699V122.699' fill='#e1c2e1'/>
+<path clip-path='url(#clip10)' d='M262.32 122.699H266.308' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M322.984 125.895V125.895' fill='#e1c2e1'/>
+<path clip-path='url(#clip10)' d='M320.992 125.894H324.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M322.984 125.895V125.895' fill='#e1c2e1'/>
+<path clip-path='url(#clip10)' d='M320.992 125.894H324.976' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M381.652 173.172V173.172' fill='#e1c2e1'/>
+<path clip-path='url(#clip10)' d='M379.66 173.172H383.644' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M381.652 173.172V173.172' fill='#e1c2e1'/>
+<path clip-path='url(#clip10)' d='M379.66 173.172H383.644' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M440.32 93.391V93.391' fill='#e1c2e1'/>
+<path clip-path='url(#clip10)' d='M438.328 93.391H442.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M440.32 93.391V93.391' fill='#e1c2e1'/>
+<path clip-path='url(#clip10)' d='M438.328 93.391H442.312' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M150.586 186.074H153.824V122.699H150.586ZM209.258 186.074H212.496V124.766H209.258ZM267.926 186.074H271.164V121.762H267.926ZM326.594 186.074H329.832V118.692H326.594ZM385.262 186.074H388.5V154.324H385.262ZM443.934 186.074H447.172V111.238H443.934Z' fill='#d1a3d1'/>
+<path clip-path='url(#clip10)' d='M150.586 186.074H153.824V122.699H150.586ZM209.258 186.074H212.496V124.766H209.258ZM267.926 186.074H271.164V121.762H267.926ZM326.594 186.074H329.832V118.692H326.594ZM385.262 186.074H388.5V154.324H385.262ZM443.934 186.074H447.172V111.238H443.934Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M152.207 122.699V122.699' fill='#d1a3d1'/>
+<path clip-path='url(#clip10)' d='M150.215 122.699H154.199' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M152.207 122.699V122.699' fill='#d1a3d1'/>
+<path clip-path='url(#clip10)' d='M150.215 122.699H154.199' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M210.875 124.766V124.766' fill='#d1a3d1'/>
+<path clip-path='url(#clip10)' d='M208.883 124.766H212.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M210.875 124.766V124.766' fill='#d1a3d1'/>
+<path clip-path='url(#clip10)' d='M208.883 124.766H212.867' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M269.543 121.762V121.762' fill='#d1a3d1'/>
+<path clip-path='url(#clip10)' d='M267.551 121.762H271.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M269.543 121.762V121.762' fill='#d1a3d1'/>
+<path clip-path='url(#clip10)' d='M267.551 121.762H271.539' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M328.215 118.692V118.692' fill='#d1a3d1'/>
+<path clip-path='url(#clip10)' d='M326.223 118.691H330.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M328.215 118.692V118.692' fill='#d1a3d1'/>
+<path clip-path='url(#clip10)' d='M326.223 118.691H330.207' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M386.883 154.324V154.324' fill='#d1a3d1'/>
+<path clip-path='url(#clip10)' d='M384.891 154.324H388.875' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M386.883 154.324V154.324' fill='#d1a3d1'/>
+<path clip-path='url(#clip10)' d='M384.891 154.324H388.875' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M445.551 111.238V111.238' fill='#d1a3d1'/>
+<path clip-path='url(#clip10)' d='M443.559 111.238H447.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M445.551 111.238V111.238' fill='#d1a3d1'/>
+<path clip-path='url(#clip10)' d='M443.559 111.238H447.543' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M155.817 186.074H159.055V119.696H155.817ZM214.488 186.074H217.727V121.887H214.488ZM273.156 186.074H276.395V113.43H273.156ZM331.824 186.074H335.063V119.004H331.824ZM390.492 186.074H393.731V89.133H390.492ZM449.164 186.074H452.402V120.633H449.164Z' fill='#c285c2'/>
+<path clip-path='url(#clip10)' d='M155.817 186.074H159.055V119.696H155.817ZM214.488 186.074H217.727V121.887H214.488ZM273.156 186.074H276.395V113.43H273.156ZM331.824 186.074H335.063V119.004H331.824ZM390.492 186.074H393.731V89.133H390.492ZM449.164 186.074H452.402V120.633H449.164Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M157.438 119.696V119.696' fill='#c285c2'/>
+<path clip-path='url(#clip10)' d='M155.445 119.695H159.429' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M157.438 119.696V119.696' fill='#c285c2'/>
+<path clip-path='url(#clip10)' d='M155.445 119.695H159.429' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M216.106 121.887V121.887' fill='#c285c2'/>
+<path clip-path='url(#clip10)' d='M214.113 121.886H218.097' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M216.106 121.887V121.887' fill='#c285c2'/>
+<path clip-path='url(#clip10)' d='M214.113 121.886H218.097' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M274.774 113.43V113.43' fill='#c285c2'/>
+<path clip-path='url(#clip10)' d='M272.781 113.43H276.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M274.774 113.43V113.43' fill='#c285c2'/>
+<path clip-path='url(#clip10)' d='M272.781 113.43H276.769' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M333.445 119.004V119.004' fill='#c285c2'/>
+<path clip-path='url(#clip10)' d='M331.453 119.004H335.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M333.445 119.004V119.004' fill='#c285c2'/>
+<path clip-path='url(#clip10)' d='M331.453 119.004H335.437' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M392.113 89.133V89.133' fill='#c285c2'/>
+<path clip-path='url(#clip10)' d='M390.121 89.133H394.105' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M392.113 89.133V89.133' fill='#c285c2'/>
+<path clip-path='url(#clip10)' d='M390.121 89.133H394.105' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M450.781 120.633V120.633' fill='#c285c2'/>
+<path clip-path='url(#clip10)' d='M448.789 120.633H452.773' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M450.781 120.633V120.633' fill='#c285c2'/>
+<path clip-path='url(#clip10)' d='M448.789 120.633H452.773' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M161.047 186.074H164.285V109.738H161.047ZM219.719 186.074H222.957V112.555H219.719ZM278.387 186.074H281.625V86.192H278.387ZM337.055 186.074H340.293V106.543H337.055ZM395.723 186.074H398.961V172.172H395.723ZM454.395 186.074H457.633V122.949H454.395Z' fill='#b366b3'/>
+<path clip-path='url(#clip10)' d='M161.047 186.074H164.285V109.738H161.047ZM219.719 186.074H222.957V112.555H219.719ZM278.387 186.074H281.625V86.192H278.387ZM337.055 186.074H340.293V106.543H337.055ZM395.723 186.074H398.961V172.172H395.723ZM454.395 186.074H457.633V122.949H454.395Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M162.668 109.738V109.738' fill='#b366b3'/>
+<path clip-path='url(#clip10)' d='M160.676 109.738H164.66' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M162.668 109.738V109.738' fill='#b366b3'/>
+<path clip-path='url(#clip10)' d='M160.676 109.738H164.66' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M221.336 112.555V112.555' fill='#b366b3'/>
+<path clip-path='url(#clip10)' d='M219.344 112.554H223.328' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M221.336 112.555V112.555' fill='#b366b3'/>
+<path clip-path='url(#clip10)' d='M219.344 112.554H223.328' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M280.004 86.192V86.192' fill='#b366b3'/>
+<path clip-path='url(#clip10)' d='M278.012 86.191H282' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M280.004 86.192V86.192' fill='#b366b3'/>
+<path clip-path='url(#clip10)' d='M278.012 86.191H282' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M338.676 106.543V106.543' fill='#b366b3'/>
+<path clip-path='url(#clip10)' d='M336.684 106.543H340.668' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M338.676 106.543V106.543' fill='#b366b3'/>
+<path clip-path='url(#clip10)' d='M336.684 106.543H340.668' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M397.344 172.172V172.172' fill='#b366b3'/>
+<path clip-path='url(#clip10)' d='M395.352 172.172H399.336' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M397.344 172.172V172.172' fill='#b366b3'/>
+<path clip-path='url(#clip10)' d='M395.352 172.172H399.336' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M456.012 122.949V122.949' fill='#b366b3'/>
+<path clip-path='url(#clip10)' d='M454.02 122.949H458.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M456.012 122.949V122.949' fill='#b366b3'/>
+<path clip-path='url(#clip10)' d='M454.02 122.949H458.004' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M166.278 186.074H169.516V98.278H166.278ZM224.949 186.074H228.188V94.77H224.949ZM283.617 186.074H286.856V78.051H283.617ZM342.285 186.074H345.524V92.453H342.285ZM400.953 186.074H404.192V154.762H400.953ZM459.625 186.074H462.863V113.242H459.625Z' fill='#a447a4'/>
+<path clip-path='url(#clip10)' d='M166.278 186.074H169.516V98.278H166.278ZM224.949 186.074H228.188V94.77H224.949ZM283.617 186.074H286.856V78.051H283.617ZM342.285 186.074H345.524V92.453H342.285ZM400.953 186.074H404.192V154.762H400.953ZM459.625 186.074H462.863V113.242H459.625Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M167.899 98.278V98.278' fill='#a447a4'/>
+<path clip-path='url(#clip10)' d='M165.906 98.277H169.89' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M167.899 98.278V98.278' fill='#a447a4'/>
+<path clip-path='url(#clip10)' d='M165.906 98.277H169.89' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M226.567 94.77V94.77' fill='#a447a4'/>
+<path clip-path='url(#clip10)' d='M224.574 94.77H228.558' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M226.567 94.77V94.77' fill='#a447a4'/>
+<path clip-path='url(#clip10)' d='M224.574 94.77H228.558' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M285.234 78.051V78.051' fill='#a447a4'/>
+<path clip-path='url(#clip10)' d='M283.242 78.051H287.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M285.234 78.051V78.051' fill='#a447a4'/>
+<path clip-path='url(#clip10)' d='M283.242 78.051H287.23' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M343.906 92.453V92.453' fill='#a447a4'/>
+<path clip-path='url(#clip10)' d='M341.914 92.453H345.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M343.906 92.453V92.453' fill='#a447a4'/>
+<path clip-path='url(#clip10)' d='M341.914 92.453H345.898' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M402.574 154.762V154.762' fill='#a447a4'/>
+<path clip-path='url(#clip10)' d='M400.582 154.761H404.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M402.574 154.762V154.762' fill='#a447a4'/>
+<path clip-path='url(#clip10)' d='M400.582 154.761H404.566' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M461.242 113.242V113.242' fill='#a447a4'/>
+<path clip-path='url(#clip10)' d='M459.25 113.242H463.234' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M461.242 113.242V113.242' fill='#a447a4'/>
+<path clip-path='url(#clip10)' d='M459.25 113.242H463.234' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M171.508 186.074H174.746V109.172H171.508ZM230.18 186.074H233.414V112.617H230.18ZM288.848 186.074H292.086V86.254H288.848ZM347.516 186.074H350.754V107.418H347.516ZM406.184 186.074H409.422V172.235H406.184ZM464.856 186.074H468.094V124.266H464.856Z' fill='#942994'/>
+<path clip-path='url(#clip10)' d='M171.508 186.074H174.746V109.172H171.508ZM230.18 186.074H233.414V112.617H230.18ZM288.848 186.074H292.086V86.254H288.848ZM347.516 186.074H350.754V107.418H347.516ZM406.184 186.074H409.422V172.235H406.184ZM464.856 186.074H468.094V124.266H464.856Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M173.129 109.172V109.172' fill='#942994'/>
+<path clip-path='url(#clip10)' d='M171.137 109.172H175.121' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M173.129 109.172V109.172' fill='#942994'/>
+<path clip-path='url(#clip10)' d='M171.137 109.172H175.121' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M231.797 112.617V112.617' fill='#942994'/>
+<path clip-path='url(#clip10)' d='M229.805 112.617H233.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M231.797 112.617V112.617' fill='#942994'/>
+<path clip-path='url(#clip10)' d='M229.805 112.617H233.789' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M290.465 86.254V86.254' fill='#942994'/>
+<path clip-path='url(#clip10)' d='M288.473 86.254H292.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M290.465 86.254V86.254' fill='#942994'/>
+<path clip-path='url(#clip10)' d='M288.473 86.254H292.461' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M349.137 107.418V107.418' fill='#942994'/>
+<path clip-path='url(#clip10)' d='M347.145 107.418H351.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M349.137 107.418V107.418' fill='#942994'/>
+<path clip-path='url(#clip10)' d='M347.145 107.418H351.129' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M407.805 172.235V172.235' fill='#942994'/>
+<path clip-path='url(#clip10)' d='M405.813 172.234H409.797' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M407.805 172.235V172.235' fill='#942994'/>
+<path clip-path='url(#clip10)' d='M405.813 172.234H409.797' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M466.473 124.266V124.266' fill='#942994'/>
+<path clip-path='url(#clip10)' d='M464.481 124.266H468.465' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M466.473 124.266V124.266' fill='#942994'/>
+<path clip-path='url(#clip10)' d='M464.481 124.266H468.465' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M411.414 186.074H414.652V151.946H411.414ZM176.738 186.074H179.977V117.375H176.738ZM235.41 186.074H238.645V119.567H235.41ZM294.078 186.074H297.317V116.813H294.078ZM352.746 186.074H355.984V123.891H352.746ZM470.086 186.074H473.324V117.313H470.086Z' fill='#850a85'/>
+<path clip-path='url(#clip10)' d='M411.414 186.074H414.652V151.946H411.414ZM176.738 186.074H179.977V117.375H176.738ZM235.41 186.074H238.645V119.567H235.41ZM294.078 186.074H297.317V116.813H294.078ZM352.746 186.074H355.984V123.891H352.746ZM470.086 186.074H473.324V117.313H470.086Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M413.035 151.946V151.946' fill='#850a85'/>
+<path clip-path='url(#clip10)' d='M411.043 151.945H415.027' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M413.035 151.946V151.946' fill='#850a85'/>
+<path clip-path='url(#clip10)' d='M411.043 151.945H415.027' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M178.36 117.375V117.375' fill='#850a85'/>
+<path clip-path='url(#clip10)' d='M176.367 117.375H180.351' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M178.36 117.375V117.375' fill='#850a85'/>
+<path clip-path='url(#clip10)' d='M176.367 117.375H180.351' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M237.027 119.567V119.567' fill='#850a85'/>
+<path clip-path='url(#clip10)' d='M235.035 119.566H239.019' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M237.027 119.567V119.567' fill='#850a85'/>
+<path clip-path='url(#clip10)' d='M235.035 119.566H239.019' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M295.695 116.813V116.813' fill='#850a85'/>
+<path clip-path='url(#clip10)' d='M293.703 116.813H297.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M295.695 116.813V116.813' fill='#850a85'/>
+<path clip-path='url(#clip10)' d='M293.703 116.813H297.691' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M354.367 123.891V123.891' fill='#850a85'/>
+<path clip-path='url(#clip10)' d='M352.375 123.89H356.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M354.367 123.891V123.891' fill='#850a85'/>
+<path clip-path='url(#clip10)' d='M352.375 123.89H356.359' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M471.703 117.313V117.313' fill='#850a85'/>
+<path clip-path='url(#clip10)' d='M469.711 117.313H473.695' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M471.703 117.313V117.313' fill='#850a85'/>
+<path clip-path='url(#clip10)' d='M469.711 117.313H473.695' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M181.969 186.074H185.207V121.762H181.969ZM240.641 186.074H243.875V122.074H240.641ZM299.309 186.074H302.547V122.574H299.309ZM357.977 186.074H361.215V121.949H357.977ZM475.317 186.074H478.555V113.746H475.317Z' fill='#760076'/>
+<path clip-path='url(#clip10)' d='M181.969 186.074H185.207V121.762H181.969ZM240.641 186.074H243.875V122.074H240.641ZM299.309 186.074H302.547V122.574H299.309ZM357.977 186.074H361.215V121.949H357.977ZM475.317 186.074H478.555V113.746H475.317Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M183.59 121.762V121.762' fill='#760076'/>
+<path clip-path='url(#clip10)' d='M181.598 121.762H185.583' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M183.59 121.762V121.762' fill='#760076'/>
+<path clip-path='url(#clip10)' d='M181.598 121.762H185.583' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M242.258 122.074V122.074' fill='#760076'/>
+<path clip-path='url(#clip10)' d='M240.266 122.074H244.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M242.258 122.074V122.074' fill='#760076'/>
+<path clip-path='url(#clip10)' d='M240.266 122.074H244.25' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M300.926 122.574V122.574' fill='#760076'/>
+<path clip-path='url(#clip10)' d='M298.933 122.574H302.921' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M300.926 122.574V122.574' fill='#760076'/>
+<path clip-path='url(#clip10)' d='M298.933 122.574H302.921' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M359.598 121.949V121.949' fill='#760076'/>
+<path clip-path='url(#clip10)' d='M357.606 121.949H361.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M359.598 121.949V121.949' fill='#760076'/>
+<path clip-path='url(#clip10)' d='M357.606 121.949H361.59' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M476.934 113.746V113.746' fill='#760076'/>
+<path clip-path='url(#clip10)' d='M474.942 113.746H478.926' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M476.934 113.746V113.746' fill='#760076'/>
+<path clip-path='url(#clip10)' d='M474.942 113.746H478.926' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M187.199 186.074H190.438V122.887H187.199ZM245.871 186.074H249.106V122.637H245.871ZM304.539 186.074H307.777V123.387H304.539ZM363.207 186.074H366.445V125.391H363.207ZM421.875 186.074H425.113V169.164H421.875ZM480.547 186.074H483.785V121.571H480.547Z' fill='#670067'/>
+<path clip-path='url(#clip10)' d='M187.199 186.074H190.438V122.887H187.199ZM245.871 186.074H249.106V122.637H245.871ZM304.539 186.074H307.777V123.387H304.539ZM363.207 186.074H366.445V125.391H363.207ZM421.875 186.074H425.113V169.164H421.875ZM480.547 186.074H483.785V121.571H480.547Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M188.82 122.887V122.887' fill='#670067'/>
+<path clip-path='url(#clip10)' d='M186.828 122.886H190.813' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M188.82 122.887V122.887' fill='#670067'/>
+<path clip-path='url(#clip10)' d='M186.828 122.886H190.813' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M247.488 122.637V122.637' fill='#670067'/>
+<path clip-path='url(#clip10)' d='M245.496 122.637H249.481' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M247.488 122.637V122.637' fill='#670067'/>
+<path clip-path='url(#clip10)' d='M245.496 122.637H249.481' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M306.156 123.387V123.387' fill='#670067'/>
+<path clip-path='url(#clip10)' d='M304.164 123.387H308.152' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M306.156 123.387V123.387' fill='#670067'/>
+<path clip-path='url(#clip10)' d='M304.164 123.387H308.152' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M364.828 125.391V125.391' fill='#670067'/>
+<path clip-path='url(#clip10)' d='M362.836 125.39H366.82' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M364.828 125.391V125.391' fill='#670067'/>
+<path clip-path='url(#clip10)' d='M362.836 125.39H366.82' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M423.496 169.164V169.164' fill='#670067'/>
+<path clip-path='url(#clip10)' d='M421.504 169.164H425.488' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M423.496 169.164V169.164' fill='#670067'/>
+<path clip-path='url(#clip10)' d='M421.504 169.164H425.488' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M482.164 121.571V121.571' fill='#670067'/>
+<path clip-path='url(#clip10)' d='M480.172 121.57H484.156' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M482.164 121.571V121.571' fill='#670067'/>
+<path clip-path='url(#clip10)' d='M480.172 121.57H484.156' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path clip-path='url(#clip10)' d='M419.477 233.301H487.762V211.324H419.477Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 254.512 88.837)'>
+<use x='168.285' xlink:href='#g1-72' y='129.265'/>
+<use x='172.021' xlink:href='#g1-80' y='129.265'/>
+<use x='175.402' xlink:href='#g1-45' y='129.265'/>
+<use x='177.167' xlink:href='#g1-90' y='129.265'/>
+<use x='180.401' xlink:href='#g1-52' y='129.265'/>
+<use x='183.047' xlink:href='#g1-45' y='129.265'/>
+<use x='184.812' xlink:href='#g1-71' y='129.265'/>
+<use x='188.34' xlink:href='#g1-52' y='129.265'/>
+<use x='190.987' xlink:href='#g1-44' y='129.265'/>
+<use x='194.221' xlink:href='#g1-49' y='129.265'/>
+<use x='196.867' xlink:href='#g1-54' y='129.265'/>
+<use x='199.514' xlink:href='#g1-71' y='129.265'/>
+<use x='203.042' xlink:href='#g1-98' y='129.265'/>
+<use x='168.285' xlink:href='#g1-56' y='134.884'/>
+<use x='170.931' xlink:href='#g1-45' y='134.884'/>
+<use x='172.695' xlink:href='#g1-99' y='134.884'/>
+<use x='175.048' xlink:href='#g1-111' y='134.884'/>
+<use x='177.547' xlink:href='#g1-114' y='134.884'/>
+<use x='179.355' xlink:href='#g1-101' y='134.884'/>
+<use x='183.471' xlink:href='#g1-73' y='134.884'/>
+<use x='184.941' xlink:href='#g1-110' y='134.884'/>
+<use x='187.674' xlink:href='#g1-116' y='134.884'/>
+<use x='189.585' xlink:href='#g1-101' y='134.884'/>
+<use x='191.938' xlink:href='#g1-108' y='134.884'/>
+<use x='194.965' xlink:href='#g1-88' y='134.884'/>
+<use x='198.493' xlink:href='#g1-69' y='134.884'/>
+<use x='201.667' xlink:href='#g1-79' y='134.884'/>
+<use x='205.576' xlink:href='#g1-78' y='134.884'/>
+<use x='211.076' xlink:href='#g1-64' y='134.884'/>
+<use x='214.605' xlink:href='#g1-50' y='134.884'/>
+<use x='217.251' xlink:href='#g1-46' y='134.884'/>
+<use x='218.721' xlink:href='#g1-55' y='134.884'/>
+<use x='221.368' xlink:href='#g1-71' y='134.884'/>
+<use x='224.896' xlink:href='#g1-104' y='134.884'/>
+<use x='227.629' xlink:href='#g1-122' y='134.884'/>
+<use x='168.285' xlink:href='#g1-85' y='140.503'/>
+<use x='171.917' xlink:href='#g1-98' y='140.503'/>
+<use x='174.65' xlink:href='#g1-117' y='140.503'/>
+<use x='177.383' xlink:href='#g1-110' y='140.503'/>
+<use x='180.116' xlink:href='#g1-116' y='140.503'/>
+<use x='182.027' xlink:href='#g1-117' y='140.503'/>
+<use x='186.524' xlink:href='#g1-49' y='140.503'/>
+<use x='189.17' xlink:href='#g1-56' y='140.503'/>
+<use x='191.817' xlink:href='#g1-46' y='140.503'/>
+<use x='193.287' xlink:href='#g1-48' y='140.503'/>
+<use x='195.933' xlink:href='#g1-52' y='140.503'/>
+<use x='198.58' xlink:href='#g1-46' y='140.503'/>
+<use x='200.05' xlink:href='#g1-49' y='140.503'/>
+<use x='202.696' xlink:href='#g1-44' y='140.503'/>
+<use x='205.931' xlink:href='#g1-71' y='140.503'/>
+<use x='209.459' xlink:href='#g1-67' y='140.503'/>
+<use x='212.841' xlink:href='#g1-67' y='140.503'/>
+<use x='217.986' xlink:href='#g1-55' y='140.503'/>
+<use x='220.633' xlink:href='#g1-46' y='140.503'/>
+<use x='222.103' xlink:href='#g1-52' y='140.503'/>
+<use x='224.749' xlink:href='#g1-46' y='140.503'/>
+<use x='226.219' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 2.877 285.711)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 61.546 285.711)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 120.215 285.711)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 178.884 285.711)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 237.553 285.711)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 296.222 285.711)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 8.107 282.329)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-53' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 66.776 283.832)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-51' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 125.445 284.96)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 184.114 288.153)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 242.783 335.434)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-50' y='140.503'/>
+<use x='175.048' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 301.452 255.652)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-52' y='140.503'/>
+<use x='175.048' xlink:href='#g1-56' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 13.338 284.96)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 72.007 287.026)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-56' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 130.676 284.02)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-51' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 189.345 280.952)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-56' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 248.014 316.584)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-53' y='140.503'/>
+<use x='175.048' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 306.683 273.5)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-50' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 18.568 281.954)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 77.237 284.146)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-51' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 135.906 275.691)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-49' y='140.503'/>
+<use x='175.048' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 194.575 281.265)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-55' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 253.244 251.394)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-53' y='140.503'/>
+<use x='175.048' xlink:href='#g1-53' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 311.913 282.893)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-52' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 23.799 271.997)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-50' y='140.503'/>
+<use x='175.048' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 82.468 274.815)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-49' y='140.503'/>
+<use x='175.048' xlink:href='#g1-55' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 141.137 248.45)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-54' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 199.806 268.803)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-50' y='140.503'/>
+<use x='175.048' xlink:href='#g1-55' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 258.475 334.432)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-50' y='140.503'/>
+<use x='175.048' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 317.144 285.21)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 29.029 260.537)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-52' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 87.698 257.03)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-52' y='140.503'/>
+<use x='175.048' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 146.367 240.309)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-55' y='140.503'/>
+<use x='175.048' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 205.036 254.713)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-52' y='140.503'/>
+<use x='175.048' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 263.705 317.023)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-53' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 322.374 275.504)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-49' y='140.503'/>
+<use x='175.048' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 34.26 271.433)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-50' y='140.503'/>
+<use x='175.048' xlink:href='#g1-51' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 92.929 274.877)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-49' y='140.503'/>
+<use x='175.048' xlink:href='#g1-55' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 151.598 248.513)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-53' y='140.503'/>
+<use x='175.048' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 210.267 269.68)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-50' y='140.503'/>
+<use x='175.048' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 268.936 334.494)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-50' y='140.503'/>
+<use x='175.048' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 327.605 286.525)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 274.166 314.205)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-53' y='140.503'/>
+<use x='175.048' xlink:href='#g1-52' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 39.49 279.637)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-49' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 98.159 281.828)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 156.828 279.073)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-49' y='140.503'/>
+<use x='175.048' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 215.497 286.149)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-57' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 332.835 279.574)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-49' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 44.72 284.02)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-51' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 103.389 284.333)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 162.058 284.834)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 220.727 284.208)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-50' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 338.065 276.005)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-49' y='140.503'/>
+<use x='175.048' xlink:href='#g1-54' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 49.951 285.147)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 108.62 284.897)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-49' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 167.289 285.648)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-48' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 225.958 287.652)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-57' y='140.503'/>
+<use x='175.048' xlink:href='#g1-55' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 284.627 331.426)'>
+<use x='168.285' xlink:href='#g1-48' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-50' y='140.503'/>
+<use x='175.048' xlink:href='#g1-55' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 343.296 283.832)'>
+<use x='168.285' xlink:href='#g1-49' y='140.503'/>
+<use x='170.931' xlink:href='#g1-46' y='140.503'/>
+<use x='172.401' xlink:href='#g1-48' y='140.503'/>
+<use x='175.048' xlink:href='#g1-51' y='140.503'/>
+</g>
+<g transform='matrix(0 -1 1 0 -27.345 343.63)'>
+<use x='168.285' xlink:href='#g0-82' y='140.503'/>
+<use x='174.255' xlink:href='#g0-101' y='140.503'/>
+<use x='178.351' xlink:href='#g0-108' y='140.503'/>
+<use x='180.551' xlink:href='#g0-97' y='140.503'/>
+<use x='184.979' xlink:href='#g0-116' y='140.503'/>
+<use x='188.307' xlink:href='#g0-105' y='140.503'/>
+<use x='190.507' xlink:href='#g0-118' y='140.503'/>
+<use x='194.755' xlink:href='#g0-101' y='140.503'/>
+<use x='201.922' xlink:href='#g0-114' y='140.503'/>
+<use x='205.07' xlink:href='#g0-115' y='140.503'/>
+<use x='208.603' xlink:href='#g0-115' y='140.503'/>
+<use x='215.207' xlink:href='#g2-40' y='140.503'/>
+<use x='218.5' xlink:href='#g2-108' y='140.503'/>
+<use x='220.521' xlink:href='#g2-111' y='140.503'/>
+<use x='224.52' xlink:href='#g2-119' y='140.503'/>
+<use x='230.068' xlink:href='#g2-101' y='140.503'/>
+<use x='233.832' xlink:href='#g2-114' y='140.503'/>
+<use x='239.547' xlink:href='#g2-105' y='140.503'/>
+<use x='241.567' xlink:href='#g2-115' y='140.503'/>
+<use x='247.636' xlink:href='#g2-98' y='140.503'/>
+<use x='252.244' xlink:href='#g2-101' y='140.503'/>
+<use x='256.008' xlink:href='#g2-116' y='140.503'/>
+<use x='259.066' xlink:href='#g2-116' y='140.503'/>
+<use x='262.124' xlink:href='#g2-101' y='140.503'/>
+<use x='265.887' xlink:href='#g2-114' y='140.503'/>
+<use x='268.779' xlink:href='#g2-41' y='140.503'/>
+</g>
+<path d='M136.149 231.446H402.137V214.453H136.149Z' fill='#ffffff'/>
+<path d='M136.149 231.446H402.137V214.453H136.149Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<path d='M139.336 224.941H142.324V216.972H139.336ZM145.313 224.941H148.301V218.964H145.313Z' fill='#f0e0f0'/>
+<path d='M139.336 224.941H142.324V216.972H139.336ZM145.313 224.941H148.301V218.964H145.313Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -33.522 84.43)'>
+<use x='185.011' xlink:href='#g2-109' y='140.503'/>
+<use x='191.736' xlink:href='#g2-105' y='140.503'/>
+</g>
+<path d='M163.422 224.941H166.41V216.973H163.422ZM169.398 224.941H172.391V218.965H169.398Z' fill='#e1c2e1'/>
+<path d='M163.422 224.941H166.41V216.972H163.422ZM169.398 224.941H172.391V218.964H169.398Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.56 83.976)'>
+<use x='208.137' xlink:href='#g2-116' y='140.503'/>
+<use x='211.195' xlink:href='#g2-99' y='140.503'/>
+</g>
+<path d='M185.586 224.941H188.574V216.973H185.586ZM191.563 224.941H194.555V218.965H191.563Z' fill='#d1a3d1'/>
+<path d='M185.586 224.941H188.574V216.972H185.586ZM191.563 224.941H194.555V218.964H191.563Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.159 84.43)'>
+<use x='229.9' xlink:href='#g2-106' y='140.503'/>
+<use x='232.155' xlink:href='#g2-101' y='140.503'/>
+</g>
+<path d='M206.949 224.941H209.938V216.973H206.949ZM212.926 224.941H215.914V218.965H212.926Z' fill='#c285c2'/>
+<path d='M206.949 224.941H209.938V216.972H206.949ZM212.926 224.941H215.914V218.964H212.926Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.958 83.47)'>
+<use x='252.061' xlink:href='#g2-115' y='140.503'/>
+<use x='255.307' xlink:href='#g2-110' y='140.503'/>
+</g>
+<path d='M229.91 224.941H232.898V216.973H229.91ZM235.887 224.941H238.875V218.965H235.887Z' fill='#b366b3'/>
+<path d='M229.91 224.941H232.898V216.972H229.91ZM235.887 224.941H238.875V218.964H235.887Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -32.781 83.47)'>
+<use x='274.845' xlink:href='#g2-114' y='140.503'/>
+<use x='277.737' xlink:href='#g2-112' y='140.503'/>
+</g>
+<path d='M252.516 224.941H255.504V216.973H252.516ZM258.496 224.941H261.484V218.965H258.496Z' fill='#a447a4'/>
+<path d='M252.516 224.941H255.504V216.972H252.516ZM258.496 224.941H261.484V218.964H258.496Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -39.001 84.467)'>
+<use x='303.672' xlink:href='#g2-104' y='140.503'/>
+<use x='308.045' xlink:href='#g2-111' y='140.503'/>
+<use x='312.279' xlink:href='#g2-97' y='140.503'/>
+<use x='316.112' xlink:href='#g2-114' y='140.503'/>
+<use x='319.004' xlink:href='#g2-100' y='140.503'/>
+</g>
+<path d='M287.563 224.941H290.551V216.973H287.563ZM293.543 224.941H296.531V218.965H293.543Z' fill='#942994'/>
+<path d='M287.563 224.941H290.551V216.972H287.563ZM293.543 224.941H296.531V218.964H293.543Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -37.472 84.467)'>
+<use x='337.19' xlink:href='#g2-103' y='140.503'/>
+<use x='341.424' xlink:href='#g2-108' y='140.503'/>
+<use x='343.444' xlink:href='#g2-105' y='140.503'/>
+<use x='345.464' xlink:href='#g2-98' y='140.503'/>
+<use x='350.072' xlink:href='#g2-99' y='140.503'/>
+</g>
+<path d='M319.551 224.941H322.539V216.973H319.551ZM325.527 224.941H328.52V218.965H325.527Z' fill='#850a85'/>
+<path d='M319.551 224.941H322.539V216.972H319.551ZM325.527 224.941H328.52V218.964H325.527Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -35.051 84.467)'>
+<use x='366.757' xlink:href='#g2-116' y='140.503'/>
+<use x='369.815' xlink:href='#g2-98' y='140.503'/>
+<use x='374.187' xlink:href='#g2-98' y='140.503'/>
+</g>
+<path d='M346.699 224.941H349.688V216.973H346.699ZM352.676 224.941H355.664V218.965H352.676Z' fill='#760076'/>
+<path d='M346.699 224.941H349.688V216.972H346.699ZM352.676 224.941H355.664V218.964H352.676Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -34.135 83.47)'>
+<use x='392.986' xlink:href='#g2-115' y='140.503'/>
+<use x='396.233' xlink:href='#g2-109' y='140.503'/>
+</g>
+<path d='M372.012 224.941H375V216.973H372.012ZM377.988 224.941H380.977V218.965H377.988Z' fill='#670067'/>
+<path d='M372.012 224.941H375V216.972H372.012ZM377.988 224.941H380.977V218.964H377.988Z' fill='none' stroke='#000000' stroke-miterlimit='10' stroke-width='0.399'/>
+<g transform='matrix(1 0 0 1 -35.145 84.43)'>
+<use x='419.31' xlink:href='#g2-115' y='140.503'/>
+<use x='422.556' xlink:href='#g2-109' y='140.503'/>
+<use x='429.281' xlink:href='#g2-105' y='140.503'/>
+</g>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+# Doxyfile 1.8.15
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = mi-malloc
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = 1.6
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO = mimalloc-logo.svg
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = ..
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all generated output in the proper direction.
+# Possible values are: None, LTR, RTL and Context.
+# The default value is: None.
+
+OUTPUT_TEXT_DIRECTION = None
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 2
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+# When you need a literal { or } or , in the value part of an alias you have to
+# escape them by means of a backslash (\), this can lead to conflicts with the
+# commands \{ and \} for these it is advised to use the version @{ and @} or use
+# a double escape (\\{ and \\})
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is
+# Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = YES
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = YES
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 0
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = NO
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation. If
+# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = mimalloc-doc.h
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.idl \
+ *.ddl \
+ *.odl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.cs \
+ *.d \
+ *.php \
+ *.php4 \
+ *.php5 \
+ *.phtml \
+ *.inc \
+ *.m \
+ *.markdown \
+ *.md \
+ *.mm \
+ *.dox \
+ *.py \
+ *.pyw \
+ *.f90 \
+ *.f95 \
+ *.f03 \
+ *.f08 \
+ *.f \
+ *.for \
+ *.tcl \
+ *.vhd \
+ *.vhdl \
+ *.ucf \
+ *.qsf
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# entity all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
+# If clang assisted parsing is enabled you can provide the clang parser with the
+# path to the compilation database (see:
+# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
+# were built. This is equivalent to specifying the "-p" option to a clang tool,
+# such as clang-check. These options will then be passed to the parser.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+
+CLANG_DATABASE_PATH =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = docs
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET = mimalloc-doxygen.css
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 189
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 12
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 240
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via Javascript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have Javascript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: https://developer.apple.com/xcode/), introduced with OSX
+# 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = YES
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 180
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: \makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD = \makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+#error "documentation file only!"
+
+
+/*! \mainpage
+
+This is the API documentation of the
+[mimalloc](https://github.com/microsoft/mimalloc) allocator
+(pronounced "me-malloc") -- a
+general purpose allocator with excellent [performance](bench.html)
+characteristics. Initially
+developed by Daan Leijen for the run-time systems of the
+[Koka](https://github.com/koka-lang/koka) and [Lean](https://github.com/leanprover/lean) languages.
+
+It is a drop-in replacement for `malloc` and can be used in other programs
+without code changes, for example, on Unix you can use it as:
+```
+> LD_PRELOAD=/usr/bin/libmimalloc.so myprogram
+```
+
+Notable aspects of the design include:
+
+- __small and consistent__: the library is less than 6k LOC using simple and
+ consistent data structures. This makes it very suitable
+ to integrate and adapt in other projects. For runtime systems it
+ provides hooks for a monotonic _heartbeat_ and deferred freeing (for
+ bounded worst-case times with reference counting).
+- __free list sharding__: the big idea: instead of one big free list (per size class) we have
+ many smaller lists per memory "page" which both reduces fragmentation
+ and increases locality --
+ things that are allocated close in time get allocated close in memory.
+ (A memory "page" in _mimalloc_ contains blocks of one size class and is
+ usually 64KiB on a 64-bit system).
+- __eager page reset__: when a "page" becomes empty (with increased chance
+ due to free list sharding) the memory is marked to the OS as unused ("reset" or "purged")
+ reducing (real) memory pressure and fragmentation, especially in long running
+ programs.
+- __secure__: _mimalloc_ can be build in secure mode, adding guard pages,
+ randomized allocation, encrypted free lists, etc. to protect against various
+ heap vulnerabilities. The performance penalty is only around 3% on average
+ over our benchmarks.
+- __first-class heaps__: efficiently create and use multiple heaps to allocate across different regions.
+ A heap can be destroyed at once instead of deallocating each object separately.
+- __bounded__: it does not suffer from _blowup_ \[1\], has bounded worst-case allocation
+ times (_wcat_), bounded space overhead (~0.2% meta-data, with at most 12.5% waste in allocation sizes),
+ and has no internal points of contention using only atomic operations.
+- __fast__: In our benchmarks (see [below](#performance)),
+ _mimalloc_ always outperforms all other leading allocators (_jemalloc_, _tcmalloc_, _Hoard_, etc),
+ and usually uses less memory (up to 25% more in the worst case). A nice property
+ is that it does consistently well over a wide range of benchmarks.
+
+You can read more on the design of _mimalloc_ in the
+[technical report](https://www.microsoft.com/en-us/research/publication/mimalloc-free-list-sharding-in-action)
+which also has detailed benchmark results.
+
+
+Further information:
+
+- \ref build
+- \ref using
+- \ref environment
+- \ref overrides
+- \ref bench
+- \ref malloc
+- \ref extended
+- \ref aligned
+- \ref heap
+- \ref typed
+- \ref analysis
+- \ref options
+- \ref posix
+- \ref cpp
+
+*/
+
+
+/// \defgroup malloc Basic Allocation
+/// The basic allocation interface.
+/// \{
+
+
+/// Free previously allocated memory.
+/// The pointer `p` must have been allocated before (or be \a NULL).
+/// @param p pointer to free, or \a NULL.
+void mi_free(void* p);
+
+/// Allocate \a size bytes.
+/// @param size number of bytes to allocate.
+/// @returns pointer to the allocated memory or \a NULL if out of memory.
+/// Returns a unique pointer if called with \a size 0.
+void* mi_malloc(size_t size);
+
+/// Allocate zero-initialized `size` bytes.
+/// @param size The size in bytes.
+/// @returns Pointer to newly allocated zero initialized memory,
+/// or \a NULL if out of memory.
+void* mi_zalloc(size_t size);
+
+/// Allocate zero-initialized \a count elements of \a size bytes.
+/// @param count number of elements.
+/// @param size size of each element.
+/// @returns pointer to the allocated memory
+/// of \a size*\a count bytes, or \a NULL if either out of memory
+/// or when `count*size` overflows.
+///
+/// Returns a unique pointer if called with either \a size or \a count of 0.
+/// @see mi_zalloc()
+void* mi_calloc(size_t count, size_t size);
+
+/// Re-allocate memory to \a newsize bytes.
+/// @param p pointer to previously allocated memory (or \a NULL).
+/// @param newsize the new required size in bytes.
+/// @returns pointer to the re-allocated memory
+/// of \a newsize bytes, or \a NULL if out of memory.
+/// If \a NULL is returned, the pointer \a p is not freed.
+/// Otherwise the original pointer is either freed or returned
+/// as the reallocated result (in case it fits in-place with the
+/// new size). If the pointer \a p is \a NULL, it behaves as
+/// \a mi_malloc(\a newsize). If \a newsize is larger than the
+/// original \a size allocated for \a p, the bytes after \a size
+/// are uninitialized.
+void* mi_realloc(void* p, size_t newsize);
+
+/// Re-allocate memory to \a count elements of \a size bytes, with extra memory initialized to zero.
+/// @param p Pointer to a previously allocated block (or \a NULL).
+/// @param count The number of elements.
+/// @param size The size of each element.
+/// @returns A pointer to a re-allocated block of \a count * \a size bytes, or \a NULL
+/// if out of memory or if \a count * \a size overflows.
+///
+/// If there is no overflow, it behaves exactly like `mi_rezalloc(p,count*size)`.
+/// @see mi_reallocn()
+/// @see [recallocarray()](http://man.openbsd.org/reallocarray) (on BSD).
+void* mi_recalloc(void* p, size_t count, size_t size);
+
+/// Try to re-allocate memory to \a newsize bytes _in place_.
+/// @param p pointer to previously allocated memory (or \a NULL).
+/// @param newsize the new required size in bytes.
+/// @returns pointer to the re-allocated memory
+/// of \a newsize bytes (always equal to \a p),
+/// or \a NULL if either out of memory or if
+/// the memory could not be expanded in place.
+/// If \a NULL is returned, the pointer \a p is not freed.
+/// Otherwise the original pointer is returned
+/// as the reallocated result since it fits in-place with the
+/// new size. If \a newsize is larger than the
+/// original \a size allocated for \a p, the bytes after \a size
+/// are uninitialized.
+void* mi_expand(void* p, size_t newsize);
+
+/// Allocate \a count elements of \a size bytes.
+/// @param count The number of elements.
+/// @param size The size of each element.
+/// @returns A pointer to a block of \a count * \a size bytes, or \a NULL
+/// if out of memory or if \a count * \a size overflows.
+///
+/// If there is no overflow, it behaves exactly like `mi_malloc(p,count*size)`.
+/// @see mi_calloc()
+/// @see mi_zallocn()
+void* mi_mallocn(size_t count, size_t size);
+
+/// Re-allocate memory to \a count elements of \a size bytes.
+/// @param p Pointer to a previously allocated block (or \a NULL).
+/// @param count The number of elements.
+/// @param size The size of each element.
+/// @returns A pointer to a re-allocated block of \a count * \a size bytes, or \a NULL
+/// if out of memory or if \a count * \a size overflows.
+///
+/// If there is no overflow, it behaves exactly like `mi_realloc(p,count*size)`.
+/// @see [reallocarray()](<http://man.openbsd.org/reallocarray>) (on BSD)
+void* mi_reallocn(void* p, size_t count, size_t size);
+
+/// Re-allocate memory to \a newsize bytes,
+/// @param p pointer to previously allocated memory (or \a NULL).
+/// @param newsize the new required size in bytes.
+/// @returns pointer to the re-allocated memory
+/// of \a newsize bytes, or \a NULL if out of memory.
+///
+/// In contrast to mi_realloc(), if \a NULL is returned, the original pointer
+/// \a p is freed (if it was not \a NULL itself).
+/// Otherwise the original pointer is either freed or returned
+/// as the reallocated result (in case it fits in-place with the
+/// new size). If the pointer \a p is \a NULL, it behaves as
+/// \a mi_malloc(\a newsize). If \a newsize is larger than the
+/// original \a size allocated for \a p, the bytes after \a size
+/// are uninitialized.
+///
+/// @see [reallocf](https://www.freebsd.org/cgi/man.cgi?query=reallocf) (on BSD)
+void* mi_reallocf(void* p, size_t newsize);
+
+
+/// Allocate and duplicate a string.
+/// @param s string to duplicate (or \a NULL).
+/// @returns a pointer to newly allocated memory initialized
+/// to string \a s, or \a NULL if either out of memory or if
+/// \a s is \a NULL.
+///
+/// Replacement for the standard [strdup()](http://pubs.opengroup.org/onlinepubs/9699919799/functions/strdup.html)
+/// such that mi_free() can be used on the returned result.
+char* mi_strdup(const char* s);
+
+/// Allocate and duplicate a string up to \a n bytes.
+/// @param s string to duplicate (or \a NULL).
+/// @param n maximum number of bytes to copy (excluding the terminating zero).
+/// @returns a pointer to newly allocated memory initialized
+/// to string \a s up to the first \a n bytes (and always zero terminated),
+/// or \a NULL if either out of memory or if \a s is \a NULL.
+///
+/// Replacement for the standard [strndup()](http://pubs.opengroup.org/onlinepubs/9699919799/functions/strndup.html)
+/// such that mi_free() can be used on the returned result.
+char* mi_strndup(const char* s, size_t n);
+
+/// Resolve a file path name.
+/// @param fname File name.
+/// @param resolved_name Should be \a NULL (but can also point to a buffer
+/// of at least \a PATH_MAX bytes).
+/// @returns If successful a pointer to the resolved absolute file name, or
+/// \a NULL on failure (with \a errno set to the error code).
+///
+/// If \a resolved_name was \a NULL, the returned result should be freed with
+/// mi_free().
+///
+/// Replacement for the standard [realpath()](http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html)
+/// such that mi_free() can be used on the returned result (if \a resolved_name was \a NULL).
+char* mi_realpath(const char* fname, char* resolved_name);
+
+/// \}
+
+// ------------------------------------------------------
+// Extended functionality
+// ------------------------------------------------------
+
+/// \defgroup extended Extended Functions
+/// Extended functionality.
+/// \{
+
+/// Maximum size allowed for small allocations in
+/// #mi_malloc_small and #mi_zalloc_small (usually `128*sizeof(void*)` (= 1KB on 64-bit systems))
+#define MI_SMALL_SIZE_MAX (128*sizeof(void*))
+
+/// Allocate a small object.
+/// @param size The size in bytes, can be at most #MI_SMALL_SIZE_MAX.
+/// @returns a pointer to newly allocated memory of at least \a size
+/// bytes, or \a NULL if out of memory.
+/// This function is meant for use in run-time systems for best
+/// performance and does not check if \a size was indeed small -- use
+/// with care!
+void* mi_malloc_small(size_t size);
+
+/// Allocate a zero initialized small object.
+/// @param size The size in bytes, can be at most #MI_SMALL_SIZE_MAX.
+/// @returns a pointer to newly allocated zero-initialized memory of at
+/// least \a size bytes, or \a NULL if out of memory.
+/// This function is meant for use in run-time systems for best
+/// performance and does not check if \a size was indeed small -- use
+/// with care!
+void* mi_zalloc_small(size_t size);
+
+/// Return the available bytes in a memory block.
+/// @param p Pointer to previously allocated memory (or \a NULL)
+/// @returns Returns the available bytes in the memory block, or
+/// 0 if \a p was \a NULL.
+///
+/// The returned size can be
+/// used to call \a mi_expand successfully.
+/// The returned size is always at least equal to the
+/// allocated size of \a p, and, in the current design,
+/// should be less than 16.7% more.
+///
+/// @see [_msize](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/msize?view=vs-2017) (Windows)
+/// @see [malloc_usable_size](http://man7.org/linux/man-pages/man3/malloc_usable_size.3.html) (Linux)
+/// @see mi_good_size()
+size_t mi_usable_size(void* p);
+
+/// Return the used allocation size.
+/// @param size The minimal required size in bytes.
+/// @returns the size `n` that will be allocated, where `n >= size`.
+///
+/// Generally, `mi_usable_size(mi_malloc(size)) == mi_good_size(size)`.
+/// This can be used to reduce internal wasted space when
+/// allocating buffers for example.
+///
+/// @see mi_usable_size()
+size_t mi_good_size(size_t size);
+
+/// Eagerly free memory.
+/// @param force If \a true, aggressively return memory to the OS (can be expensive!)
+///
+/// Regular code should not have to call this function. It can be beneficial
+/// in very narrow circumstances; in particular, when a long running thread
+/// allocates a lot of blocks that are freed by other threads it may improve
+/// resource usage by calling this every once in a while.
+void mi_collect(bool force);
+
+/// Deprecated
+/// @param out Ignored, outputs to the registered output function or stderr by default.
+///
+/// Most detailed when using a debug build.
+void mi_stats_print(void* out);
+
+/// Print the main statistics.
+/// @param out An output function or \a NULL for the default.
+/// @param arg Optional argument passed to \a out (if not \a NULL)
+///
+/// Most detailed when using a debug build.
+void mi_stats_print_out(mi_output_fun* out, void* arg);
+
+/// Reset statistics.
+void mi_stats_reset(void);
+
+/// Merge thread local statistics with the main statistics and reset.
+void mi_stats_merge(void);
+
+/// Initialize mimalloc on a thread.
+/// Should not be used as on most systems (pthreads, windows) this is done
+/// automatically.
+void mi_thread_init(void);
+
+/// Uninitialize mimalloc on a thread.
+/// Should not be used as on most systems (pthreads, windows) this is done
+/// automatically. Ensures that any memory that is not freed yet (but will
+/// be freed by other threads in the future) is properly handled.
+void mi_thread_done(void);
+
+/// Print out heap statistics for this thread.
+/// @param out An output function or \a NULL for the default.
+/// @param arg Optional argument passed to \a out (if not \a NULL)
+///
+/// Most detailed when using a debug build.
+void mi_thread_stats_print_out(mi_output_fun* out, void* arg);
+
+/// Type of deferred free functions.
+/// @param force If \a true all outstanding items should be freed.
+/// @param heartbeat A monotonically increasing count.
+/// @param arg Argument that was passed at registration to hold extra state.
+///
+/// @see mi_register_deferred_free
+typedef void (mi_deferred_free_fun)(bool force, unsigned long long heartbeat, void* arg);
+
+/// Register a deferred free function.
+/// @param deferred_free Address of a deferred free-ing function or \a NULL to unregister.
+/// @param arg Argument that will be passed on to the deferred free function.
+///
+/// Some runtime systems use deferred free-ing, for example when using
+/// reference counting to limit the worst case free time.
+/// Such systems can register (re-entrant) deferred free function
+/// to free more memory on demand. When the \a force parameter is
+/// \a true all possible memory should be freed.
+/// The per-thread \a heartbeat parameter is monotonically increasing
+/// and guaranteed to be deterministic if the program allocates
+/// deterministically. The \a deferred_free function is guaranteed
+/// to be called deterministically after some number of allocations
+/// (regardless of freeing or available free memory).
+/// At most one \a deferred_free function can be active.
+void mi_register_deferred_free(mi_deferred_free_fun* deferred_free, void* arg);
+
+/// Type of output functions.
+/// @param msg Message to output.
+/// @param arg Argument that was passed at registration to hold extra state.
+///
+/// @see mi_register_output()
+typedef void (mi_output_fun)(const char* msg, void* arg);
+
+/// Register an output function.
+/// @param out The output function, use `NULL` to output to stderr.
+/// @param arg Argument that will be passed on to the output function.
+///
+/// The `out` function is called to output any information from mimalloc,
+/// like verbose or warning messages.
+void mi_register_output(mi_output_fun* out, void* arg);
+
+/// Type of error callback functions.
+/// @param err Error code (see mi_register_error() for a complete list).
+/// @param arg Argument that was passed at registration to hold extra state.
+///
+/// @see mi_register_error()
+typedef void (mi_error_fun)(int err, void* arg);
+
+/// Register an error callback function.
+/// @param errfun The error function that is called on an error (use \a NULL for default)
+/// @param arg Extra argument that will be passed on to the error function.
+///
+/// The \a errfun function is called on an error in mimalloc after emitting
+/// an error message (through the output function). It as always legal to just
+/// return from the \a errfun function in which case allocation functions generally
+/// return \a NULL or ignore the condition. The default function only calls abort()
+/// when compiled in secure mode with an \a EFAULT error. The possible error
+/// codes are:
+/// * \a EAGAIN: Double free was detected (only in debug and secure mode).
+/// * \a EFAULT: Corrupted free list or meta-data was detected (only in debug and secure mode).
+/// * \a ENOMEM: Not enough memory available to satisfy the request.
+/// * \a EOVERFLOW: Too large a request, for example in mi_calloc(), the \a count and \a size parameters are too large.
+/// * \a EINVAL: Trying to free or re-allocate an invalid pointer.
+void mi_register_error(mi_error_fun* errfun, void* arg);
+
+/// Is a pointer part of our heap?
+/// @param p The pointer to check.
+/// @returns \a true if this is a pointer into our heap.
+/// This function is relatively fast.
+bool mi_is_in_heap_region(const void* p);
+
+
+/// Reserve \a pages of huge OS pages (1GiB) evenly divided over \a numa_nodes nodes,
+/// but stops after at most `timeout_msecs` seconds.
+/// @param pages The number of 1GiB pages to reserve.
+/// @param numa_nodes The number of nodes do evenly divide the pages over, or 0 for using the actual number of NUMA nodes.
+/// @param timeout_msecs Maximum number of milli-seconds to try reserving, or 0 for no timeout.
+/// @returns 0 if successfull, \a ENOMEM if running out of memory, or \a ETIMEDOUT if timed out.
+///
+/// The reserved memory is used by mimalloc to satisfy allocations.
+/// May quit before \a timeout_msecs are expired if it estimates it will take more than
+/// 1.5 times \a timeout_msecs. The time limit is needed because on some operating systems
+/// it can take a long time to reserve contiguous memory if the physical memory is
+/// fragmented.
+int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs);
+
+/// Reserve \a pages of huge OS pages (1GiB) at a specific \a numa_node,
+/// but stops after at most `timeout_msecs` seconds.
+/// @param pages The number of 1GiB pages to reserve.
+/// @param numa_node The NUMA node where the memory is reserved (start at 0).
+/// @param timeout_msecs Maximum number of milli-seconds to try reserving, or 0 for no timeout.
+/// @returns 0 if successfull, \a ENOMEM if running out of memory, or \a ETIMEDOUT if timed out.
+///
+/// The reserved memory is used by mimalloc to satisfy allocations.
+/// May quit before \a timeout_msecs are expired if it estimates it will take more than
+/// 1.5 times \a timeout_msecs. The time limit is needed because on some operating systems
+/// it can take a long time to reserve contiguous memory if the physical memory is
+/// fragmented.
+int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs);
+
+
+/// Is the C runtime \a malloc API redirected?
+/// @returns \a true if all malloc API calls are redirected to mimalloc.
+///
+/// Currenty only used on Windows.
+bool mi_is_redirected();
+
+
+/// \}
+
+// ------------------------------------------------------
+// Aligned allocation
+// ------------------------------------------------------
+
+/// \defgroup aligned Aligned Allocation
+///
+/// Allocating aligned memory blocks.
+///
+/// \{
+
+/// Allocate \a size bytes aligned by \a alignment.
+/// @param size number of bytes to allocate.
+/// @param alignment the minimal alignment of the allocated memory.
+/// @returns pointer to the allocated memory or \a NULL if out of memory.
+/// The returned pointer is aligned by \a alignment, i.e.
+/// `(uintptr_t)p % alignment == 0`.
+///
+/// Returns a unique pointer if called with \a size 0.
+/// @see [_aligned_malloc](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-malloc?view=vs-2017) (on Windows)
+/// @see [aligned_alloc](http://man.openbsd.org/reallocarray) (on BSD, with switched arguments!)
+/// @see [posix_memalign](https://linux.die.net/man/3/posix_memalign) (on Posix, with switched arguments!)
+/// @see [memalign](https://linux.die.net/man/3/posix_memalign) (on Linux, with switched arguments!)
+void* mi_malloc_aligned(size_t size, size_t alignment);
+void* mi_zalloc_aligned(size_t size, size_t alignment);
+void* mi_calloc_aligned(size_t count, size_t size, size_t alignment);
+void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment);
+
+/// Allocate \a size bytes aligned by \a alignment at a specified \a offset.
+/// @param size number of bytes to allocate.
+/// @param alignment the minimal alignment of the allocated memory at \a offset.
+/// @param offset the offset that should be aligned.
+/// @returns pointer to the allocated memory or \a NULL if out of memory.
+/// The returned pointer is aligned by \a alignment at \a offset, i.e.
+/// `((uintptr_t)p + offset) % alignment == 0`.
+///
+/// Returns a unique pointer if called with \a size 0.
+/// @see [_aligned_offset_malloc](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-offset-malloc?view=vs-2017) (on Windows)
+void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset);
+void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset);
+void* mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset);
+void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset);
+
+/// \}
+
+/// \defgroup heap Heap Allocation
+///
+/// First-class heaps that can be destroyed in one go.
+///
+/// \{
+
+/// Type of first-class heaps.
+/// A heap can only be used for allocation in
+/// the thread that created this heap! Any allocated
+/// blocks can be freed or reallocated by any other thread though.
+struct mi_heap_s;
+
+/// Type of first-class heaps.
+/// A heap can only be used for (re)allocation in
+/// the thread that created this heap! Any allocated
+/// blocks can be freed by any other thread though.
+typedef struct mi_heap_s mi_heap_t;
+
+/// Create a new heap that can be used for allocation.
+mi_heap_t* mi_heap_new();
+
+/// Delete a previously allocated heap.
+/// This will release resources and migrate any
+/// still allocated blocks in this heap (efficienty)
+/// to the default heap.
+///
+/// If \a heap is the default heap, the default
+/// heap is set to the backing heap.
+void mi_heap_delete(mi_heap_t* heap);
+
+/// Destroy a heap, freeing all its still allocated blocks.
+/// Use with care as this will free all blocks still
+/// allocated in the heap. However, this can be a very
+/// efficient way to free all heap memory in one go.
+///
+/// If \a heap is the default heap, the default
+/// heap is set to the backing heap.
+void mi_heap_destroy(mi_heap_t* heap);
+
+/// Set the default heap to use for mi_malloc() et al.
+/// @param heap The new default heap.
+/// @returns The previous default heap.
+mi_heap_t* mi_heap_set_default(mi_heap_t* heap);
+
+/// Get the default heap that is used for mi_malloc() et al.
+/// @returns The current default heap.
+mi_heap_t* mi_heap_get_default();
+
+/// Get the backing heap.
+/// The _backing_ heap is the initial default heap for
+/// a thread and always available for allocations.
+/// It cannot be destroyed or deleted
+/// except by exiting the thread.
+mi_heap_t* mi_heap_get_backing();
+
+/// Release outstanding resources in a specific heap.
+void mi_heap_collect(mi_heap_t* heap, bool force);
+
+/// Allocate in a specific heap.
+/// @see mi_malloc()
+void* mi_heap_malloc(mi_heap_t* heap, size_t size);
+
+/// Allocate a small object in a specific heap.
+/// \a size must be smaller or equal to MI_SMALL_SIZE_MAX().
+/// @see mi_malloc()
+void* mi_heap_malloc_small(mi_heap_t* heap, size_t size);
+
+/// Allocate zero-initialized in a specific heap.
+/// @see mi_zalloc()
+void* mi_heap_zalloc(mi_heap_t* heap, size_t size);
+
+/// Allocate \a count zero-initialized elements in a specific heap.
+/// @see mi_calloc()
+void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size);
+
+/// Allocate \a count elements in a specific heap.
+/// @see mi_mallocn()
+void* mi_heap_mallocn(mi_heap_t* heap, size_t count, size_t size);
+
+/// Duplicate a string in a specific heap.
+/// @see mi_strdup()
+char* mi_heap_strdup(mi_heap_t* heap, const char* s);
+
+/// Duplicate a string of at most length \a n in a specific heap.
+/// @see mi_strndup()
+char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n);
+
+/// Resolve a file path name using a specific \a heap to allocate the result.
+/// @see mi_realpath()
+char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name);
+
+void* mi_heap_realloc(mi_heap_t* heap, void* p, size_t newsize);
+void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size);
+void* mi_heap_reallocf(mi_heap_t* heap, void* p, size_t newsize);
+
+void* mi_heap_malloc_aligned(mi_heap_t* heap, size_t size, size_t alignment);
+void* mi_heap_malloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset);
+void* mi_heap_zalloc_aligned(mi_heap_t* heap, size_t size, size_t alignment);
+void* mi_heap_zalloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset);
+void* mi_heap_calloc_aligned(mi_heap_t* heap, size_t count, size_t size, size_t alignment);
+void* mi_heap_calloc_aligned_at(mi_heap_t* heap, size_t count, size_t size, size_t alignment, size_t offset);
+void* mi_heap_realloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment);
+void* mi_heap_realloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset);
+
+/// \}
+
+
+/// \defgroup zeroinit Zero initialized re-allocation
+///
+/// The zero-initialized re-allocations are only valid on memory that was
+/// originally allocated with zero initialization too.
+/// e.g. `mi_calloc`, `mi_zalloc`, `mi_zalloc_aligned` etc.
+/// see <https://github.com/microsoft/mimalloc/issues/63#issuecomment-508272992>
+///
+/// \{
+
+void* mi_rezalloc(void* p, size_t newsize);
+void* mi_recalloc(void* p, size_t newcount, size_t size) ;
+
+void* mi_rezalloc_aligned(void* p, size_t newsize, size_t alignment);
+void* mi_rezalloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset);
+void* mi_recalloc_aligned(void* p, size_t newcount, size_t size, size_t alignment);
+void* mi_recalloc_aligned_at(void* p, size_t newcount, size_t size, size_t alignment, size_t offset);
+
+void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize);
+void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t newcount, size_t size);
+
+void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment);
+void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset);
+void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment);
+void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset);
+
+/// \}
+
+/// \defgroup typed Typed Macros
+///
+/// Typed allocation macros. For example:
+/// ```
+/// int* p = mi_malloc_tp(int)
+/// ```
+///
+/// \{
+
+/// Allocate a block of type \a tp.
+/// @param tp The type of the block to allocate.
+/// @returns A pointer to an object of type \a tp, or
+/// \a NULL if out of memory.
+///
+/// **Example:**
+/// ```
+/// int* p = mi_malloc_tp(int)
+/// ```
+///
+/// @see mi_malloc()
+#define mi_malloc_tp(tp) ((tp*)mi_malloc(sizeof(tp)))
+
+/// Allocate a zero-initialized block of type \a tp.
+#define mi_zalloc_tp(tp) ((tp*)mi_zalloc(sizeof(tp)))
+
+/// Allocate \a count zero-initialized blocks of type \a tp.
+#define mi_calloc_tp(tp,count) ((tp*)mi_calloc(count,sizeof(tp)))
+
+/// Allocate \a count blocks of type \a tp.
+#define mi_mallocn_tp(tp,count) ((tp*)mi_mallocn(count,sizeof(tp)))
+
+/// Re-allocate to \a count blocks of type \a tp.
+#define mi_reallocn_tp(p,tp,count) ((tp*)mi_reallocn(p,count,sizeof(tp)))
+
+/// Allocate a block of type \a tp in a heap \a hp.
+#define mi_heap_malloc_tp(hp,tp) ((tp*)mi_heap_malloc(hp,sizeof(tp)))
+
+/// Allocate a zero-initialized block of type \a tp in a heap \a hp.
+#define mi_heap_zalloc_tp(hp,tp) ((tp*)mi_heap_zalloc(hp,sizeof(tp)))
+
+/// Allocate \a count zero-initialized blocks of type \a tp in a heap \a hp.
+#define mi_heap_calloc_tp(hp,tp,count) ((tp*)mi_heap_calloc(hp,count,sizeof(tp)))
+
+/// Allocate \a count blocks of type \a tp in a heap \a hp.
+#define mi_heap_mallocn_tp(hp,tp,count) ((tp*)mi_heap_mallocn(hp,count,sizeof(tp)))
+
+/// Re-allocate to \a count blocks of type \a tp in a heap \a hp.
+#define mi_heap_reallocn_tp(hp,p,tp,count) ((tp*)mi_heap_reallocn(p,count,sizeof(tp)))
+
+/// Re-allocate to \a count zero initialized blocks of type \a tp in a heap \a hp.
+#define mi_heap_recalloc_tp(hp,p,tp,count) ((tp*)mi_heap_recalloc(p,count,sizeof(tp)))
+
+/// \}
+
+/// \defgroup analysis Heap Introspection
+///
+/// Inspect the heap at runtime.
+///
+/// \{
+
+/// Does a heap contain a pointer to a previously allocated block?
+/// @param heap The heap.
+/// @param p Pointer to a previously allocated block (in any heap)-- cannot be some
+/// random pointer!
+/// @returns \a true if the block pointed to by \a p is in the \a heap.
+/// @see mi_heap_check_owned()
+bool mi_heap_contains_block(mi_heap_t* heap, const void* p);
+
+/// Check safely if any pointer is part of a heap.
+/// @param heap The heap.
+/// @param p Any pointer -- not required to be previously allocated by us.
+/// @returns \a true if \a p points to a block in \a heap.
+///
+/// Note: expensive function, linear in the pages in the heap.
+/// @see mi_heap_contains_block()
+/// @see mi_heap_get_default()
+bool mi_heap_check_owned(mi_heap_t* heap, const void* p);
+
+/// Check safely if any pointer is part of the default heap of this thread.
+/// @param p Any pointer -- not required to be previously allocated by us.
+/// @returns \a true if \a p points to a block in default heap of this thread.
+///
+/// Note: expensive function, linear in the pages in the heap.
+/// @see mi_heap_contains_block()
+/// @see mi_heap_get_default()
+bool mi_check_owned(const void* p);
+
+/// An area of heap space contains blocks of a single size.
+/// The bytes in freed blocks are `committed - used`.
+typedef struct mi_heap_area_s {
+ void* blocks; ///< start of the area containing heap blocks
+ size_t reserved; ///< bytes reserved for this area
+ size_t committed; ///< current committed bytes of this area
+ size_t used; ///< bytes in use by allocated blocks
+ size_t block_size; ///< size in bytes of one block
+} mi_heap_area_t;
+
+/// Visitor function passed to mi_heap_visit_blocks()
+/// @returns \a true if ok, \a false to stop visiting (i.e. break)
+///
+/// This function is always first called for every \a area
+/// with \a block as a \a NULL pointer. If \a visit_all_blocks
+/// was \a true, the function is then called for every allocated
+/// block in that area.
+typedef bool (mi_block_visit_fun)(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg);
+
+/// Visit all areas and blocks in a heap.
+/// @param heap The heap to visit.
+/// @param visit_all_blocks If \a true visits all allocated blocks, otherwise
+/// \a visitor is only called for every heap area.
+/// @param visitor This function is called for every area in the heap
+/// (with \a block as \a NULL). If \a visit_all_blocks is
+/// \a true, \a visitor is also called for every allocated
+/// block in every area (with `block!=NULL`).
+/// return \a false from this function to stop visiting early.
+/// @param arg Extra argument passed to \a visitor.
+/// @returns \a true if all areas and blocks were visited.
+bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_all_blocks, mi_block_visit_fun* visitor, void* arg);
+
+/// \}
+
+/// \defgroup options Runtime Options
+///
+/// Set runtime behavior.
+///
+/// \{
+
+/// Runtime options.
+typedef enum mi_option_e {
+ // stable options
+ mi_option_show_errors, ///< Print error messages to `stderr`.
+ mi_option_show_stats, ///< Print statistics to `stderr` when the program is done.
+ mi_option_verbose, ///< Print verbose messages to `stderr`.
+ // the following options are experimental
+ mi_option_eager_commit, ///< Eagerly commit segments (4MiB) (enabled by default).
+ mi_option_eager_region_commit, ///< Eagerly commit large (256MiB) memory regions (enabled by default, except on Windows)
+ mi_option_large_os_pages, ///< Use large OS pages (2MiB in size) if possible
+ mi_option_reserve_huge_os_pages, ///< The number of huge OS pages (1GiB in size) to reserve at the start of the program.
+ mi_option_segment_cache, ///< The number of segments per thread to keep cached.
+ mi_option_page_reset, ///< Reset page memory after \a mi_option_reset_delay milliseconds when it becomes free.
+ mi_option_segment_reset, ///< Experimental
+ mi_option_reset_delay, ///< Delay in milli-seconds before resetting a page (100ms by default)
+ mi_option_use_numa_nodes, ///< Pretend there are at most N NUMA nodes
+ mi_option_reset_decommits, ///< Experimental
+ mi_option_eager_commit_delay, ///< Experimental
+ mi_option_os_tag, ///< OS tag to assign to mimalloc'd memory
+ _mi_option_last
+} mi_option_t;
+
+
+bool mi_option_is_enabled(mi_option_t option);
+void mi_option_enable(mi_option_t option);
+void mi_option_disable(mi_option_t option);
+void mi_option_set_enabled(mi_option_t option, bool enable);
+void mi_option_set_enabled_default(mi_option_t option, bool enable);
+
+long mi_option_get(mi_option_t option);
+void mi_option_set(mi_option_t option, long value);
+void mi_option_set_default(mi_option_t option, long value);
+
+
+/// \}
+
+/// \defgroup posix Posix
+///
+/// `mi_` prefixed implementations of various Posix, Unix, and C++ allocation functions.
+/// Defined for convenience as all redirect to the regular mimalloc API.
+///
+/// \{
+
+void* mi_recalloc(void* p, size_t count, size_t size);
+size_t mi_malloc_size(const void* p);
+size_t mi_malloc_usable_size(const void *p);
+
+/// Just as `free` but also checks if the pointer `p` belongs to our heap.
+void mi_cfree(void* p);
+
+int mi_posix_memalign(void** p, size_t alignment, size_t size);
+int mi__posix_memalign(void** p, size_t alignment, size_t size);
+void* mi_memalign(size_t alignment, size_t size);
+void* mi_valloc(size_t size);
+
+void* mi_pvalloc(size_t size);
+void* mi_aligned_alloc(size_t alignment, size_t size);
+void* mi_reallocarray(void* p, size_t count, size_t size);
+
+void mi_free_size(void* p, size_t size);
+void mi_free_size_aligned(void* p, size_t size, size_t alignment);
+void mi_free_aligned(void* p, size_t alignment);
+
+/// \}
+
+/// \defgroup cpp C++ wrappers
+///
+/// `mi_` prefixed implementations of various allocation functions
+/// that use C++ semantics on out-of-memory, generally calling
+/// `std::get_new_handler` and raising a `std::bad_alloc` exception on failure.
+///
+/// Note: use the `mimalloc-new-delete.h` header to override the \a new
+/// and \a delete operators globally. The wrappers here are mostly
+/// for convience for library writers that need to interface with
+/// mimalloc from C++.
+///
+/// \{
+
+/// like mi_malloc(), but when out of memory, use `std::get_new_handler` and raise `std::bad_alloc` exception on failure.
+void* mi_new(std::size_t n) noexcept(false);
+
+/// like mi_mallocn(), but when out of memory, use `std::get_new_handler` and raise `std::bad_alloc` exception on failure.
+void* mi_new_n(size_t count, size_t size) noexcept(false);
+
+/// like mi_malloc_aligned(), but when out of memory, use `std::get_new_handler` and raise `std::bad_alloc` exception on failure.
+void* mi_new_aligned(std::size_t n, std::align_val_t alignment) noexcept(false);
+
+/// like `mi_malloc`, but when out of memory, use `std::get_new_handler` but return \a NULL on failure.
+void* mi_new_nothrow(size_t n);
+
+/// like `mi_malloc_aligned`, but when out of memory, use `std::get_new_handler` but return \a NULL on failure.
+void* mi_new_aligned_nothrow(size_t n, size_t alignment);
+
+/// like mi_realloc(), but when out of memory, use `std::get_new_handler` and raise `std::bad_alloc` exception on failure.
+void* mi_new_realloc(void* p, size_t newsize);
+
+/// like mi_reallocn(), but when out of memory, use `std::get_new_handler` and raise `std::bad_alloc` exception on failure.
+void* mi_new_reallocn(void* p, size_t newcount, size_t size);
+
+/// \a std::allocator implementation for mimalloc for use in STL containers.
+/// For example:
+/// ```
+/// std::vector<int, mi_stl_allocator<int> > vec;
+/// vec.push_back(1);
+/// vec.pop_back();
+/// ```
+template<class T> struct mi_stl_allocator { }
+
+/// \}
+
+/*! \page build Building
+
+Checkout the sources from Github:
+```
+git clone https://github.com/microsoft/mimalloc
+```
+
+## Windows
+
+Open `ide/vs2019/mimalloc.sln` in Visual Studio 2019 and build (or `ide/vs2017/mimalloc.sln`).
+The `mimalloc` project builds a static library (in `out/msvc-x64`), while the
+`mimalloc-override` project builds a DLL for overriding malloc
+in the entire program.
+
+## macOS, Linux, BSD, etc.
+
+We use [`cmake`](https://cmake.org)<sup>1</sup> as the build system:
+
+```
+> mkdir -p out/release
+> cd out/release
+> cmake ../..
+> make
+```
+This builds the library as a shared (dynamic)
+library (`.so` or `.dylib`), a static library (`.a`), and
+as a single object file (`.o`).
+
+`> sudo make install` (install the library and header files in `/usr/local/lib` and `/usr/local/include`)
+
+You can build the debug version which does many internal checks and
+maintains detailed statistics as:
+
+```
+> mkdir -p out/debug
+> cd out/debug
+> cmake -DCMAKE_BUILD_TYPE=Debug ../..
+> make
+```
+This will name the shared library as `libmimalloc-debug.so`.
+
+Finally, you can build a _secure_ version that uses guard pages, encrypted
+free lists, etc, as:
+```
+> mkdir -p out/secure
+> cd out/secure
+> cmake -DMI_SECURE=ON ../..
+> make
+```
+This will name the shared library as `libmimalloc-secure.so`.
+Use `ccmake`<sup>2</sup> instead of `cmake`
+to see and customize all the available build options.
+
+Notes:
+1. Install CMake: `sudo apt-get install cmake`
+2. Install CCMake: `sudo apt-get install cmake-curses-gui`
+
+*/
+
+/*! \page using Using the library
+
+### Build
+
+The preferred usage is including `<mimalloc.h>`, linking with
+the shared- or static library, and using the `mi_malloc` API exclusively for allocation. For example,
+```
+gcc -o myprogram -lmimalloc myfile.c
+```
+
+mimalloc uses only safe OS calls (`mmap` and `VirtualAlloc`) and can co-exist
+with other allocators linked to the same program.
+If you use `cmake`, you can simply use:
+```
+find_package(mimalloc 1.0 REQUIRED)
+```
+in your `CMakeLists.txt` to find a locally installed mimalloc. Then use either:
+```
+target_link_libraries(myapp PUBLIC mimalloc)
+```
+to link with the shared (dynamic) library, or:
+```
+target_link_libraries(myapp PUBLIC mimalloc-static)
+```
+to link with the static library. See `test\CMakeLists.txt` for an example.
+
+### C++
+For best performance in C++ programs, it is also recommended to override the
+global `new` and `delete` operators. For convience, mimalloc provides
+[`mimalloc-new-delete.h`](https://github.com/microsoft/mimalloc/blob/master/include/mimalloc-new-delete.h) which does this for you -- just include it in a single(!) source file in your project.
+
+In C++, mimalloc also provides the `mi_stl_allocator` struct which implements the `std::allocator`
+interface. For example:
+```
+std::vector<some_struct, mi_stl_allocator<some_struct>> vec;
+vec.push_back(some_struct());
+```
+
+### Statistics
+
+You can pass environment variables to print verbose messages (`MIMALLOC_VERBOSE=1`)
+and statistics (`MIMALLOC_SHOW_STATS=1`) (in the debug version):
+```
+> env MIMALLOC_SHOW_STATS=1 ./cfrac 175451865205073170563711388363
+
+175451865205073170563711388363 = 374456281610909315237213 * 468551
+
+heap stats: peak total freed unit
+normal 2: 16.4 kb 17.5 mb 17.5 mb 16 b ok
+normal 3: 16.3 kb 15.2 mb 15.2 mb 24 b ok
+normal 4: 64 b 4.6 kb 4.6 kb 32 b ok
+normal 5: 80 b 118.4 kb 118.4 kb 40 b ok
+normal 6: 48 b 48 b 48 b 48 b ok
+normal 17: 960 b 960 b 960 b 320 b ok
+
+heap stats: peak total freed unit
+ normal: 33.9 kb 32.8 mb 32.8 mb 1 b ok
+ huge: 0 b 0 b 0 b 1 b ok
+ total: 33.9 kb 32.8 mb 32.8 mb 1 b ok
+malloc requested: 32.8 mb
+
+ committed: 58.2 kb 58.2 kb 58.2 kb 1 b ok
+ reserved: 2.0 mb 2.0 mb 2.0 mb 1 b ok
+ reset: 0 b 0 b 0 b 1 b ok
+ segments: 1 1 1
+-abandoned: 0
+ pages: 6 6 6
+-abandoned: 0
+ mmaps: 3
+ mmap fast: 0
+ mmap slow: 1
+ threads: 0
+ elapsed: 2.022s
+ process: user: 1.781s, system: 0.016s, faults: 756, reclaims: 0, rss: 2.7 mb
+```
+
+The above model of using the `mi_` prefixed API is not always possible
+though in existing programs that already use the standard malloc interface,
+and another option is to override the standard malloc interface
+completely and redirect all calls to the _mimalloc_ library instead.
+
+See \ref overrides for more info.
+
+*/
+
+/*! \page environment Environment Options
+
+You can set further options either programmatically (using [`mi_option_set`](https://microsoft.github.io/mimalloc/group__options.html)),
+or via environment variables.
+
+- `MIMALLOC_SHOW_STATS=1`: show statistics when the program terminates.
+- `MIMALLOC_VERBOSE=1`: show verbose messages.
+- `MIMALLOC_SHOW_ERRORS=1`: show error and warning messages.
+- `MIMALLOC_PAGE_RESET=0`: by default, mimalloc will reset (or purge) OS pages when not in use to signal to the OS
+ that the underlying physical memory can be reused. This can reduce memory fragmentation in long running (server)
+ programs. By setting it to `0` no such page resets will be done which can improve performance for programs that are not long
+ running. As an alternative, the `MIMALLOC_RESET_DELAY=`<msecs> can be set higher (100ms by default) to make the page
+ reset occur less frequently instead of turning it off completely.
+- `MIMALLOC_LARGE_OS_PAGES=1`: use large OS pages (2MiB) when available; for some workloads this can significantly
+ improve performance. Use `MIMALLOC_VERBOSE` to check if the large OS pages are enabled -- usually one needs
+ to explicitly allow large OS pages (as on [Windows][windows-huge] and [Linux][linux-huge]). However, sometimes
+ the OS is very slow to reserve contiguous physical memory for large OS pages so use with care on systems that
+ can have fragmented memory (for that reason, we generally recommend to use `MIMALLOC_RESERVE_HUGE_OS_PAGES` instead when possible).
+- `MIMALLOC_RESERVE_HUGE_OS_PAGES=N`: where N is the number of 1GiB _huge_ OS pages. This reserves the huge pages at
+ startup and sometimes this can give a large (latency) performance improvement on big workloads.
+ Usually it is better to not use
+ `MIMALLOC_LARGE_OS_PAGES` in combination with this setting. Just like large OS pages, use with care as reserving
+ contiguous physical memory can take a long time when memory is fragmented (but reserving the huge pages is done at
+ startup only once).
+ Note that we usually need to explicitly enable huge OS pages (as on [Windows][windows-huge] and [Linux][linux-huge])). With huge OS pages, it may be beneficial to set the setting
+ `MIMALLOC_EAGER_COMMIT_DELAY=N` (`N` is 1 by default) to delay the initial `N` segments (of 4MiB)
+ of a thread to not allocate in the huge OS pages; this prevents threads that are short lived
+ and allocate just a little to take up space in the huge OS page area (which cannot be reset).
+
+Use caution when using `fork` in combination with either large or huge OS pages: on a fork, the OS uses copy-on-write
+for all pages in the original process including the huge OS pages. When any memory is now written in that area, the
+OS will copy the entire 1GiB huge page (or 2MiB large page) which can cause the memory usage to grow in big increments.
+
+[linux-huge]: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/tuning_and_optimizing_red_hat_enterprise_linux_for_oracle_9i_and_10g_databases/sect-oracle_9i_and_10g_tuning_guide-large_memory_optimization_big_pages_and_huge_pages-configuring_huge_pages_in_red_hat_enterprise_linux_4_or_5
+[windows-huge]: https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows?view=sql-server-2017
+
+*/
+
+/*! \page overrides Overriding Malloc
+
+Overriding the standard `malloc` can be done either _dynamically_ or _statically_.
+
+## Dynamic override
+
+This is the recommended way to override the standard malloc interface.
+
+
+### Linux, BSD
+
+On these systems we preload the mimalloc shared
+library so all calls to the standard `malloc` interface are
+resolved to the _mimalloc_ library.
+
+- `env LD_PRELOAD=/usr/lib/libmimalloc.so myprogram`
+
+You can set extra environment variables to check that mimalloc is running,
+like:
+```
+env MIMALLOC_VERBOSE=1 LD_PRELOAD=/usr/lib/libmimalloc.so myprogram
+```
+or run with the debug version to get detailed statistics:
+```
+env MIMALLOC_SHOW_STATS=1 LD_PRELOAD=/usr/lib/libmimalloc-debug.so myprogram
+```
+
+### MacOS
+
+On macOS we can also preload the mimalloc shared
+library so all calls to the standard `malloc` interface are
+resolved to the _mimalloc_ library.
+
+- `env DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=/usr/lib/libmimalloc.dylib myprogram`
+
+Note that certain security restrictions may apply when doing this from
+the [shell](https://stackoverflow.com/questions/43941322/dyld-insert-libraries-ignored-when-calling-application-through-bash).
+
+(Note: macOS support for dynamic overriding is recent, please report any issues.)
+
+
+### Windows
+
+Overriding on Windows is robust and has the
+particular advantage to be able to redirect all malloc/free calls that go through
+the (dynamic) C runtime allocator, including those from other DLL's or libraries.
+
+The overriding on Windows requires that you link your program explicitly with
+the mimalloc DLL and use the C-runtime library as a DLL (using the `/MD` or `/MDd` switch).
+Also, the `mimalloc-redirect.dll` (or `mimalloc-redirect32.dll`) must be available
+in the same folder as the main `mimalloc-override.dll` at runtime (as it is a dependency).
+The redirection DLL ensures that all calls to the C runtime malloc API get redirected to
+mimalloc (in `mimalloc-override.dll`).
+
+To ensure the mimalloc DLL is loaded at run-time it is easiest to insert some
+call to the mimalloc API in the `main` function, like `mi_version()`
+(or use the `/INCLUDE:mi_version` switch on the linker). See the `mimalloc-override-test` project
+for an example on how to use this. For best performance on Windows with C++, it
+is also recommended to also override the `new`/`delete` operations (by including
+[`mimalloc-new-delete.h`](https://github.com/microsoft/mimalloc/blob/master/include/mimalloc-new-delete.h) a single(!) source file in your project).
+
+The environment variable `MIMALLOC_DISABLE_REDIRECT=1` can be used to disable dynamic
+overriding at run-time. Use `MIMALLOC_VERBOSE=1` to check if mimalloc was successfully redirected.
+
+(Note: in principle, it is possible to even patch existing executables without any recompilation
+if they are linked with the dynamic C runtime (`ucrtbase.dll`) -- just put the `mimalloc-override.dll`
+into the import table (and put `mimalloc-redirect.dll` in the same folder)
+Such patching can be done for example with [CFF Explorer](https://ntcore.com/?page_id=388)).
+
+
+## Static override
+
+On Unix systems, you can also statically link with _mimalloc_ to override the standard
+malloc interface. The recommended way is to link the final program with the
+_mimalloc_ single object file (`mimalloc-override.o`). We use
+an object file instead of a library file as linkers give preference to
+that over archives to resolve symbols. To ensure that the standard
+malloc interface resolves to the _mimalloc_ library, link it as the first
+object file. For example:
+
+```
+gcc -o myprogram mimalloc-override.o myfile1.c ...
+```
+
+## List of Overrides:
+
+The specific functions that get redirected to the _mimalloc_ library are:
+
+```
+// C
+void* malloc(size_t size);
+void* calloc(size_t size, size_t n);
+void* realloc(void* p, size_t newsize);
+void free(void* p);
+
+// C++
+void operator delete(void* p);
+void operator delete[](void* p);
+
+void* operator new(std::size_t n) noexcept(false);
+void* operator new[](std::size_t n) noexcept(false);
+void* operator new( std::size_t n, std::align_val_t align) noexcept(false);
+void* operator new[]( std::size_t n, std::align_val_t align) noexcept(false);
+
+void* operator new ( std::size_t count, const std::nothrow_t& tag);
+void* operator new[]( std::size_t count, const std::nothrow_t& tag);
+void* operator new ( std::size_t count, std::align_val_t al, const std::nothrow_t&);
+void* operator new[]( std::size_t count, std::align_val_t al, const std::nothrow_t&);
+
+// Posix
+int posix_memalign(void** p, size_t alignment, size_t size);
+
+// Linux
+void* memalign(size_t alignment, size_t size);
+void* aligned_alloc(size_t alignment, size_t size);
+void* valloc(size_t size);
+void* pvalloc(size_t size);
+size_t malloc_usable_size(void *p);
+
+// BSD
+void* reallocarray( void* p, size_t count, size_t size );
+void* reallocf(void* p, size_t newsize);
+void cfree(void* p);
+
+// Windows
+void* _expand(void* p, size_t newsize);
+size_t _msize(void* p);
+
+void* _malloc_dbg(size_t size, int block_type, const char* fname, int line);
+void* _realloc_dbg(void* p, size_t newsize, int block_type, const char* fname, int line);
+void* _calloc_dbg(size_t count, size_t size, int block_type, const char* fname, int line);
+void* _expand_dbg(void* p, size_t size, int block_type, const char* fname, int line);
+size_t _msize_dbg(void* p, int block_type);
+void _free_dbg(void* p, int block_type);
+```
+
+*/
+
+/*! \page bench Performance
+
+We tested _mimalloc_ against many other top allocators over a wide
+range of benchmarks, ranging from various real world programs to
+synthetic benchmarks that see how the allocator behaves under more
+extreme circumstances.
+
+In our benchmarks, _mimalloc_ always outperforms all other leading
+allocators (_jemalloc_, _tcmalloc_, _Hoard_, etc) (Apr 2019),
+and usually uses less memory (up to 25% more in the worst case).
+A nice property is that it does *consistently* well over the wide
+range of benchmarks.
+
+See the [Performance](https://github.com/microsoft/mimalloc#Performance)
+section in the _mimalloc_ repository for benchmark results,
+or the the technical report for detailed benchmark results.
+
+*/
--- /dev/null
+#projectlogo img {
+ padding: 1ex;
+}
+tt, code, kbd, samp, div.memproto, div.fragment, div.line, table.memname {
+ font-family: Consolas, Monaco, Inconsolata, "Courier New", monospace;
+}
+.image img, .textblock img {
+ max-width: 99%;
+ max-height: 350px;
+}
+table.memname, .memname{
+ font-weight: bold;
+}
+code {
+ background-color: #EEE;
+ padding: 0ex 0.25ex;
+}
+body {
+ margin: 1ex 1ex 0ex 1ex;
+ border: 1px solid black;
+}
+.contents table, .contents div, .contents p, .contents dl {
+ font-size: 16px;
+ line-height: 1.44;
+}
+body #nav-tree .label {
+ font-size: 14px;
+}
+a{
+ text-decoration: underline;
+}
+#side-nav {
+ margin-left: 1ex;
+ border-left: 1px solid black;
+}
+#nav-tree {
+ padding-left: 1ex;
+}
+#nav-path {
+ display: none;
+}
+div.fragment {
+ background-color: #EEE;
+ padding: 0.25ex 0.5ex;
+ border-color: black;
+}
+#nav-sync img {
+ display: none;
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="20mm"
+ height="20mm"
+ viewBox="0 0 10 10"
+ version="1.1"
+ id="svg8"
+ sodipodi:docname="mimalloc-logo.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <defs
+ id="defs2">
+ <linearGradient
+ id="linearGradient6471"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#008da3;stop-opacity:1;"
+ offset="0"
+ id="stop6469" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="23.706667"
+ inkscape:cx="24.864771"
+ inkscape:cy="35.79485"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="3840"
+ inkscape:window-height="2050"
+ inkscape:window-x="-12"
+ inkscape:window-y="-12"
+ inkscape:window-maximized="1"
+ inkscape:snap-object-midpoints="false"
+ inkscape:snap-bbox="false"
+ inkscape:snap-bbox-midpoints="false"
+ inkscape:bbox-nodes="false"
+ inkscape:bbox-paths="false"
+ inkscape:snap-bbox-edge-midpoints="false"
+ showguides="false"
+ showborder="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid815"
+ units="mm"
+ spacingx="0.99999997"
+ spacingy="0.99999997" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-287)">
+ <circle
+ id="path840"
+ cx="5"
+ cy="292"
+ style="fill:#0d8ca4;fill-opacity:0.64444448;fill-rule:nonzero;stroke:#000000;stroke-width:0.56603777;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ r="4.7169809" />
+ <ellipse
+ id="path4522"
+ style="fill:none;stroke:#000000;stroke-width:0.6;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+ cx="5.171"
+ cy="292"
+ r="4.8711185" />
+ <g
+ aria-label="malloc"
+ transform="matrix(0.9031136,0,0,0.80782132,0.58122269,37.023319)"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;line-height:1.25;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.28599727"
+ id="text6501">
+ <path
+ inkscape:connector-curvature="0"
+ d="m 1.7799307,318.87079 c 0.029047,0 0.053624,-0.009 0.075968,-0.0313 0.020109,-0.0201 0.031281,-0.0447 0.031281,-0.076 v -0.70159 c 0.013406,-0.0223 0.031281,-0.0469 0.05139,-0.0693 0.017875,-0.0223 0.049156,-0.0447 0.089374,-0.0693 0.040218,-0.0223 0.082671,-0.0358 0.1273581,-0.0358 0.040218,0 0.078202,0.0179 0.1117177,0.0536 0.031281,0.0358 0.049156,0.0827 0.049156,0.143 v 0.67924 c 0,0.0313 0.00894,0.0559 0.031281,0.076 0.020109,0.0223 0.044687,0.0313 0.075968,0.0313 0.029047,0 0.053624,-0.009 0.075968,-0.0313 0.020109,-0.0201 0.031281,-0.0447 0.031281,-0.076 v -0.67924 -0.0223 c 0.0067,-0.0112 0.01564,-0.0223 0.024578,-0.0358 0.00894,-0.0134 0.022344,-0.0268 0.040218,-0.0447 0.017875,-0.0179 0.03575,-0.0335 0.053624,-0.0469 0.017875,-0.0134 0.042453,-0.0246 0.069265,-0.0335 0.026812,-0.009 0.053625,-0.0134 0.080437,-0.0134 0.040218,0 0.078202,0.0179 0.1117177,0.0536 0.031281,0.0358 0.049156,0.0827 0.049156,0.143 v 0.67924 c 0,0.0313 0.00894,0.0559 0.031281,0.076 0.020109,0.0223 0.044687,0.0313 0.075968,0.0313 0.029047,0 0.053624,-0.009 0.075968,-0.0313 0.020109,-0.0201 0.031281,-0.0447 0.031281,-0.076 v -0.67924 c 0,-0.11396 -0.037984,-0.21003 -0.1094833,-0.29047 -0.073734,-0.0804 -0.1631078,-0.12066 -0.2658881,-0.12066 -0.073734,0 -0.1407643,0.0156 -0.1988575,0.0425 -0.058093,0.0268 -0.1094833,0.0626 -0.1541704,0.10725 -0.075968,-0.0983 -0.1720452,-0.14971 -0.290466,-0.14971 -0.1027802,0 -0.1943887,0.029 -0.2748255,0.0849 -0.00894,-0.0179 -0.022343,-0.0335 -0.040218,-0.0469 -0.017875,-0.0134 -0.037984,-0.0201 -0.060328,-0.0201 -0.031281,0 -0.055859,0.0112 -0.075968,0.0313 -0.022343,0.0223 -0.031281,0.0469 -0.031281,0.076 v 0.96524 c 0,0.0313 0.00894,0.0559 0.031281,0.076 0.020109,0.0223 0.044687,0.0313 0.075968,0.0313 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.28599727"
+ id="path6515" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 4.2824069,317.69105 c -0.01564,0 -0.029047,0.004 -0.042453,0.009 -0.013406,0.007 -0.026812,0.0156 -0.03575,0.0246 -0.00894,0.0112 -0.017875,0.0223 -0.022344,0.0335 -0.078202,-0.0559 -0.1631079,-0.0849 -0.2569507,-0.0849 -0.145233,0 -0.2658881,0.0626 -0.359731,0.18322 -0.093843,0.12066 -0.1407642,0.26366 -0.1407642,0.42453 0,0.16311 0.046921,0.30611 0.1407642,0.42676 0.093843,0.12066 0.214498,0.18098 0.359731,0.18098 0.093843,0 0.1787483,-0.0268 0.2569507,-0.0849 0.00894,0.0201 0.022344,0.0358 0.040218,0.0491 0.017875,0.0134 0.037984,0.0179 0.060328,0.0179 0.029047,0 0.053625,-0.009 0.075968,-0.0313 0.020109,-0.0201 0.031281,-0.0447 0.031281,-0.076 v -0.96524 c 0,-0.029 -0.011172,-0.0536 -0.031281,-0.076 -0.022343,-0.0201 -0.046921,-0.0313 -0.075968,-0.0313 z m -0.107249,0.77979 c -0.017875,0.0424 -0.040218,0.0782 -0.067031,0.10948 -0.026812,0.0313 -0.055859,0.0559 -0.08714,0.0715 -0.031281,0.0156 -0.064796,0.0224 -0.096077,0.0224 -0.073734,0 -0.1407643,-0.0358 -0.1988575,-0.10949 -0.058093,-0.0737 -0.08714,-0.16757 -0.08714,-0.28376 0,-0.11395 0.029047,-0.2078 0.08714,-0.28153 0.058093,-0.0737 0.1251238,-0.11172 0.1988575,-0.11172 0.049156,0 0.098312,0.0179 0.1429986,0.0536 0.044687,0.0358 0.080437,0.0871 0.107249,0.1497 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.28599727"
+ id="path6517" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 4.7471525,317.2263 c -0.031281,0 -0.055859,0.0112 -0.075968,0.0313 -0.022343,0.0223 -0.031281,0.0469 -0.031281,0.076 v 1.21549 c 0,0.10502 0.0067,0.18545 0.022343,0.24131 0.00447,0.0268 0.017875,0.0469 0.037984,0.0603 0.017875,0.0134 0.040218,0.0201 0.064796,0.0201 0.00894,0 0.017875,0 0.026812,-0.002 0.00447,-0.002 0.011172,-0.004 0.017875,-0.009 0.0067,-0.002 0.013406,-0.004 0.017875,-0.009 0.00447,-0.004 0.011172,-0.009 0.01564,-0.0134 0.00447,-0.004 0.0067,-0.0112 0.011172,-0.0156 0.00447,-0.004 0.0067,-0.0112 0.00894,-0.0179 0.00223,-0.007 0.00447,-0.0134 0.0067,-0.0201 0,-0.007 0.00223,-0.0134 0.00223,-0.0201 v -0.007 c 0,-0.002 -0.00223,-0.004 -0.00223,-0.007 0,-0.002 0,-0.004 0,-0.009 -0.011172,-0.0447 -0.01564,-0.10725 -0.01564,-0.19216 v -1.21549 c 0,-0.029 -0.011172,-0.0536 -0.031281,-0.076 -0.022343,-0.0201 -0.046921,-0.0313 -0.075968,-0.0313 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.28599727"
+ id="path6519" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 5.2655226,317.2263 c -0.031281,0 -0.055859,0.0112 -0.075968,0.0313 -0.022343,0.0223 -0.031281,0.0469 -0.031281,0.076 v 1.21549 c 0,0.10502 0.0067,0.18545 0.022344,0.24131 0.00447,0.0268 0.017875,0.0469 0.037984,0.0603 0.017875,0.0134 0.040218,0.0201 0.064796,0.0201 0.00894,0 0.017875,0 0.026812,-0.002 0.00447,-0.002 0.011172,-0.004 0.017875,-0.009 0.0067,-0.002 0.013406,-0.004 0.017875,-0.009 0.00447,-0.004 0.011172,-0.009 0.01564,-0.0134 0.00447,-0.004 0.0067,-0.0112 0.011172,-0.0156 0.00447,-0.004 0.0067,-0.0112 0.00894,-0.0179 0.00223,-0.007 0.00447,-0.0134 0.0067,-0.0201 0,-0.007 0.00223,-0.0134 0.00223,-0.0201 v -0.007 c 0,-0.002 -0.00223,-0.004 -0.00223,-0.007 0,-0.002 0,-0.004 0,-0.009 -0.011172,-0.0447 -0.01564,-0.10725 -0.01564,-0.19216 v -1.21549 c 0,-0.029 -0.011172,-0.0536 -0.031281,-0.076 -0.022343,-0.0201 -0.046921,-0.0313 -0.075968,-0.0313 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.28599727"
+ id="path6521" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 6.6061344,318.28092 c 0,-0.16087 -0.049156,-0.30387 -0.1429986,-0.42453 -0.093843,-0.12065 -0.2144979,-0.18322 -0.3574966,-0.18322 -0.145233,0 -0.2658881,0.0626 -0.3597309,0.18322 -0.093843,0.12066 -0.1407643,0.26366 -0.1407643,0.42453 0,0.16311 0.046921,0.30611 0.1407643,0.42676 0.093843,0.12066 0.2144979,0.18098 0.3597309,0.18098 0.1429987,0 0.2636538,-0.0603 0.3574966,-0.18098 0.093843,-0.12065 0.1429986,-0.26365 0.1429986,-0.42676 z m -0.2144979,0 c 0,0.11619 -0.031281,0.21003 -0.089374,0.28376 -0.058093,0.0737 -0.1251238,0.10949 -0.1966231,0.10949 -0.073734,0 -0.1407643,-0.0358 -0.1988575,-0.10949 -0.058093,-0.0737 -0.08714,-0.16757 -0.08714,-0.28376 0,-0.11395 0.029047,-0.2078 0.08714,-0.28153 0.058093,-0.0737 0.1251238,-0.11172 0.1988575,-0.11172 0.071499,0 0.1385299,0.038 0.1966231,0.11172 0.058093,0.0737 0.089374,0.16758 0.089374,0.28153 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.28599727"
+ id="path6523" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 7.6406407,318.47754 c -0.00894,-0.004 -0.01564,-0.007 -0.024578,-0.009 -0.00894,-0.002 -0.017875,-0.004 -0.026812,-0.004 -0.044687,0 -0.075968,0.0201 -0.093843,0.0559 -0.026812,0.0514 -0.060328,0.0894 -0.098312,0.11618 -0.040218,0.0268 -0.080437,0.038 -0.1206551,0.038 -0.071499,0 -0.1340612,-0.0358 -0.1921544,-0.10949 -0.058093,-0.0737 -0.084905,-0.16757 -0.084905,-0.28376 0,-0.11395 0.026812,-0.2078 0.084905,-0.28153 0.058093,-0.0737 0.1206551,-0.11172 0.1921544,-0.11172 0.080437,0 0.1474674,0.0447 0.2055606,0.1296 0.020109,0.0335 0.049156,0.0491 0.089374,0.0491 0.022343,0 0.040218,-0.004 0.058093,-0.0179 0.0067,-0.004 0.013406,-0.009 0.020109,-0.0156 0.0067,-0.007 0.011172,-0.0134 0.01564,-0.0224 0.00447,-0.009 0.0067,-0.0156 0.00894,-0.0246 0.00223,-0.009 0.00447,-0.0179 0.00447,-0.0268 0,-0.004 -0.00223,-0.0112 -0.00223,-0.0156 0,-0.004 -0.00223,-0.009 -0.00223,-0.0134 -0.00223,-0.004 -0.00447,-0.0112 -0.0067,-0.0156 -0.00223,-0.004 -0.00447,-0.009 -0.0067,-0.0134 -0.031281,-0.0447 -0.064796,-0.0827 -0.1027803,-0.11619 -0.037984,-0.0335 -0.080437,-0.0603 -0.1295925,-0.0804 -0.049156,-0.0201 -0.1005459,-0.0313 -0.1519361,-0.0313 -0.1429986,0 -0.2614194,0.0626 -0.3530279,0.18322 -0.093843,0.12066 -0.1385299,0.26366 -0.1385299,0.42453 0,0.16311 0.044687,0.30611 0.1385299,0.42676 0.091608,0.12066 0.2100293,0.18098 0.3530279,0.18098 0.084905,0 0.1631078,-0.0246 0.2346072,-0.0737 0.071499,-0.0492 0.1273581,-0.11172 0.1720452,-0.19215 0.0067,-0.0134 0.011172,-0.0313 0.013406,-0.0514 0,-0.0425 -0.020109,-0.0737 -0.055859,-0.0939 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.28599727"
+ id="path6525" />
+ </g>
+ <g
+ aria-label="m"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.3694315px;line-height:1.25;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.15923578"
+ id="text848">
+ <path
+ d="m 2.3718985,293.17081 c 0.080862,0 0.1492836,-0.0249 0.211485,-0.0871 0.055981,-0.056 0.087082,-0.1244 0.087082,-0.21148 v -1.95313 c 0.037321,-0.0622 0.087082,-0.13062 0.1430634,-0.19282 0.049761,-0.0622 0.1368433,-0.1244 0.2488059,-0.19283 0.1119627,-0.0622 0.2301455,-0.0995 0.3545485,-0.0995 0.1119626,0 0.2177051,0.0498 0.3110074,0.14929 0.087082,0.0995 0.1368432,0.23014 0.1368432,0.39808 v 1.89093 c 0,0.0871 0.024881,0.1555 0.087082,0.21148 0.055981,0.0622 0.124403,0.0871 0.211485,0.0871 0.080862,0 0.1492836,-0.0249 0.2114851,-0.0871 0.055981,-0.056 0.087082,-0.1244 0.087082,-0.21148 v -1.89093 -0.0622 c 0.018661,-0.0311 0.043541,-0.0622 0.068422,-0.0995 0.024881,-0.0373 0.062201,-0.0746 0.1119626,-0.1244 0.049761,-0.0498 0.099522,-0.0933 0.1492836,-0.13063 0.049761,-0.0373 0.1181828,-0.0684 0.1928246,-0.0933 0.074642,-0.0249 0.1492835,-0.0373 0.2239253,-0.0373 0.1119626,0 0.2177052,0.0498 0.3110074,0.14929 0.087082,0.0995 0.1368432,0.23014 0.1368432,0.39808 v 1.89093 c 0,0.0871 0.024881,0.1555 0.087082,0.21148 0.055981,0.0622 0.124403,0.0871 0.211485,0.0871 0.080862,0 0.1492836,-0.0249 0.2114851,-0.0871 0.055981,-0.056 0.087082,-0.1244 0.087082,-0.21148 v -1.89093 c 0,-0.31722 -0.1057425,-0.58469 -0.3047872,-0.80861 -0.2052649,-0.22393 -0.4540708,-0.33589 -0.7401976,-0.33589 -0.2052649,0 -0.3918693,0.0435 -0.5535932,0.11818 -0.1617238,0.0746 -0.3047872,0.17416 -0.4291902,0.29857 -0.211485,-0.27369 -0.4789514,-0.41675 -0.8086192,-0.41675 -0.2861268,0 -0.5411529,0.0809 -0.7650782,0.23636 -0.024881,-0.0498 -0.062202,-0.0933 -0.1119627,-0.13062 -0.049761,-0.0373 -0.1057425,-0.056 -0.167944,-0.056 -0.087082,0 -0.1555037,0.0311 -0.211485,0.0871 -0.062202,0.0622 -0.087082,0.13062 -0.087082,0.21149 v 2.6871 c 0,0.0871 0.024881,0.1555 0.087082,0.21148 0.055981,0.0622 0.1244029,0.0871 0.211485,0.0871 z"
+ style="stroke-width:0.15923578"
+ id="path834" />
+ </g>
+ <g
+ id="g28"
+ transform="translate(-0.23995531,0.02790178)">
+ <g
+ id="g835">
+ <g
+ transform="matrix(1.0000001,0,0,0.98554676,-7.6075554e-7,4.2369817)"
+ id="g25">
+ <path
+ d="m 7.426334,293.15097 c 0.080862,0 0.1492836,-0.0249 0.2114851,-0.0871 0.055981,-0.056 0.087082,-0.1244 0.087082,-0.21149 v -2.6871 c 0,-0.0809 -0.031101,-0.14928 -0.087082,-0.21149 -0.062201,-0.056 -0.1306232,-0.0871 -0.2114851,-0.0871 -0.087082,0 -0.1555037,0.0311 -0.211485,0.0871 -0.062202,0.0622 -0.087082,0.13063 -0.087082,0.21149 v 2.6871 c 0,0.0871 0.024881,0.15551 0.087082,0.21149 0.055981,0.0622 0.124403,0.0871 0.211485,0.0871 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:1.25;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.15923578"
+ id="path896"
+ inkscape:connector-curvature="0" />
+ </g>
+ <path
+ d="m 7.4249389,289.61754 c 0.080862,0 0.1492836,-0.0249 0.2114851,-0.0871 0.055981,-0.056 0.086228,-0.1244 0.087082,-0.21149 l 0.0014,-0.14231 c 7.93e-4,-0.0809 -0.031101,-0.14929 -0.087082,-0.21149 -0.062201,-0.056 -0.1306232,-0.0871 -0.2114851,-0.0871 -0.087082,0 -0.1555037,0.0311 -0.211485,0.0871 -0.062202,0.0622 -0.086289,0.13062 -0.087082,0.21149 l -0.0014,0.14231 c -8.538e-4,0.0871 0.024881,0.15551 0.087082,0.21149 0.055981,0.0622 0.124403,0.0871 0.211485,0.0871 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:1.25;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.15923578"
+ id="path898"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="scsscscsscs" />
+ </g>
+ </g>
+ </g>
+</svg>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Data Structures</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('annotated.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="headertitle">
+<div class="title">Data Structures</div> </div>
+</div><!--header-->
+<div class="contents">
+<div class="textblock">Here are the data structures with brief descriptions:</div><div class="directory">
+<table class="directory">
+<tr id="row_0_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><span class="icona"><span class="icon">C</span></span><a class="el" href="group__analysis.html#structmi__heap__area__t" target="_self">mi_heap_area_t</a></td><td class="desc">An area of heap space contains blocks of a single size </td></tr>
+<tr id="row_1_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><span class="icona"><span class="icon">C</span></span><a class="el" href="group__cpp.html#structmi__stl__allocator" target="_self">mi_stl_allocator</a></td><td class="desc"><em>std::allocator</em> implementation for mimalloc for use in STL containers </td></tr>
+</table>
+</div><!-- directory -->
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+var annotated_dup =
+[
+ [ "mi_heap_area_t", "group__analysis.html#structmi__heap__area__t", "group__analysis_structmi__heap__area__t" ],
+ [ "mi_stl_allocator", "group__cpp.html#structmi__stl__allocator", null ]
+];
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Performance</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('bench.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="PageDoc"><div class="header">
+ <div class="headertitle">
+<div class="title">Performance </div> </div>
+</div><!--header-->
+<div class="contents">
+<div class="textblock"><p>We tested <em>mimalloc</em> against many other top allocators over a wide range of benchmarks, ranging from various real world programs to synthetic benchmarks that see how the allocator behaves under more extreme circumstances.</p>
+<p>In our benchmarks, <em>mimalloc</em> always outperforms all other leading allocators (<em>jemalloc</em>, <em>tcmalloc</em>, <em>Hoard</em>, etc) (Apr 2019), and usually uses less memory (up to 25% more in the worst case). A nice property is that it does <em>consistently</em> well over the wide range of benchmarks.</p>
+<p>See the <a href="https://github.com/microsoft/mimalloc#Performance">Performance</a> section in the <em>mimalloc</em> repository for benchmark results, or the the technical report for detailed benchmark results. </p>
+</div></div><!-- PageDoc -->
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Building</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('build.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="PageDoc"><div class="header">
+ <div class="headertitle">
+<div class="title">Building </div> </div>
+</div><!--header-->
+<div class="contents">
+<div class="textblock"><p>Checkout the sources from Github: </p><div class="fragment"><div class="line">git clone https:<span class="comment">//github.com/microsoft/mimalloc</span></div></div><!-- fragment --><h2>Windows</h2>
+<p>Open <code>ide/vs2019/mimalloc.sln</code> in Visual Studio 2019 and build (or <code>ide/vs2017/mimalloc.sln</code>). The <code>mimalloc</code> project builds a static library (in <code>out/msvc-x64</code>), while the <code>mimalloc-override</code> project builds a DLL for overriding malloc in the entire program.</p>
+<h2>macOS, Linux, BSD, etc.</h2>
+<p>We use <a href="https://cmake.org"><code>cmake</code></a><sup>1</sup> as the build system:</p>
+<div class="fragment"><div class="line">> mkdir -p out/release</div><div class="line">> cd out/release</div><div class="line">> cmake ../..</div><div class="line">> make</div></div><!-- fragment --><p> This builds the library as a shared (dynamic) library (<code>.so</code> or <code>.dylib</code>), a static library (<code>.a</code>), and as a single object file (<code>.o</code>).</p>
+<p><code>> sudo make install</code> (install the library and header files in <code>/usr/local/lib</code> and <code>/usr/local/include</code>)</p>
+<p>You can build the debug version which does many internal checks and maintains detailed statistics as:</p>
+<div class="fragment"><div class="line">> mkdir -p out/debug</div><div class="line">> cd out/debug</div><div class="line">> cmake -DCMAKE_BUILD_TYPE=Debug ../..</div><div class="line">> make</div></div><!-- fragment --><p> This will name the shared library as <code>libmimalloc-debug.so</code>.</p>
+<p>Finally, you can build a <em>secure</em> version that uses guard pages, encrypted free lists, etc, as: </p><div class="fragment"><div class="line">> mkdir -p out/secure</div><div class="line">> cd out/secure</div><div class="line">> cmake -DMI_SECURE=ON ../..</div><div class="line">> make</div></div><!-- fragment --><p> This will name the shared library as <code>libmimalloc-secure.so</code>. Use <code>ccmake</code><sup>2</sup> instead of <code>cmake</code> to see and customize all the available build options.</p>
+<p>Notes:</p><ol type="1">
+<li>Install CMake: <code>sudo apt-get install cmake</code></li>
+<li>Install CCMake: <code>sudo apt-get install cmake-curses-gui</code> </li>
+</ol>
+</div></div><!-- PageDoc -->
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Data Structure Index</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('classes.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="headertitle">
+<div class="title">Data Structure Index</div> </div>
+</div><!--header-->
+<div class="contents">
+<div class="qindex"><a class="qindex" href="#letter_m">m</a></div>
+<table class="classindex">
+<tr><td rowspan="2" valign="bottom"><a name="letter_m"></a><table border="0" cellspacing="0" cellpadding="0"><tr><td><div class="ah">  m  </div></td></tr></table>
+</td><td valign="top"><a class="el" href="group__cpp.html#structmi__stl__allocator">mi_stl_allocator</a>   </td><td></td></tr>
+<tr><td></td><td></td><td></td></tr>
+<tr><td valign="top"><a class="el" href="group__analysis.html#structmi__heap__area__t">mi_heap_area_t</a>   </td><td></td><td></td></tr>
+<tr><td></td><td></td><td></td></tr>
+</table>
+<div class="qindex"><a class="qindex" href="#letter_m">m</a></div>
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+/* The standard CSS for doxygen 1.8.15 */
+
+body, table, div, p, dl {
+ font: 400 14px/22px Roboto,sans-serif;
+}
+
+p.reference, p.definition {
+ font: 400 14px/22px Roboto,sans-serif;
+}
+
+/* @group Heading Levels */
+
+h1.groupheader {
+ font-size: 150%;
+}
+
+.title {
+ font: 400 14px/28px Roboto,sans-serif;
+ font-size: 150%;
+ font-weight: bold;
+ margin: 10px 2px;
+}
+
+h2.groupheader {
+ border-bottom: 1px solid #474D4E;
+ color: #0A0B0B;
+ font-size: 150%;
+ font-weight: normal;
+ margin-top: 1.75em;
+ padding-top: 8px;
+ padding-bottom: 4px;
+ width: 100%;
+}
+
+h3.groupheader {
+ font-size: 100%;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ -webkit-transition: text-shadow 0.5s linear;
+ -moz-transition: text-shadow 0.5s linear;
+ -ms-transition: text-shadow 0.5s linear;
+ -o-transition: text-shadow 0.5s linear;
+ transition: text-shadow 0.5s linear;
+ margin-right: 15px;
+}
+
+h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow {
+ text-shadow: 0 0 15px cyan;
+}
+
+dt {
+ font-weight: bold;
+}
+
+div.multicol {
+ -moz-column-gap: 1em;
+ -webkit-column-gap: 1em;
+ -moz-column-count: 3;
+ -webkit-column-count: 3;
+}
+
+p.startli, p.startdd {
+ margin-top: 2px;
+}
+
+p.starttd {
+ margin-top: 0px;
+}
+
+p.endli {
+ margin-bottom: 0px;
+}
+
+p.enddd {
+ margin-bottom: 4px;
+}
+
+p.endtd {
+ margin-bottom: 2px;
+}
+
+p.interli {
+}
+
+p.interdd {
+}
+
+p.intertd {
+}
+
+/* @end */
+
+caption {
+ font-weight: bold;
+}
+
+span.legend {
+ font-size: 70%;
+ text-align: center;
+}
+
+h3.version {
+ font-size: 90%;
+ text-align: center;
+}
+
+div.qindex, div.navtab{
+ background-color: #D6D9D9;
+ border: 1px solid #636C6D;
+ text-align: center;
+}
+
+div.qindex, div.navpath {
+ width: 100%;
+ line-height: 140%;
+}
+
+div.navtab {
+ margin-right: 15px;
+}
+
+/* @group Link Styling */
+
+a {
+ color: #0F1010;
+ font-weight: normal;
+ text-decoration: none;
+}
+
+.contents a:visited {
+ color: #171919;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+a.qindex {
+ font-weight: bold;
+}
+
+a.qindexHL {
+ font-weight: bold;
+ background-color: #5B6364;
+ color: #FFFFFF;
+ border: 1px double #464C4D;
+}
+
+.contents a.qindexHL:visited {
+ color: #FFFFFF;
+}
+
+a.el {
+ font-weight: bold;
+}
+
+a.elRef {
+}
+
+a.code, a.code:visited, a.line, a.line:visited {
+ color: #171919;
+}
+
+a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited {
+ color: #171919;
+}
+
+/* @end */
+
+dl.el {
+ margin-left: -1cm;
+}
+
+ul {
+ overflow: hidden; /*Fixed: list item bullets overlap floating elements*/
+}
+
+#side-nav ul {
+ overflow: visible; /* reset ul rule for scroll bar in GENERATE_TREEVIEW window */
+}
+
+#main-nav ul {
+ overflow: visible; /* reset ul rule for the navigation bar drop down lists */
+}
+
+.fragment {
+ text-align: left;
+ direction: ltr;
+ overflow-x: auto; /*Fixed: fragment lines overlap floating elements*/
+ overflow-y: hidden;
+}
+
+pre.fragment {
+ border: 1px solid #90989A;
+ background-color: #F7F8F8;
+ padding: 4px 6px;
+ margin: 4px 8px 4px 2px;
+ overflow: auto;
+ word-wrap: break-word;
+ font-size: 9pt;
+ line-height: 125%;
+ font-family: monospace, fixed;
+ font-size: 105%;
+}
+
+div.fragment {
+ padding: 0 0 1px 0; /*Fixed: last line underline overlap border*/
+ margin: 4px 8px 4px 2px;
+ background-color: #F7F8F8;
+ border: 1px solid #90989A;
+}
+
+div.line {
+ font-family: monospace, fixed;
+ font-size: 13px;
+ min-height: 13px;
+ line-height: 1.0;
+ text-wrap: unrestricted;
+ white-space: -moz-pre-wrap; /* Moz */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ white-space: pre-wrap; /* CSS3 */
+ word-wrap: break-word; /* IE 5.5+ */
+ text-indent: -53px;
+ padding-left: 53px;
+ padding-bottom: 0px;
+ margin: 0px;
+ -webkit-transition-property: background-color, box-shadow;
+ -webkit-transition-duration: 0.5s;
+ -moz-transition-property: background-color, box-shadow;
+ -moz-transition-duration: 0.5s;
+ -ms-transition-property: background-color, box-shadow;
+ -ms-transition-duration: 0.5s;
+ -o-transition-property: background-color, box-shadow;
+ -o-transition-duration: 0.5s;
+ transition-property: background-color, box-shadow;
+ transition-duration: 0.5s;
+}
+
+div.line:after {
+ content:"\000A";
+ white-space: pre;
+}
+
+div.line.glow {
+ background-color: cyan;
+ box-shadow: 0 0 10px cyan;
+}
+
+
+span.lineno {
+ padding-right: 4px;
+ text-align: right;
+ border-right: 2px solid #0F0;
+ background-color: #E8E8E8;
+ white-space: pre;
+}
+span.lineno a {
+ background-color: #D8D8D8;
+}
+
+span.lineno a:hover {
+ background-color: #C8C8C8;
+}
+
+.lineno {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+div.ah, span.ah {
+ background-color: black;
+ font-weight: bold;
+ color: #FFFFFF;
+ margin-bottom: 3px;
+ margin-top: 3px;
+ padding: 0.2em;
+ border: solid thin #333;
+ border-radius: 0.5em;
+ -webkit-border-radius: .5em;
+ -moz-border-radius: .5em;
+ box-shadow: 2px 2px 3px #999;
+ -webkit-box-shadow: 2px 2px 3px #999;
+ -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444));
+ background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000 110%);
+}
+
+div.classindex ul {
+ list-style: none;
+ padding-left: 0;
+}
+
+div.classindex span.ai {
+ display: inline-block;
+}
+
+div.groupHeader {
+ margin-left: 16px;
+ margin-top: 12px;
+ font-weight: bold;
+}
+
+div.groupText {
+ margin-left: 16px;
+ font-style: italic;
+}
+
+body {
+ background-color: white;
+ color: black;
+ margin: 0;
+}
+
+div.contents {
+ margin-top: 10px;
+ margin-left: 12px;
+ margin-right: 8px;
+}
+
+td.indexkey {
+ background-color: #D6D9D9;
+ font-weight: bold;
+ border: 1px solid #90989A;
+ margin: 2px 0px 2px 0;
+ padding: 2px 10px;
+ white-space: nowrap;
+ vertical-align: top;
+}
+
+td.indexvalue {
+ background-color: #D6D9D9;
+ border: 1px solid #90989A;
+ padding: 2px 10px;
+ margin: 2px 0px;
+}
+
+tr.memlist {
+ background-color: #DADDDE;
+}
+
+p.formulaDsp {
+ text-align: center;
+}
+
+img.formulaDsp {
+
+}
+
+img.formulaInl, img.inline {
+ vertical-align: middle;
+}
+
+div.center {
+ text-align: center;
+ margin-top: 0px;
+ margin-bottom: 0px;
+ padding: 0px;
+}
+
+div.center img {
+ border: 0px;
+}
+
+address.footer {
+ text-align: right;
+ padding-right: 12px;
+}
+
+img.footer {
+ border: 0px;
+ vertical-align: middle;
+}
+
+/* @group Code Colorization */
+
+span.keyword {
+ color: #008000
+}
+
+span.keywordtype {
+ color: #604020
+}
+
+span.keywordflow {
+ color: #e08000
+}
+
+span.comment {
+ color: #800000
+}
+
+span.preprocessor {
+ color: #806020
+}
+
+span.stringliteral {
+ color: #002080
+}
+
+span.charliteral {
+ color: #008080
+}
+
+span.vhdldigit {
+ color: #ff00ff
+}
+
+span.vhdlchar {
+ color: #000000
+}
+
+span.vhdlkeyword {
+ color: #700070
+}
+
+span.vhdllogic {
+ color: #ff0000
+}
+
+blockquote {
+ background-color: #EDEFEF;
+ border-left: 2px solid #5B6364;
+ margin: 0 24px 0 4px;
+ padding: 0 12px 0 16px;
+}
+
+blockquote.DocNodeRTL {
+ border-left: 0;
+ border-right: 2px solid #5B6364;
+ margin: 0 4px 0 24px;
+ padding: 0 16px 0 12px;
+}
+
+/* @end */
+
+/*
+.search {
+ color: #003399;
+ font-weight: bold;
+}
+
+form.search {
+ margin-bottom: 0px;
+ margin-top: 0px;
+}
+
+input.search {
+ font-size: 75%;
+ color: #000080;
+ font-weight: normal;
+ background-color: #e8eef2;
+}
+*/
+
+td.tiny {
+ font-size: 75%;
+}
+
+.dirtab {
+ padding: 4px;
+ border-collapse: collapse;
+ border: 1px solid #636C6D;
+}
+
+th.dirtab {
+ background: #D6D9D9;
+ font-weight: bold;
+}
+
+hr {
+ height: 0px;
+ border: none;
+ border-top: 1px solid #1A1D1D;
+}
+
+hr.footer {
+ height: 1px;
+}
+
+/* @group Member Descriptions */
+
+table.memberdecls {
+ border-spacing: 0px;
+ padding: 0px;
+}
+
+.memberdecls td, .fieldtable tr {
+ -webkit-transition-property: background-color, box-shadow;
+ -webkit-transition-duration: 0.5s;
+ -moz-transition-property: background-color, box-shadow;
+ -moz-transition-duration: 0.5s;
+ -ms-transition-property: background-color, box-shadow;
+ -ms-transition-duration: 0.5s;
+ -o-transition-property: background-color, box-shadow;
+ -o-transition-duration: 0.5s;
+ transition-property: background-color, box-shadow;
+ transition-duration: 0.5s;
+}
+
+.memberdecls td.glow, .fieldtable tr.glow {
+ background-color: cyan;
+ box-shadow: 0 0 15px cyan;
+}
+
+.mdescLeft, .mdescRight,
+.memItemLeft, .memItemRight,
+.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
+ background-color: #F2F3F3;
+ border: none;
+ margin: 4px;
+ padding: 1px 0 0 8px;
+}
+
+.mdescLeft, .mdescRight {
+ padding: 0px 8px 4px 8px;
+ color: #555;
+}
+
+.memSeparator {
+ border-bottom: 1px solid #BBC0C1;
+ line-height: 1px;
+ margin: 0px;
+ padding: 0px;
+}
+
+.memItemLeft, .memTemplItemLeft {
+ white-space: nowrap;
+}
+
+.memItemRight {
+ width: 100%;
+}
+
+.memTemplParams {
+ color: #171919;
+ white-space: nowrap;
+ font-size: 80%;
+}
+
+/* @end */
+
+/* @group Member Details */
+
+/* Styles for detailed member documentation */
+
+.memtitle {
+ padding: 8px;
+ border-top: 1px solid #697273;
+ border-left: 1px solid #697273;
+ border-right: 1px solid #697273;
+ border-top-right-radius: 4px;
+ border-top-left-radius: 4px;
+ margin-bottom: -1px;
+ background-image: url('nav_f.png');
+ background-repeat: repeat-x;
+ background-color: #C4C8C9;
+ line-height: 1.25;
+ font-weight: 300;
+ float:left;
+}
+
+.permalink
+{
+ font-size: 65%;
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.memtemplate {
+ font-size: 80%;
+ color: #171919;
+ font-weight: normal;
+ margin-left: 9px;
+}
+
+.memnav {
+ background-color: #D6D9D9;
+ border: 1px solid #636C6D;
+ text-align: center;
+ margin: 2px;
+ margin-right: 15px;
+ padding: 2px;
+}
+
+.mempage {
+ width: 100%;
+}
+
+.memitem {
+ padding: 0;
+ margin-bottom: 10px;
+ margin-right: 5px;
+ -webkit-transition: box-shadow 0.5s linear;
+ -moz-transition: box-shadow 0.5s linear;
+ -ms-transition: box-shadow 0.5s linear;
+ -o-transition: box-shadow 0.5s linear;
+ transition: box-shadow 0.5s linear;
+ display: table !important;
+ width: 100%;
+}
+
+.memitem.glow {
+ box-shadow: 0 0 15px cyan;
+}
+
+.memname {
+ font-weight: 400;
+ margin-left: 6px;
+}
+
+.memname td {
+ vertical-align: bottom;
+}
+
+.memproto, dl.reflist dt {
+ border-top: 1px solid #697273;
+ border-left: 1px solid #697273;
+ border-right: 1px solid #697273;
+ padding: 6px 0px 6px 0px;
+ color: #030303;
+ font-weight: bold;
+ text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);
+ background-color: #BDC2C3;
+ /* opera specific markup */
+ box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+ border-top-right-radius: 4px;
+ /* firefox specific markup */
+ -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+ -moz-border-radius-topright: 4px;
+ /* webkit specific markup */
+ -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+ -webkit-border-top-right-radius: 4px;
+
+}
+
+.overload {
+ font-family: "courier new",courier,monospace;
+ font-size: 65%;
+}
+
+.memdoc, dl.reflist dd {
+ border-bottom: 1px solid #697273;
+ border-left: 1px solid #697273;
+ border-right: 1px solid #697273;
+ padding: 6px 10px 2px 10px;
+ background-color: #F7F8F8;
+ border-top-width: 0;
+ background-image:url('nav_g.png');
+ background-repeat:repeat-x;
+ background-color: #FFFFFF;
+ /* opera specific markup */
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+ /* firefox specific markup */
+ -moz-border-radius-bottomleft: 4px;
+ -moz-border-radius-bottomright: 4px;
+ -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+ /* webkit specific markup */
+ -webkit-border-bottom-left-radius: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+ -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+}
+
+dl.reflist dt {
+ padding: 5px;
+}
+
+dl.reflist dd {
+ margin: 0px 0px 10px 0px;
+ padding: 5px;
+}
+
+.paramkey {
+ text-align: right;
+}
+
+.paramtype {
+ white-space: nowrap;
+}
+
+.paramname {
+ color: #602020;
+ white-space: nowrap;
+}
+.paramname em {
+ font-style: normal;
+}
+.paramname code {
+ line-height: 14px;
+}
+
+.params, .retval, .exception, .tparams {
+ margin-left: 0px;
+ padding-left: 0px;
+}
+
+.params .paramname, .retval .paramname, .tparams .paramname {
+ font-weight: bold;
+ vertical-align: top;
+}
+
+.params .paramtype, .tparams .paramtype {
+ font-style: italic;
+ vertical-align: top;
+}
+
+.params .paramdir, .tparams .paramdir {
+ font-family: "courier new",courier,monospace;
+ vertical-align: top;
+}
+
+table.mlabels {
+ border-spacing: 0px;
+}
+
+td.mlabels-left {
+ width: 100%;
+ padding: 0px;
+}
+
+td.mlabels-right {
+ vertical-align: bottom;
+ padding: 0px;
+ white-space: nowrap;
+}
+
+span.mlabels {
+ margin-left: 8px;
+}
+
+span.mlabel {
+ background-color: #353A3B;
+ border-top:1px solid #212425;
+ border-left:1px solid #212425;
+ border-right:1px solid #90989A;
+ border-bottom:1px solid #90989A;
+ text-shadow: none;
+ color: white;
+ margin-right: 4px;
+ padding: 2px 3px;
+ border-radius: 3px;
+ font-size: 7pt;
+ white-space: nowrap;
+ vertical-align: middle;
+}
+
+
+
+/* @end */
+
+/* these are for tree view inside a (index) page */
+
+div.directory {
+ margin: 10px 0px;
+ border-top: 1px solid #5B6364;
+ border-bottom: 1px solid #5B6364;
+ width: 100%;
+}
+
+.directory table {
+ border-collapse:collapse;
+}
+
+.directory td {
+ margin: 0px;
+ padding: 0px;
+ vertical-align: top;
+}
+
+.directory td.entry {
+ white-space: nowrap;
+ padding-right: 6px;
+ padding-top: 3px;
+}
+
+.directory td.entry a {
+ outline:none;
+}
+
+.directory td.entry a img {
+ border: none;
+}
+
+.directory td.desc {
+ width: 100%;
+ padding-left: 6px;
+ padding-right: 6px;
+ padding-top: 3px;
+ border-left: 1px solid rgba(0,0,0,0.05);
+}
+
+.directory tr.even {
+ padding-left: 6px;
+ background-color: #EDEFEF;
+}
+
+.directory img {
+ vertical-align: -30%;
+}
+
+.directory .levels {
+ white-space: nowrap;
+ width: 100%;
+ text-align: right;
+ font-size: 9pt;
+}
+
+.directory .levels span {
+ cursor: pointer;
+ padding-left: 2px;
+ padding-right: 2px;
+ color: #0F1010;
+}
+
+.arrow {
+ color: #5B6364;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ cursor: pointer;
+ font-size: 80%;
+ display: inline-block;
+ width: 16px;
+ height: 22px;
+}
+
+.icon {
+ font-family: Arial, Helvetica;
+ font-weight: bold;
+ font-size: 12px;
+ height: 14px;
+ width: 16px;
+ display: inline-block;
+ background-color: #353A3B;
+ color: white;
+ text-align: center;
+ border-radius: 4px;
+ margin-left: 2px;
+ margin-right: 2px;
+}
+
+.icona {
+ width: 24px;
+ height: 22px;
+ display: inline-block;
+}
+
+.iconfopen {
+ width: 24px;
+ height: 18px;
+ margin-bottom: 4px;
+ background-image:url('folderopen.png');
+ background-position: 0px -4px;
+ background-repeat: repeat-y;
+ vertical-align:top;
+ display: inline-block;
+}
+
+.iconfclosed {
+ width: 24px;
+ height: 18px;
+ margin-bottom: 4px;
+ background-image:url('folderclosed.png');
+ background-position: 0px -4px;
+ background-repeat: repeat-y;
+ vertical-align:top;
+ display: inline-block;
+}
+
+.icondoc {
+ width: 24px;
+ height: 18px;
+ margin-bottom: 4px;
+ background-image:url('doc.png');
+ background-position: 0px -4px;
+ background-repeat: repeat-y;
+ vertical-align:top;
+ display: inline-block;
+}
+
+table.directory {
+ font: 400 14px Roboto,sans-serif;
+}
+
+/* @end */
+
+div.dynheader {
+ margin-top: 8px;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+address {
+ font-style: normal;
+ color: #050505;
+}
+
+table.doxtable caption {
+ caption-side: top;
+}
+
+table.doxtable {
+ border-collapse:collapse;
+ margin-top: 4px;
+ margin-bottom: 4px;
+}
+
+table.doxtable td, table.doxtable th {
+ border: 1px solid #060606;
+ padding: 3px 7px 2px;
+}
+
+table.doxtable th {
+ background-color: #0B0C0C;
+ color: #FFFFFF;
+ font-size: 110%;
+ padding-bottom: 4px;
+ padding-top: 5px;
+}
+
+table.fieldtable {
+ /*width: 100%;*/
+ margin-bottom: 10px;
+ border: 1px solid #697273;
+ border-spacing: 0px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+ -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
+ -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15);
+ box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15);
+}
+
+.fieldtable td, .fieldtable th {
+ padding: 3px 7px 2px;
+}
+
+.fieldtable td.fieldtype, .fieldtable td.fieldname {
+ white-space: nowrap;
+ border-right: 1px solid #697273;
+ border-bottom: 1px solid #697273;
+ vertical-align: top;
+}
+
+.fieldtable td.fieldname {
+ padding-top: 3px;
+}
+
+.fieldtable td.fielddoc {
+ border-bottom: 1px solid #697273;
+ /*width: 100%;*/
+}
+
+.fieldtable td.fielddoc p:first-child {
+ margin-top: 0px;
+}
+
+.fieldtable td.fielddoc p:last-child {
+ margin-bottom: 2px;
+}
+
+.fieldtable tr:last-child td {
+ border-bottom: none;
+}
+
+.fieldtable th {
+ background-image:url('nav_f.png');
+ background-repeat:repeat-x;
+ background-color: #C4C8C9;
+ font-size: 90%;
+ color: #030303;
+ padding-bottom: 4px;
+ padding-top: 5px;
+ text-align:left;
+ font-weight: 400;
+ -moz-border-radius-topleft: 4px;
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-left-radius: 4px;
+ -webkit-border-top-right-radius: 4px;
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ border-bottom: 1px solid #697273;
+}
+
+
+.tabsearch {
+ top: 0px;
+ left: 10px;
+ height: 36px;
+ background-image: url('tab_b.png');
+ z-index: 101;
+ overflow: hidden;
+ font-size: 13px;
+}
+
+.navpath ul
+{
+ font-size: 11px;
+ background-image:url('tab_b.png');
+ background-repeat:repeat-x;
+ background-position: 0 -5px;
+ height:30px;
+ line-height:30px;
+ color:#494F50;
+ border:solid 1px #8C9596;
+ overflow:hidden;
+ margin:0px;
+ padding:0px;
+}
+
+.navpath li
+{
+ list-style-type:none;
+ float:left;
+ padding-left:10px;
+ padding-right:15px;
+ background-image:url('bc_s.png');
+ background-repeat:no-repeat;
+ background-position:right;
+ color:#0A0B0B;
+}
+
+.navpath li.navelem a
+{
+ height:32px;
+ display:block;
+ text-decoration: none;
+ outline: none;
+ color: #040404;
+ font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif;
+ text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);
+ text-decoration: none;
+}
+
+.navpath li.navelem a:hover
+{
+ color:#2E3233;
+}
+
+.navpath li.footer
+{
+ list-style-type:none;
+ float:right;
+ padding-left:10px;
+ padding-right:15px;
+ background-image:none;
+ background-repeat:no-repeat;
+ background-position:right;
+ color:#0A0B0B;
+ font-size: 8pt;
+}
+
+
+div.summary
+{
+ float: right;
+ font-size: 8pt;
+ padding-right: 5px;
+ width: 50%;
+ text-align: right;
+}
+
+div.summary a
+{
+ white-space: nowrap;
+}
+
+table.classindex
+{
+ margin: 10px;
+ white-space: nowrap;
+ margin-left: 3%;
+ margin-right: 3%;
+ width: 94%;
+ border: 0;
+ border-spacing: 0;
+ padding: 0;
+}
+
+div.ingroups
+{
+ font-size: 8pt;
+ width: 50%;
+ text-align: left;
+}
+
+div.ingroups a
+{
+ white-space: nowrap;
+}
+
+div.header
+{
+ background-image:url('nav_h.png');
+ background-repeat:repeat-x;
+ background-color: #F2F3F3;
+ margin: 0px;
+ border-bottom: 1px solid #90989A;
+}
+
+div.headertitle
+{
+ padding: 5px 5px 5px 10px;
+}
+
+.PageDocRTL-title div.headertitle {
+ text-align: right;
+ direction: rtl;
+}
+
+dl {
+ padding: 0 0 0 0;
+}
+
+/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug, dl.examples */
+dl.section {
+ margin-left: 0px;
+ padding-left: 0px;
+}
+
+dl.section.DocNodeRTL {
+ margin-right: 0px;
+ padding-right: 0px;
+}
+
+dl.note {
+ margin-left: -7px;
+ padding-left: 3px;
+ border-left: 4px solid;
+ border-color: #D0C000;
+}
+
+dl.note.DocNodeRTL {
+ margin-left: 0;
+ padding-left: 0;
+ border-left: 0;
+ margin-right: -7px;
+ padding-right: 3px;
+ border-right: 4px solid;
+ border-color: #D0C000;
+}
+
+dl.warning, dl.attention {
+ margin-left: -7px;
+ padding-left: 3px;
+ border-left: 4px solid;
+ border-color: #FF0000;
+}
+
+dl.warning.DocNodeRTL, dl.attention.DocNodeRTL {
+ margin-left: 0;
+ padding-left: 0;
+ border-left: 0;
+ margin-right: -7px;
+ padding-right: 3px;
+ border-right: 4px solid;
+ border-color: #FF0000;
+}
+
+dl.pre, dl.post, dl.invariant {
+ margin-left: -7px;
+ padding-left: 3px;
+ border-left: 4px solid;
+ border-color: #00D000;
+}
+
+dl.pre.DocNodeRTL, dl.post.DocNodeRTL, dl.invariant.DocNodeRTL {
+ margin-left: 0;
+ padding-left: 0;
+ border-left: 0;
+ margin-right: -7px;
+ padding-right: 3px;
+ border-right: 4px solid;
+ border-color: #00D000;
+}
+
+dl.deprecated {
+ margin-left: -7px;
+ padding-left: 3px;
+ border-left: 4px solid;
+ border-color: #505050;
+}
+
+dl.deprecated.DocNodeRTL {
+ margin-left: 0;
+ padding-left: 0;
+ border-left: 0;
+ margin-right: -7px;
+ padding-right: 3px;
+ border-right: 4px solid;
+ border-color: #505050;
+}
+
+dl.todo {
+ margin-left: -7px;
+ padding-left: 3px;
+ border-left: 4px solid;
+ border-color: #00C0E0;
+}
+
+dl.todo.DocNodeRTL {
+ margin-left: 0;
+ padding-left: 0;
+ border-left: 0;
+ margin-right: -7px;
+ padding-right: 3px;
+ border-right: 4px solid;
+ border-color: #00C0E0;
+}
+
+dl.test {
+ margin-left: -7px;
+ padding-left: 3px;
+ border-left: 4px solid;
+ border-color: #3030E0;
+}
+
+dl.test.DocNodeRTL {
+ margin-left: 0;
+ padding-left: 0;
+ border-left: 0;
+ margin-right: -7px;
+ padding-right: 3px;
+ border-right: 4px solid;
+ border-color: #3030E0;
+}
+
+dl.bug {
+ margin-left: -7px;
+ padding-left: 3px;
+ border-left: 4px solid;
+ border-color: #C08050;
+}
+
+dl.bug.DocNodeRTL {
+ margin-left: 0;
+ padding-left: 0;
+ border-left: 0;
+ margin-right: -7px;
+ padding-right: 3px;
+ border-right: 4px solid;
+ border-color: #C08050;
+}
+
+dl.section dd {
+ margin-bottom: 6px;
+}
+
+
+#projectlogo
+{
+ text-align: center;
+ vertical-align: bottom;
+ border-collapse: separate;
+}
+
+#projectlogo img
+{
+ border: 0px none;
+}
+
+#projectalign
+{
+ vertical-align: middle;
+}
+
+#projectname
+{
+ font: 300% Tahoma, Arial,sans-serif;
+ margin: 0px;
+ padding: 2px 0px;
+}
+
+#projectbrief
+{
+ font: 120% Tahoma, Arial,sans-serif;
+ margin: 0px;
+ padding: 0px;
+}
+
+#projectnumber
+{
+ font: 50% Tahoma, Arial,sans-serif;
+ margin: 0px;
+ padding: 0px;
+}
+
+#titlearea
+{
+ padding: 0px;
+ margin: 0px;
+ width: 100%;
+ border-bottom: 1px solid #212425;
+}
+
+.image
+{
+ text-align: center;
+}
+
+.dotgraph
+{
+ text-align: center;
+}
+
+.mscgraph
+{
+ text-align: center;
+}
+
+.plantumlgraph
+{
+ text-align: center;
+}
+
+.diagraph
+{
+ text-align: center;
+}
+
+.caption
+{
+ font-weight: bold;
+}
+
+div.zoom
+{
+ border: 1px solid #4F5657;
+}
+
+dl.citelist {
+ margin-bottom:50px;
+}
+
+dl.citelist dt {
+ color:#080909;
+ float:left;
+ font-weight:bold;
+ margin-right:10px;
+ padding:5px;
+}
+
+dl.citelist dd {
+ margin:2px 0;
+ padding:5px 0;
+}
+
+div.toc {
+ padding: 14px 25px;
+ background-color: #E8EAEA;
+ border: 1px solid #B1B7B8;
+ border-radius: 7px 7px 7px 7px;
+ float: right;
+ height: auto;
+ margin: 0 8px 10px 10px;
+ width: 200px;
+}
+
+.PageDocRTL-title div.toc {
+ float: left !important;
+ text-align: right;
+}
+
+div.toc li {
+ background: url("bdwn.png") no-repeat scroll 0 5px transparent;
+ font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif;
+ margin-top: 5px;
+ padding-left: 10px;
+ padding-top: 2px;
+}
+
+.PageDocRTL-title div.toc li {
+ background-position-x: right !important;
+ padding-left: 0 !important;
+ padding-right: 10px;
+}
+
+div.toc h3 {
+ font: bold 12px/1.2 Arial,FreeSans,sans-serif;
+ color: #171919;
+ border-bottom: 0 none;
+ margin: 0;
+}
+
+div.toc ul {
+ list-style: none outside none;
+ border: medium none;
+ padding: 0px;
+}
+
+div.toc li.level1 {
+ margin-left: 0px;
+}
+
+div.toc li.level2 {
+ margin-left: 15px;
+}
+
+div.toc li.level3 {
+ margin-left: 30px;
+}
+
+div.toc li.level4 {
+ margin-left: 45px;
+}
+
+.PageDocRTL-title div.toc li.level1 {
+ margin-left: 0 !important;
+ margin-right: 0;
+}
+
+.PageDocRTL-title div.toc li.level2 {
+ margin-left: 0 !important;
+ margin-right: 15px;
+}
+
+.PageDocRTL-title div.toc li.level3 {
+ margin-left: 0 !important;
+ margin-right: 30px;
+}
+
+.PageDocRTL-title div.toc li.level4 {
+ margin-left: 0 !important;
+ margin-right: 45px;
+}
+
+.inherit_header {
+ font-weight: bold;
+ color: gray;
+ cursor: pointer;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.inherit_header td {
+ padding: 6px 0px 2px 5px;
+}
+
+.inherit {
+ display: none;
+}
+
+tr.heading h2 {
+ margin-top: 12px;
+ margin-bottom: 4px;
+}
+
+/* tooltip related style info */
+
+.ttc {
+ position: absolute;
+ display: none;
+}
+
+#powerTip {
+ cursor: default;
+ white-space: nowrap;
+ background-color: white;
+ border: 1px solid gray;
+ border-radius: 4px 4px 4px 4px;
+ box-shadow: 1px 1px 7px gray;
+ display: none;
+ font-size: smaller;
+ max-width: 80%;
+ opacity: 0.9;
+ padding: 1ex 1em 1em;
+ position: absolute;
+ z-index: 2147483647;
+}
+
+#powerTip div.ttdoc {
+ color: grey;
+ font-style: italic;
+}
+
+#powerTip div.ttname a {
+ font-weight: bold;
+}
+
+#powerTip div.ttname {
+ font-weight: bold;
+}
+
+#powerTip div.ttdeci {
+ color: #006318;
+}
+
+#powerTip div {
+ margin: 0px;
+ padding: 0px;
+ font: 12px/16px Roboto,sans-serif;
+}
+
+#powerTip:before, #powerTip:after {
+ content: "";
+ position: absolute;
+ margin: 0px;
+}
+
+#powerTip.n:after, #powerTip.n:before,
+#powerTip.s:after, #powerTip.s:before,
+#powerTip.w:after, #powerTip.w:before,
+#powerTip.e:after, #powerTip.e:before,
+#powerTip.ne:after, #powerTip.ne:before,
+#powerTip.se:after, #powerTip.se:before,
+#powerTip.nw:after, #powerTip.nw:before,
+#powerTip.sw:after, #powerTip.sw:before {
+ border: solid transparent;
+ content: " ";
+ height: 0;
+ width: 0;
+ position: absolute;
+}
+
+#powerTip.n:after, #powerTip.s:after,
+#powerTip.w:after, #powerTip.e:after,
+#powerTip.nw:after, #powerTip.ne:after,
+#powerTip.sw:after, #powerTip.se:after {
+ border-color: rgba(255, 255, 255, 0);
+}
+
+#powerTip.n:before, #powerTip.s:before,
+#powerTip.w:before, #powerTip.e:before,
+#powerTip.nw:before, #powerTip.ne:before,
+#powerTip.sw:before, #powerTip.se:before {
+ border-color: rgba(128, 128, 128, 0);
+}
+
+#powerTip.n:after, #powerTip.n:before,
+#powerTip.ne:after, #powerTip.ne:before,
+#powerTip.nw:after, #powerTip.nw:before {
+ top: 100%;
+}
+
+#powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after {
+ border-top-color: #FFFFFF;
+ border-width: 10px;
+ margin: 0px -10px;
+}
+#powerTip.n:before {
+ border-top-color: #808080;
+ border-width: 11px;
+ margin: 0px -11px;
+}
+#powerTip.n:after, #powerTip.n:before {
+ left: 50%;
+}
+
+#powerTip.nw:after, #powerTip.nw:before {
+ right: 14px;
+}
+
+#powerTip.ne:after, #powerTip.ne:before {
+ left: 14px;
+}
+
+#powerTip.s:after, #powerTip.s:before,
+#powerTip.se:after, #powerTip.se:before,
+#powerTip.sw:after, #powerTip.sw:before {
+ bottom: 100%;
+}
+
+#powerTip.s:after, #powerTip.se:after, #powerTip.sw:after {
+ border-bottom-color: #FFFFFF;
+ border-width: 10px;
+ margin: 0px -10px;
+}
+
+#powerTip.s:before, #powerTip.se:before, #powerTip.sw:before {
+ border-bottom-color: #808080;
+ border-width: 11px;
+ margin: 0px -11px;
+}
+
+#powerTip.s:after, #powerTip.s:before {
+ left: 50%;
+}
+
+#powerTip.sw:after, #powerTip.sw:before {
+ right: 14px;
+}
+
+#powerTip.se:after, #powerTip.se:before {
+ left: 14px;
+}
+
+#powerTip.e:after, #powerTip.e:before {
+ left: 100%;
+}
+#powerTip.e:after {
+ border-left-color: #FFFFFF;
+ border-width: 10px;
+ top: 50%;
+ margin-top: -10px;
+}
+#powerTip.e:before {
+ border-left-color: #808080;
+ border-width: 11px;
+ top: 50%;
+ margin-top: -11px;
+}
+
+#powerTip.w:after, #powerTip.w:before {
+ right: 100%;
+}
+#powerTip.w:after {
+ border-right-color: #FFFFFF;
+ border-width: 10px;
+ top: 50%;
+ margin-top: -10px;
+}
+#powerTip.w:before {
+ border-right-color: #808080;
+ border-width: 11px;
+ top: 50%;
+ margin-top: -11px;
+}
+
+@media print
+{
+ #top { display: none; }
+ #side-nav { display: none; }
+ #nav-path { display: none; }
+ body { overflow:visible; }
+ h1, h2, h3, h4, h5, h6 { page-break-after: avoid; }
+ .summary { display: none; }
+ .memitem { page-break-inside: avoid; }
+ #doc-content
+ {
+ margin-left:0 !important;
+ height:auto !important;
+ width:auto !important;
+ overflow:inherit;
+ display:inline;
+ }
+}
+
+/* @group Markdown */
+
+/*
+table.markdownTable {
+ border-collapse:collapse;
+ margin-top: 4px;
+ margin-bottom: 4px;
+}
+
+table.markdownTable td, table.markdownTable th {
+ border: 1px solid #060606;
+ padding: 3px 7px 2px;
+}
+
+table.markdownTableHead tr {
+}
+
+table.markdownTableBodyLeft td, table.markdownTable th {
+ border: 1px solid #060606;
+ padding: 3px 7px 2px;
+}
+
+th.markdownTableHeadLeft th.markdownTableHeadRight th.markdownTableHeadCenter th.markdownTableHeadNone {
+ background-color: #0B0C0C;
+ color: #FFFFFF;
+ font-size: 110%;
+ padding-bottom: 4px;
+ padding-top: 5px;
+}
+
+th.markdownTableHeadLeft {
+ text-align: left
+}
+
+th.markdownTableHeadRight {
+ text-align: right
+}
+
+th.markdownTableHeadCenter {
+ text-align: center
+}
+*/
+
+table.markdownTable {
+ border-collapse:collapse;
+ margin-top: 4px;
+ margin-bottom: 4px;
+}
+
+table.markdownTable td, table.markdownTable th {
+ border: 1px solid #060606;
+ padding: 3px 7px 2px;
+}
+
+table.markdownTable tr {
+}
+
+th.markdownTableHeadLeft, th.markdownTableHeadRight, th.markdownTableHeadCenter, th.markdownTableHeadNone {
+ background-color: #0B0C0C;
+ color: #FFFFFF;
+ font-size: 110%;
+ padding-bottom: 4px;
+ padding-top: 5px;
+}
+
+th.markdownTableHeadLeft, td.markdownTableBodyLeft {
+ text-align: left
+}
+
+th.markdownTableHeadRight, td.markdownTableBodyRight {
+ text-align: right
+}
+
+th.markdownTableHeadCenter, td.markdownTableBodyCenter {
+ text-align: center
+}
+
+.DocNodeRTL {
+ text-align: right;
+ direction: rtl;
+}
+
+.DocNodeLTR {
+ text-align: left;
+ direction: ltr;
+}
+
+table.DocNodeRTL {
+ width: auto;
+ margin-right: 0;
+ margin-left: auto;
+}
+
+table.DocNodeLTR {
+ width: auto;
+ margin-right: auto;
+ margin-left: 0;
+}
+
+tt, code, kbd, samp
+{
+ display: inline-block;
+ direction:ltr;
+}
+/* @end */
+
+u {
+ text-decoration: underline;
+}
+
--- /dev/null
+/*
+ @licstart The following is the entire license notice for the
+ JavaScript code in this file.
+
+ Copyright (C) 1997-2017 by Dimitri van Heesch
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ @licend The above is the entire license notice
+ for the JavaScript code in this file
+ */
+function toggleVisibility(linkObj)
+{
+ var base = $(linkObj).attr('id');
+ var summary = $('#'+base+'-summary');
+ var content = $('#'+base+'-content');
+ var trigger = $('#'+base+'-trigger');
+ var src=$(trigger).attr('src');
+ if (content.is(':visible')===true) {
+ content.hide();
+ summary.show();
+ $(linkObj).addClass('closed').removeClass('opened');
+ $(trigger).attr('src',src.substring(0,src.length-8)+'closed.png');
+ } else {
+ content.show();
+ summary.hide();
+ $(linkObj).removeClass('closed').addClass('opened');
+ $(trigger).attr('src',src.substring(0,src.length-10)+'open.png');
+ }
+ return false;
+}
+
+function updateStripes()
+{
+ $('table.directory tr').
+ removeClass('even').filter(':visible:even').addClass('even');
+}
+
+function toggleLevel(level)
+{
+ $('table.directory tr').each(function() {
+ var l = this.id.split('_').length-1;
+ var i = $('#img'+this.id.substring(3));
+ var a = $('#arr'+this.id.substring(3));
+ if (l<level+1) {
+ i.removeClass('iconfopen iconfclosed').addClass('iconfopen');
+ a.html('▼');
+ $(this).show();
+ } else if (l==level+1) {
+ i.removeClass('iconfclosed iconfopen').addClass('iconfclosed');
+ a.html('►');
+ $(this).show();
+ } else {
+ $(this).hide();
+ }
+ });
+ updateStripes();
+}
+
+function toggleFolder(id)
+{
+ // the clicked row
+ var currentRow = $('#row_'+id);
+
+ // all rows after the clicked row
+ var rows = currentRow.nextAll("tr");
+
+ var re = new RegExp('^row_'+id+'\\d+_$', "i"); //only one sub
+
+ // only match elements AFTER this one (can't hide elements before)
+ var childRows = rows.filter(function() { return this.id.match(re); });
+
+ // first row is visible we are HIDING
+ if (childRows.filter(':first').is(':visible')===true) {
+ // replace down arrow by right arrow for current row
+ var currentRowSpans = currentRow.find("span");
+ currentRowSpans.filter(".iconfopen").removeClass("iconfopen").addClass("iconfclosed");
+ currentRowSpans.filter(".arrow").html('►');
+ rows.filter("[id^=row_"+id+"]").hide(); // hide all children
+ } else { // we are SHOWING
+ // replace right arrow by down arrow for current row
+ var currentRowSpans = currentRow.find("span");
+ currentRowSpans.filter(".iconfclosed").removeClass("iconfclosed").addClass("iconfopen");
+ currentRowSpans.filter(".arrow").html('▼');
+ // replace down arrows by right arrows for child rows
+ var childRowsSpans = childRows.find("span");
+ childRowsSpans.filter(".iconfopen").removeClass("iconfopen").addClass("iconfclosed");
+ childRowsSpans.filter(".arrow").html('►');
+ childRows.show(); //show all children
+ }
+ updateStripes();
+}
+
+
+function toggleInherit(id)
+{
+ var rows = $('tr.inherit.'+id);
+ var img = $('tr.inherit_header.'+id+' img');
+ var src = $(img).attr('src');
+ if (rows.filter(':first').is(':visible')===true) {
+ rows.css('display','none');
+ $(img).attr('src',src.substring(0,src.length-8)+'closed.png');
+ } else {
+ rows.css('display','table-row'); // using show() causes jump in firefox
+ $(img).attr('src',src.substring(0,src.length-10)+'open.png');
+ }
+}
+/* @license-end */
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Environment Options</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('environment.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="PageDoc"><div class="header">
+ <div class="headertitle">
+<div class="title">Environment Options </div> </div>
+</div><!--header-->
+<div class="contents">
+<div class="textblock"><p>You can set further options either programmatically (using <a href="https://microsoft.github.io/mimalloc/group__options.html"><code>mi_option_set</code></a>), or via environment variables.</p>
+<ul>
+<li><code>MIMALLOC_SHOW_STATS=1</code>: show statistics when the program terminates.</li>
+<li><code>MIMALLOC_VERBOSE=1</code>: show verbose messages.</li>
+<li><code>MIMALLOC_SHOW_ERRORS=1</code>: show error and warning messages.</li>
+<li><code>MIMALLOC_PAGE_RESET=0</code>: by default, mimalloc will reset (or purge) OS pages when not in use to signal to the OS that the underlying physical memory can be reused. This can reduce memory fragmentation in long running (server) programs. By setting it to <code>0</code> no such page resets will be done which can improve performance for programs that are not long running. As an alternative, the <code>MIMALLOC_RESET_DELAY=</code><msecs> can be set higher (100ms by default) to make the page reset occur less frequently instead of turning it off completely.</li>
+<li><code>MIMALLOC_LARGE_OS_PAGES=1</code>: use large OS pages (2MiB) when available; for some workloads this can significantly improve performance. Use <code>MIMALLOC_VERBOSE</code> to check if the large OS pages are enabled – usually one needs to explicitly allow large OS pages (as on <a href="https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows?view=sql-server-2017">Windows</a> and <a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/tuning_and_optimizing_red_hat_enterprise_linux_for_oracle_9i_and_10g_databases/sect-oracle_9i_and_10g_tuning_guide-large_memory_optimization_big_pages_and_huge_pages-configuring_huge_pages_in_red_hat_enterprise_linux_4_or_5">Linux</a>). However, sometimes the OS is very slow to reserve contiguous physical memory for large OS pages so use with care on systems that can have fragmented memory (for that reason, we generally recommend to use <code>MIMALLOC_RESERVE_HUGE_OS_PAGES</code> instead when possible).</li>
+<li><code>MIMALLOC_RESERVE_HUGE_OS_PAGES=N</code>: where N is the number of 1GiB <em>huge</em> OS pages. This reserves the huge pages at startup and sometimes this can give a large (latency) performance improvement on big workloads. Usually it is better to not use <code>MIMALLOC_LARGE_OS_PAGES</code> in combination with this setting. Just like large OS pages, use with care as reserving contiguous physical memory can take a long time when memory is fragmented (but reserving the huge pages is done at startup only once). Note that we usually need to explicitly enable huge OS pages (as on <a href="https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows?view=sql-server-2017">Windows</a> and <a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/tuning_and_optimizing_red_hat_enterprise_linux_for_oracle_9i_and_10g_databases/sect-oracle_9i_and_10g_tuning_guide-large_memory_optimization_big_pages_and_huge_pages-configuring_huge_pages_in_red_hat_enterprise_linux_4_or_5">Linux</a>)). With huge OS pages, it may be beneficial to set the setting <code>MIMALLOC_EAGER_COMMIT_DELAY=N</code> (<code>N</code> is 1 by default) to delay the initial <code>N</code> segments (of 4MiB) of a thread to not allocate in the huge OS pages; this prevents threads that are short lived and allocate just a little to take up space in the huge OS page area (which cannot be reset).</li>
+</ul>
+<p>Use caution when using <code>fork</code> in combination with either large or huge OS pages: on a fork, the OS uses copy-on-write for all pages in the original process including the huge OS pages. When any memory is now written in that area, the OS will copy the entire 1GiB huge page (or 2MiB large page) which can cause the memory usage to grow in big increments. </p>
+</div></div><!-- PageDoc -->
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Data Fields</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('functions.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="contents">
+<div class="textblock">Here is a list of all struct and union fields with links to the structures/unions they belong to:</div><ul>
+<li>block_size
+: <a class="el" href="group__analysis.html#a332a6c14d736a99699d5453a1cb04b41">mi_heap_area_t</a>
+</li>
+<li>blocks
+: <a class="el" href="group__analysis.html#ae0085e6e1cf059a4eb7767e30e9991b8">mi_heap_area_t</a>
+</li>
+<li>committed
+: <a class="el" href="group__analysis.html#ab47526df656d8837ec3e97f11b83f835">mi_heap_area_t</a>
+</li>
+<li>reserved
+: <a class="el" href="group__analysis.html#ae848a3e6840414891035423948ca0383">mi_heap_area_t</a>
+</li>
+<li>used
+: <a class="el" href="group__analysis.html#ab820302c5cd0df133eb8e51650a008b4">mi_heap_area_t</a>
+</li>
+</ul>
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Data Fields - Variables</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('functions_vars.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="contents">
+ <ul>
+<li>block_size
+: <a class="el" href="group__analysis.html#a332a6c14d736a99699d5453a1cb04b41">mi_heap_area_t</a>
+</li>
+<li>blocks
+: <a class="el" href="group__analysis.html#ae0085e6e1cf059a4eb7767e30e9991b8">mi_heap_area_t</a>
+</li>
+<li>committed
+: <a class="el" href="group__analysis.html#ab47526df656d8837ec3e97f11b83f835">mi_heap_area_t</a>
+</li>
+<li>reserved
+: <a class="el" href="group__analysis.html#ae848a3e6840414891035423948ca0383">mi_heap_area_t</a>
+</li>
+<li>used
+: <a class="el" href="group__analysis.html#ab820302c5cd0df133eb8e51650a008b4">mi_heap_area_t</a>
+</li>
+</ul>
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Aligned Allocation</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('group__aligned.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="summary">
+<a href="#func-members">Functions</a> </div>
+ <div class="headertitle">
+<div class="title">Aligned Allocation</div> </div>
+</div><!--header-->
+<div class="contents">
+
+<p>Allocating aligned memory blocks.
+<a href="#details">More...</a></p>
+<table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="func-members"></a>
+Functions</h2></td></tr>
+<tr class="memitem:ga68930196751fa2cca9e1fd0d71bade56"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__aligned.html#ga68930196751fa2cca9e1fd0d71bade56">mi_malloc_aligned</a> (size_t size, size_t alignment)</td></tr>
+<tr class="memdesc:ga68930196751fa2cca9e1fd0d71bade56"><td class="mdescLeft"> </td><td class="mdescRight">Allocate <em>size</em> bytes aligned by <em>alignment</em>. <a href="#ga68930196751fa2cca9e1fd0d71bade56">More...</a><br /></td></tr>
+<tr class="separator:ga68930196751fa2cca9e1fd0d71bade56"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga0cadbcf5b89a7b6fb171bc8df8734819"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__aligned.html#ga0cadbcf5b89a7b6fb171bc8df8734819">mi_zalloc_aligned</a> (size_t size, size_t alignment)</td></tr>
+<tr class="separator:ga0cadbcf5b89a7b6fb171bc8df8734819"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga53dddb4724042a90315b94bc268fb4c9"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__aligned.html#ga53dddb4724042a90315b94bc268fb4c9">mi_calloc_aligned</a> (size_t count, size_t size, size_t alignment)</td></tr>
+<tr class="separator:ga53dddb4724042a90315b94bc268fb4c9"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga4028d1cf4aa4c87c880747044a8322ae"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__aligned.html#ga4028d1cf4aa4c87c880747044a8322ae">mi_realloc_aligned</a> (void *p, size_t newsize, size_t alignment)</td></tr>
+<tr class="separator:ga4028d1cf4aa4c87c880747044a8322ae"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga5850da130c936bd77db039dcfbc8295d"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__aligned.html#ga5850da130c936bd77db039dcfbc8295d">mi_malloc_aligned_at</a> (size_t size, size_t alignment, size_t offset)</td></tr>
+<tr class="memdesc:ga5850da130c936bd77db039dcfbc8295d"><td class="mdescLeft"> </td><td class="mdescRight">Allocate <em>size</em> bytes aligned by <em>alignment</em> at a specified <em>offset</em>. <a href="#ga5850da130c936bd77db039dcfbc8295d">More...</a><br /></td></tr>
+<tr class="separator:ga5850da130c936bd77db039dcfbc8295d"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga5f8c2353766db522565e642fafd8a3f8"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__aligned.html#ga5f8c2353766db522565e642fafd8a3f8">mi_zalloc_aligned_at</a> (size_t size, size_t alignment, size_t offset)</td></tr>
+<tr class="separator:ga5f8c2353766db522565e642fafd8a3f8"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga08647c4593f3b2eef24a919a73eba3a3"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__aligned.html#ga08647c4593f3b2eef24a919a73eba3a3">mi_calloc_aligned_at</a> (size_t count, size_t size, size_t alignment, size_t offset)</td></tr>
+<tr class="separator:ga08647c4593f3b2eef24a919a73eba3a3"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaf66a9ae6c6f08bd6be6fb6ea771faffb"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__aligned.html#gaf66a9ae6c6f08bd6be6fb6ea771faffb">mi_realloc_aligned_at</a> (void *p, size_t newsize, size_t alignment, size_t offset)</td></tr>
+<tr class="separator:gaf66a9ae6c6f08bd6be6fb6ea771faffb"><td class="memSeparator" colspan="2"> </td></tr>
+</table>
+<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
+<p>Allocating aligned memory blocks. </p>
+<h2 class="groupheader">Function Documentation</h2>
+<a id="ga53dddb4724042a90315b94bc268fb4c9"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga53dddb4724042a90315b94bc268fb4c9">◆ </a></span>mi_calloc_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_calloc_aligned </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>count</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga08647c4593f3b2eef24a919a73eba3a3"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga08647c4593f3b2eef24a919a73eba3a3">◆ </a></span>mi_calloc_aligned_at()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_calloc_aligned_at </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>count</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>offset</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga68930196751fa2cca9e1fd0d71bade56"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga68930196751fa2cca9e1fd0d71bade56">◆ </a></span>mi_malloc_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_malloc_aligned </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate <em>size</em> bytes aligned by <em>alignment</em>. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">size</td><td>number of bytes to allocate. </td></tr>
+ <tr><td class="paramname">alignment</td><td>the minimal alignment of the allocated memory. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>pointer to the allocated memory or <em>NULL</em> if out of memory. The returned pointer is aligned by <em>alignment</em>, i.e. <code>(uintptr_t)p % alignment == 0</code>.</dd></dl>
+<p>Returns a unique pointer if called with <em>size</em> 0. </p><dl class="section see"><dt>See also</dt><dd><a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-malloc?view=vs-2017">_aligned_malloc</a> (on Windows) </dd>
+<dd>
+<a href="http://man.openbsd.org/reallocarray">aligned_alloc</a> (on BSD, with switched arguments!) </dd>
+<dd>
+<a href="https://linux.die.net/man/3/posix_memalign">posix_memalign</a> (on Posix, with switched arguments!) </dd>
+<dd>
+<a href="https://linux.die.net/man/3/posix_memalign">memalign</a> (on Linux, with switched arguments!) </dd></dl>
+
+</div>
+</div>
+<a id="ga5850da130c936bd77db039dcfbc8295d"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga5850da130c936bd77db039dcfbc8295d">◆ </a></span>mi_malloc_aligned_at()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_malloc_aligned_at </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>offset</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate <em>size</em> bytes aligned by <em>alignment</em> at a specified <em>offset</em>. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">size</td><td>number of bytes to allocate. </td></tr>
+ <tr><td class="paramname">alignment</td><td>the minimal alignment of the allocated memory at <em>offset</em>. </td></tr>
+ <tr><td class="paramname">offset</td><td>the offset that should be aligned. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>pointer to the allocated memory or <em>NULL</em> if out of memory. The returned pointer is aligned by <em>alignment</em> at <em>offset</em>, i.e. <code>((uintptr_t)p + offset) % alignment == 0</code>.</dd></dl>
+<p>Returns a unique pointer if called with <em>size</em> 0. </p><dl class="section see"><dt>See also</dt><dd><a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-offset-malloc?view=vs-2017">_aligned_offset_malloc</a> (on Windows) </dd></dl>
+
+</div>
+</div>
+<a id="ga4028d1cf4aa4c87c880747044a8322ae"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga4028d1cf4aa4c87c880747044a8322ae">◆ </a></span>mi_realloc_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_realloc_aligned </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gaf66a9ae6c6f08bd6be6fb6ea771faffb"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaf66a9ae6c6f08bd6be6fb6ea771faffb">◆ </a></span>mi_realloc_aligned_at()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_realloc_aligned_at </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>offset</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga0cadbcf5b89a7b6fb171bc8df8734819"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga0cadbcf5b89a7b6fb171bc8df8734819">◆ </a></span>mi_zalloc_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_zalloc_aligned </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga5f8c2353766db522565e642fafd8a3f8"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga5f8c2353766db522565e642fafd8a3f8">◆ </a></span>mi_zalloc_aligned_at()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_zalloc_aligned_at </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>offset</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+var group__aligned =
+[
+ [ "mi_calloc_aligned", "group__aligned.html#ga53dddb4724042a90315b94bc268fb4c9", null ],
+ [ "mi_calloc_aligned_at", "group__aligned.html#ga08647c4593f3b2eef24a919a73eba3a3", null ],
+ [ "mi_malloc_aligned", "group__aligned.html#ga68930196751fa2cca9e1fd0d71bade56", null ],
+ [ "mi_malloc_aligned_at", "group__aligned.html#ga5850da130c936bd77db039dcfbc8295d", null ],
+ [ "mi_realloc_aligned", "group__aligned.html#ga4028d1cf4aa4c87c880747044a8322ae", null ],
+ [ "mi_realloc_aligned_at", "group__aligned.html#gaf66a9ae6c6f08bd6be6fb6ea771faffb", null ],
+ [ "mi_zalloc_aligned", "group__aligned.html#ga0cadbcf5b89a7b6fb171bc8df8734819", null ],
+ [ "mi_zalloc_aligned_at", "group__aligned.html#ga5f8c2353766db522565e642fafd8a3f8", null ]
+];
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Heap Introspection</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('group__analysis.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="summary">
+<a href="#nested-classes">Data Structures</a> |
+<a href="#typedef-members">Typedefs</a> |
+<a href="#func-members">Functions</a> </div>
+ <div class="headertitle">
+<div class="title">Heap Introspection</div> </div>
+</div><!--header-->
+<div class="contents">
+
+<p>Inspect the heap at runtime.
+<a href="#details">More...</a></p>
+<table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="nested-classes"></a>
+Data Structures</h2></td></tr>
+<tr class="memitem:structmi__heap__area__t"><td class="memItemLeft" align="right" valign="top">struct  </td><td class="memItemRight" valign="bottom"><a class="el" href="group__analysis.html#structmi__heap__area__t">mi_heap_area_t</a></td></tr>
+<tr class="memdesc:structmi__heap__area__t"><td class="mdescLeft"> </td><td class="mdescRight">An area of heap space contains blocks of a single size. <a href="group__analysis.html#structmi__heap__area__t">More...</a><br /></td></tr>
+<tr class="separator:structmi__heap__area__t"><td class="memSeparator" colspan="2"> </td></tr>
+</table><table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="typedef-members"></a>
+Typedefs</h2></td></tr>
+<tr class="memitem:gadfa01e2900f0e5d515ad5506b26f6d65"><td class="memItemLeft" align="right" valign="top">typedef bool() </td><td class="memItemRight" valign="bottom"><a class="el" href="group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65">mi_block_visit_fun</a>(const <a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, const <a class="el" href="group__analysis.html#structmi__heap__area__t">mi_heap_area_t</a> *area, void *block, size_t block_size, void *arg)</td></tr>
+<tr class="memdesc:gadfa01e2900f0e5d515ad5506b26f6d65"><td class="mdescLeft"> </td><td class="mdescRight">Visitor function passed to <a class="el" href="group__analysis.html#ga70c46687dc6e9dc98b232b02646f8bed" title="Visit all areas and blocks in a heap.">mi_heap_visit_blocks()</a> <a href="#gadfa01e2900f0e5d515ad5506b26f6d65">More...</a><br /></td></tr>
+<tr class="separator:gadfa01e2900f0e5d515ad5506b26f6d65"><td class="memSeparator" colspan="2"> </td></tr>
+</table><table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="func-members"></a>
+Functions</h2></td></tr>
+<tr class="memitem:gaa862aa8ed8d57d84cae41fc1022d71af"><td class="memItemLeft" align="right" valign="top">bool </td><td class="memItemRight" valign="bottom"><a class="el" href="group__analysis.html#gaa862aa8ed8d57d84cae41fc1022d71af">mi_heap_contains_block</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, const void *p)</td></tr>
+<tr class="memdesc:gaa862aa8ed8d57d84cae41fc1022d71af"><td class="mdescLeft"> </td><td class="mdescRight">Does a heap contain a pointer to a previously allocated block? <a href="#gaa862aa8ed8d57d84cae41fc1022d71af">More...</a><br /></td></tr>
+<tr class="separator:gaa862aa8ed8d57d84cae41fc1022d71af"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga0d67c1789faaa15ff366c024fcaf6377"><td class="memItemLeft" align="right" valign="top">bool </td><td class="memItemRight" valign="bottom"><a class="el" href="group__analysis.html#ga0d67c1789faaa15ff366c024fcaf6377">mi_heap_check_owned</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, const void *p)</td></tr>
+<tr class="memdesc:ga0d67c1789faaa15ff366c024fcaf6377"><td class="mdescLeft"> </td><td class="mdescRight">Check safely if any pointer is part of a heap. <a href="#ga0d67c1789faaa15ff366c024fcaf6377">More...</a><br /></td></tr>
+<tr class="separator:ga0d67c1789faaa15ff366c024fcaf6377"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga628c237489c2679af84a4d0d143b3dd5"><td class="memItemLeft" align="right" valign="top">bool </td><td class="memItemRight" valign="bottom"><a class="el" href="group__analysis.html#ga628c237489c2679af84a4d0d143b3dd5">mi_check_owned</a> (const void *p)</td></tr>
+<tr class="memdesc:ga628c237489c2679af84a4d0d143b3dd5"><td class="mdescLeft"> </td><td class="mdescRight">Check safely if any pointer is part of the default heap of this thread. <a href="#ga628c237489c2679af84a4d0d143b3dd5">More...</a><br /></td></tr>
+<tr class="separator:ga628c237489c2679af84a4d0d143b3dd5"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga70c46687dc6e9dc98b232b02646f8bed"><td class="memItemLeft" align="right" valign="top">bool </td><td class="memItemRight" valign="bottom"><a class="el" href="group__analysis.html#ga70c46687dc6e9dc98b232b02646f8bed">mi_heap_visit_blocks</a> (const <a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, bool visit_all_blocks, <a class="el" href="group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65">mi_block_visit_fun</a> *visitor, void *arg)</td></tr>
+<tr class="memdesc:ga70c46687dc6e9dc98b232b02646f8bed"><td class="mdescLeft"> </td><td class="mdescRight">Visit all areas and blocks in a heap. <a href="#ga70c46687dc6e9dc98b232b02646f8bed">More...</a><br /></td></tr>
+<tr class="separator:ga70c46687dc6e9dc98b232b02646f8bed"><td class="memSeparator" colspan="2"> </td></tr>
+</table>
+<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
+<p>Inspect the heap at runtime. </p>
+<hr/><h2 class="groupheader">Data Structure Documentation</h2>
+<a name="structmi__heap__area__t" id="structmi__heap__area__t"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#structmi__heap__area__t">◆ </a></span>mi_heap_area_t</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">struct mi_heap_area_t</td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+<div class="textblock"><p>An area of heap space contains blocks of a single size. </p>
+<p>The bytes in freed blocks are <code>committed - used</code>. </p>
+</div><table class="fieldtable">
+<tr><th colspan="3">Data Fields</th></tr>
+<tr><td class="fieldtype">
+<a id="a332a6c14d736a99699d5453a1cb04b41"></a>size_t</td>
+<td class="fieldname">
+block_size</td>
+<td class="fielddoc">
+size in bytes of one block </td></tr>
+<tr><td class="fieldtype">
+<a id="ae0085e6e1cf059a4eb7767e30e9991b8"></a>void *</td>
+<td class="fieldname">
+blocks</td>
+<td class="fielddoc">
+start of the area containing heap blocks </td></tr>
+<tr><td class="fieldtype">
+<a id="ab47526df656d8837ec3e97f11b83f835"></a>size_t</td>
+<td class="fieldname">
+committed</td>
+<td class="fielddoc">
+current committed bytes of this area </td></tr>
+<tr><td class="fieldtype">
+<a id="ae848a3e6840414891035423948ca0383"></a>size_t</td>
+<td class="fieldname">
+reserved</td>
+<td class="fielddoc">
+bytes reserved for this area </td></tr>
+<tr><td class="fieldtype">
+<a id="ab820302c5cd0df133eb8e51650a008b4"></a>size_t</td>
+<td class="fieldname">
+used</td>
+<td class="fielddoc">
+bytes in use by allocated blocks </td></tr>
+</table>
+
+</div>
+</div>
+<h2 class="groupheader">Typedef Documentation</h2>
+<a id="gadfa01e2900f0e5d515ad5506b26f6d65"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gadfa01e2900f0e5d515ad5506b26f6d65">◆ </a></span>mi_block_visit_fun</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">typedef bool() mi_block_visit_fun(const <a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, const <a class="el" href="group__analysis.html#structmi__heap__area__t">mi_heap_area_t</a> *area, void *block, size_t block_size, void *arg)</td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Visitor function passed to <a class="el" href="group__analysis.html#ga70c46687dc6e9dc98b232b02646f8bed" title="Visit all areas and blocks in a heap.">mi_heap_visit_blocks()</a> </p>
+<dl class="section return"><dt>Returns</dt><dd><em>true</em> if ok, <em>false</em> to stop visiting (i.e. break)</dd></dl>
+<p>This function is always first called for every <em>area</em> with <em>block</em> as a <em>NULL</em> pointer. If <em>visit_all_blocks</em> was <em>true</em>, the function is then called for every allocated block in that area. </p>
+
+</div>
+</div>
+<h2 class="groupheader">Function Documentation</h2>
+<a id="ga628c237489c2679af84a4d0d143b3dd5"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga628c237489c2679af84a4d0d143b3dd5">◆ </a></span>mi_check_owned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">bool mi_check_owned </td>
+ <td>(</td>
+ <td class="paramtype">const void * </td>
+ <td class="paramname"><em>p</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Check safely if any pointer is part of the default heap of this thread. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">p</td><td>Any pointer – not required to be previously allocated by us. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd><em>true</em> if <em>p</em> points to a block in default heap of this thread.</dd></dl>
+<p>Note: expensive function, linear in the pages in the heap. </p><dl class="section see"><dt>See also</dt><dd><a class="el" href="group__analysis.html#gaa862aa8ed8d57d84cae41fc1022d71af" title="Does a heap contain a pointer to a previously allocated block?">mi_heap_contains_block()</a> </dd>
+<dd>
+<a class="el" href="group__heap.html#ga8db4cbb87314a989a9a187464d6b5e05" title="Get the default heap that is used for mi_malloc() et al.">mi_heap_get_default()</a> </dd></dl>
+
+</div>
+</div>
+<a id="ga0d67c1789faaa15ff366c024fcaf6377"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga0d67c1789faaa15ff366c024fcaf6377">◆ </a></span>mi_heap_check_owned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">bool mi_heap_check_owned </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">const void * </td>
+ <td class="paramname"><em>p</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Check safely if any pointer is part of a heap. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">heap</td><td>The heap. </td></tr>
+ <tr><td class="paramname">p</td><td>Any pointer – not required to be previously allocated by us. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd><em>true</em> if <em>p</em> points to a block in <em>heap</em>.</dd></dl>
+<p>Note: expensive function, linear in the pages in the heap. </p><dl class="section see"><dt>See also</dt><dd><a class="el" href="group__analysis.html#gaa862aa8ed8d57d84cae41fc1022d71af" title="Does a heap contain a pointer to a previously allocated block?">mi_heap_contains_block()</a> </dd>
+<dd>
+<a class="el" href="group__heap.html#ga8db4cbb87314a989a9a187464d6b5e05" title="Get the default heap that is used for mi_malloc() et al.">mi_heap_get_default()</a> </dd></dl>
+
+</div>
+</div>
+<a id="gaa862aa8ed8d57d84cae41fc1022d71af"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaa862aa8ed8d57d84cae41fc1022d71af">◆ </a></span>mi_heap_contains_block()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">bool mi_heap_contains_block </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">const void * </td>
+ <td class="paramname"><em>p</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Does a heap contain a pointer to a previously allocated block? </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">heap</td><td>The heap. </td></tr>
+ <tr><td class="paramname">p</td><td>Pointer to a previously allocated block (in any heap)– cannot be some random pointer! </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd><em>true</em> if the block pointed to by <em>p</em> is in the <em>heap</em>. </dd></dl>
+<dl class="section see"><dt>See also</dt><dd><a class="el" href="group__analysis.html#ga0d67c1789faaa15ff366c024fcaf6377" title="Check safely if any pointer is part of a heap.">mi_heap_check_owned()</a> </dd></dl>
+
+</div>
+</div>
+<a id="ga70c46687dc6e9dc98b232b02646f8bed"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga70c46687dc6e9dc98b232b02646f8bed">◆ </a></span>mi_heap_visit_blocks()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">bool mi_heap_visit_blocks </td>
+ <td>(</td>
+ <td class="paramtype">const <a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">bool </td>
+ <td class="paramname"><em>visit_all_blocks</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"><a class="el" href="group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65">mi_block_visit_fun</a> * </td>
+ <td class="paramname"><em>visitor</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>arg</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Visit all areas and blocks in a heap. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">heap</td><td>The heap to visit. </td></tr>
+ <tr><td class="paramname">visit_all_blocks</td><td>If <em>true</em> visits all allocated blocks, otherwise <em>visitor</em> is only called for every heap area. </td></tr>
+ <tr><td class="paramname">visitor</td><td>This function is called for every area in the heap (with <em>block</em> as <em>NULL</em>). If <em>visit_all_blocks</em> is <em>true</em>, <em>visitor</em> is also called for every allocated block in every area (with <code>block!=NULL</code>). return <em>false</em> from this function to stop visiting early. </td></tr>
+ <tr><td class="paramname">arg</td><td>Extra argument passed to <em>visitor</em>. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd><em>true</em> if all areas and blocks were visited. </dd></dl>
+
+</div>
+</div>
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+var group__analysis =
+[
+ [ "mi_heap_area_t", "group__analysis.html#structmi__heap__area__t", [
+ [ "block_size", "group__analysis.html#a332a6c14d736a99699d5453a1cb04b41", null ],
+ [ "blocks", "group__analysis.html#ae0085e6e1cf059a4eb7767e30e9991b8", null ],
+ [ "committed", "group__analysis.html#ab47526df656d8837ec3e97f11b83f835", null ],
+ [ "reserved", "group__analysis.html#ae848a3e6840414891035423948ca0383", null ],
+ [ "used", "group__analysis.html#ab820302c5cd0df133eb8e51650a008b4", null ]
+ ] ],
+ [ "mi_block_visit_fun", "group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65", null ],
+ [ "mi_check_owned", "group__analysis.html#ga628c237489c2679af84a4d0d143b3dd5", null ],
+ [ "mi_heap_check_owned", "group__analysis.html#ga0d67c1789faaa15ff366c024fcaf6377", null ],
+ [ "mi_heap_contains_block", "group__analysis.html#gaa862aa8ed8d57d84cae41fc1022d71af", null ],
+ [ "mi_heap_visit_blocks", "group__analysis.html#ga70c46687dc6e9dc98b232b02646f8bed", null ]
+];
\ No newline at end of file
--- /dev/null
+var group__analysis_structmi__heap__area__t =
+[
+ [ "block_size", "group__analysis.html#a332a6c14d736a99699d5453a1cb04b41", null ],
+ [ "blocks", "group__analysis.html#ae0085e6e1cf059a4eb7767e30e9991b8", null ],
+ [ "committed", "group__analysis.html#ab47526df656d8837ec3e97f11b83f835", null ],
+ [ "reserved", "group__analysis.html#ae848a3e6840414891035423948ca0383", null ],
+ [ "used", "group__analysis.html#ab820302c5cd0df133eb8e51650a008b4", null ]
+];
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: C++ wrappers</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('group__cpp.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="summary">
+<a href="#nested-classes">Data Structures</a> |
+<a href="#func-members">Functions</a> </div>
+ <div class="headertitle">
+<div class="title">C++ wrappers</div> </div>
+</div><!--header-->
+<div class="contents">
+
+<p><code>mi_</code> prefixed implementations of various allocation functions that use C++ semantics on out-of-memory, generally calling <code>std::get_new_handler</code> and raising a <code>std::bad_alloc</code> exception on failure.
+<a href="#details">More...</a></p>
+<table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="nested-classes"></a>
+Data Structures</h2></td></tr>
+<tr class="memitem:structmi__stl__allocator"><td class="memItemLeft" align="right" valign="top">struct  </td><td class="memItemRight" valign="bottom"><a class="el" href="group__cpp.html#structmi__stl__allocator">mi_stl_allocator< T ></a></td></tr>
+<tr class="memdesc:structmi__stl__allocator"><td class="mdescLeft"> </td><td class="mdescRight"><em>std::allocator</em> implementation for mimalloc for use in STL containers. <a href="group__cpp.html#structmi__stl__allocator">More...</a><br /></td></tr>
+<tr class="separator:structmi__stl__allocator"><td class="memSeparator" colspan="2"> </td></tr>
+</table><table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="func-members"></a>
+Functions</h2></td></tr>
+<tr class="memitem:gaad048a9fce3d02c5909cd05c6ec24545"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__cpp.html#gaad048a9fce3d02c5909cd05c6ec24545">mi_new</a> (std::size_t n) noexcept(false)</td></tr>
+<tr class="memdesc:gaad048a9fce3d02c5909cd05c6ec24545"><td class="mdescLeft"> </td><td class="mdescRight">like <a class="el" href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a" title="Allocate size bytes.">mi_malloc()</a>, but when out of memory, use <code>std::get_new_handler</code> and raise <code>std::bad_alloc</code> exception on failure. <a href="#gaad048a9fce3d02c5909cd05c6ec24545">More...</a><br /></td></tr>
+<tr class="separator:gaad048a9fce3d02c5909cd05c6ec24545"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gae7bc4f56cd57ed3359060ff4f38bda81"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__cpp.html#gae7bc4f56cd57ed3359060ff4f38bda81">mi_new_n</a> (size_t count, size_t size) noexcept(false)</td></tr>
+<tr class="memdesc:gae7bc4f56cd57ed3359060ff4f38bda81"><td class="mdescLeft"> </td><td class="mdescRight">like <a class="el" href="group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6" title="Allocate count elements of size bytes.">mi_mallocn()</a>, but when out of memory, use <code>std::get_new_handler</code> and raise <code>std::bad_alloc</code> exception on failure. <a href="#gae7bc4f56cd57ed3359060ff4f38bda81">More...</a><br /></td></tr>
+<tr class="separator:gae7bc4f56cd57ed3359060ff4f38bda81"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaef2c2bdb4f70857902d3c8903ac095f3"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__cpp.html#gaef2c2bdb4f70857902d3c8903ac095f3">mi_new_aligned</a> (std::size_t n, std::align_val_t alignment) noexcept(false)</td></tr>
+<tr class="memdesc:gaef2c2bdb4f70857902d3c8903ac095f3"><td class="mdescLeft"> </td><td class="mdescRight">like <a class="el" href="group__aligned.html#ga68930196751fa2cca9e1fd0d71bade56" title="Allocate size bytes aligned by alignment.">mi_malloc_aligned()</a>, but when out of memory, use <code>std::get_new_handler</code> and raise <code>std::bad_alloc</code> exception on failure. <a href="#gaef2c2bdb4f70857902d3c8903ac095f3">More...</a><br /></td></tr>
+<tr class="separator:gaef2c2bdb4f70857902d3c8903ac095f3"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaeaded64eda71ed6b1d569d3e723abc4a"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__cpp.html#gaeaded64eda71ed6b1d569d3e723abc4a">mi_new_nothrow</a> (size_t n)</td></tr>
+<tr class="memdesc:gaeaded64eda71ed6b1d569d3e723abc4a"><td class="mdescLeft"> </td><td class="mdescRight">like <code>mi_malloc</code>, but when out of memory, use <code>std::get_new_handler</code> but return <em>NULL</em> on failure. <a href="#gaeaded64eda71ed6b1d569d3e723abc4a">More...</a><br /></td></tr>
+<tr class="separator:gaeaded64eda71ed6b1d569d3e723abc4a"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gab5e29558926d934c3f1cae8c815f942c"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__cpp.html#gab5e29558926d934c3f1cae8c815f942c">mi_new_aligned_nothrow</a> (size_t n, size_t alignment)</td></tr>
+<tr class="memdesc:gab5e29558926d934c3f1cae8c815f942c"><td class="mdescLeft"> </td><td class="mdescRight">like <code>mi_malloc_aligned</code>, but when out of memory, use <code>std::get_new_handler</code> but return <em>NULL</em> on failure. <a href="#gab5e29558926d934c3f1cae8c815f942c">More...</a><br /></td></tr>
+<tr class="separator:gab5e29558926d934c3f1cae8c815f942c"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaab78a32f55149e9fbf432d5288e38e1e"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__cpp.html#gaab78a32f55149e9fbf432d5288e38e1e">mi_new_realloc</a> (void *p, size_t newsize)</td></tr>
+<tr class="memdesc:gaab78a32f55149e9fbf432d5288e38e1e"><td class="mdescLeft"> </td><td class="mdescRight">like <a class="el" href="group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6" title="Re-allocate memory to newsize bytes.">mi_realloc()</a>, but when out of memory, use <code>std::get_new_handler</code> and raise <code>std::bad_alloc</code> exception on failure. <a href="#gaab78a32f55149e9fbf432d5288e38e1e">More...</a><br /></td></tr>
+<tr class="separator:gaab78a32f55149e9fbf432d5288e38e1e"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga756f4b2bc6a7ecd0a90baea8e90c7907"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__cpp.html#ga756f4b2bc6a7ecd0a90baea8e90c7907">mi_new_reallocn</a> (void *p, size_t newcount, size_t size)</td></tr>
+<tr class="memdesc:ga756f4b2bc6a7ecd0a90baea8e90c7907"><td class="mdescLeft"> </td><td class="mdescRight">like <a class="el" href="group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853" title="Re-allocate memory to count elements of size bytes.">mi_reallocn()</a>, but when out of memory, use <code>std::get_new_handler</code> and raise <code>std::bad_alloc</code> exception on failure. <a href="#ga756f4b2bc6a7ecd0a90baea8e90c7907">More...</a><br /></td></tr>
+<tr class="separator:ga756f4b2bc6a7ecd0a90baea8e90c7907"><td class="memSeparator" colspan="2"> </td></tr>
+</table>
+<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
+<p><code>mi_</code> prefixed implementations of various allocation functions that use C++ semantics on out-of-memory, generally calling <code>std::get_new_handler</code> and raising a <code>std::bad_alloc</code> exception on failure. </p>
+<p>Note: use the <code>mimalloc-new-delete.h</code> header to override the <em>new</em> and <em>delete</em> operators globally. The wrappers here are mostly for convience for library writers that need to interface with mimalloc from C++. </p>
+<hr/><h2 class="groupheader">Data Structure Documentation</h2>
+<a name="structmi__stl__allocator" id="structmi__stl__allocator"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#structmi__stl__allocator">◆ </a></span>mi_stl_allocator</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">struct mi_stl_allocator</td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+<div class="textblock"><h3>template<class T><br />
+struct mi_stl_allocator< T ></h3>
+
+<p><em>std::allocator</em> implementation for mimalloc for use in STL containers. </p>
+<p>For example: </p><div class="fragment"><div class="line">std::vector<int, mi_stl_allocator<int> > vec;</div><div class="line">vec.push_back(1);</div><div class="line">vec.pop_back();</div></div><!-- fragment --> </div>
+</div>
+</div>
+<h2 class="groupheader">Function Documentation</h2>
+<a id="gaad048a9fce3d02c5909cd05c6ec24545"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaad048a9fce3d02c5909cd05c6ec24545">◆ </a></span>mi_new()</h2>
+
+<div class="memitem">
+<div class="memproto">
+<table class="mlabels">
+ <tr>
+ <td class="mlabels-left">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_new </td>
+ <td>(</td>
+ <td class="paramtype">std::size_t </td>
+ <td class="paramname"><em>n</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+ </td>
+ <td class="mlabels-right">
+<span class="mlabels"><span class="mlabel">noexcept</span></span> </td>
+ </tr>
+</table>
+</div><div class="memdoc">
+
+<p>like <a class="el" href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a" title="Allocate size bytes.">mi_malloc()</a>, but when out of memory, use <code>std::get_new_handler</code> and raise <code>std::bad_alloc</code> exception on failure. </p>
+
+</div>
+</div>
+<a id="gaef2c2bdb4f70857902d3c8903ac095f3"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaef2c2bdb4f70857902d3c8903ac095f3">◆ </a></span>mi_new_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+<table class="mlabels">
+ <tr>
+ <td class="mlabels-left">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_new_aligned </td>
+ <td>(</td>
+ <td class="paramtype">std::size_t </td>
+ <td class="paramname"><em>n</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">std::align_val_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+ </td>
+ <td class="mlabels-right">
+<span class="mlabels"><span class="mlabel">noexcept</span></span> </td>
+ </tr>
+</table>
+</div><div class="memdoc">
+
+<p>like <a class="el" href="group__aligned.html#ga68930196751fa2cca9e1fd0d71bade56" title="Allocate size bytes aligned by alignment.">mi_malloc_aligned()</a>, but when out of memory, use <code>std::get_new_handler</code> and raise <code>std::bad_alloc</code> exception on failure. </p>
+
+</div>
+</div>
+<a id="gab5e29558926d934c3f1cae8c815f942c"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gab5e29558926d934c3f1cae8c815f942c">◆ </a></span>mi_new_aligned_nothrow()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_new_aligned_nothrow </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>n</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>like <code>mi_malloc_aligned</code>, but when out of memory, use <code>std::get_new_handler</code> but return <em>NULL</em> on failure. </p>
+
+</div>
+</div>
+<a id="gae7bc4f56cd57ed3359060ff4f38bda81"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gae7bc4f56cd57ed3359060ff4f38bda81">◆ </a></span>mi_new_n()</h2>
+
+<div class="memitem">
+<div class="memproto">
+<table class="mlabels">
+ <tr>
+ <td class="mlabels-left">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_new_n </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>count</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+ </td>
+ <td class="mlabels-right">
+<span class="mlabels"><span class="mlabel">noexcept</span></span> </td>
+ </tr>
+</table>
+</div><div class="memdoc">
+
+<p>like <a class="el" href="group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6" title="Allocate count elements of size bytes.">mi_mallocn()</a>, but when out of memory, use <code>std::get_new_handler</code> and raise <code>std::bad_alloc</code> exception on failure. </p>
+
+</div>
+</div>
+<a id="gaeaded64eda71ed6b1d569d3e723abc4a"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaeaded64eda71ed6b1d569d3e723abc4a">◆ </a></span>mi_new_nothrow()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_new_nothrow </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>n</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>like <code>mi_malloc</code>, but when out of memory, use <code>std::get_new_handler</code> but return <em>NULL</em> on failure. </p>
+
+</div>
+</div>
+<a id="gaab78a32f55149e9fbf432d5288e38e1e"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaab78a32f55149e9fbf432d5288e38e1e">◆ </a></span>mi_new_realloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_new_realloc </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>like <a class="el" href="group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6" title="Re-allocate memory to newsize bytes.">mi_realloc()</a>, but when out of memory, use <code>std::get_new_handler</code> and raise <code>std::bad_alloc</code> exception on failure. </p>
+
+</div>
+</div>
+<a id="ga756f4b2bc6a7ecd0a90baea8e90c7907"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga756f4b2bc6a7ecd0a90baea8e90c7907">◆ </a></span>mi_new_reallocn()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_new_reallocn </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newcount</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>like <a class="el" href="group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853" title="Re-allocate memory to count elements of size bytes.">mi_reallocn()</a>, but when out of memory, use <code>std::get_new_handler</code> and raise <code>std::bad_alloc</code> exception on failure. </p>
+
+</div>
+</div>
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+var group__cpp =
+[
+ [ "mi_stl_allocator", "group__cpp.html#structmi__stl__allocator", null ],
+ [ "mi_new", "group__cpp.html#gaad048a9fce3d02c5909cd05c6ec24545", null ],
+ [ "mi_new_aligned", "group__cpp.html#gaef2c2bdb4f70857902d3c8903ac095f3", null ],
+ [ "mi_new_aligned_nothrow", "group__cpp.html#gab5e29558926d934c3f1cae8c815f942c", null ],
+ [ "mi_new_n", "group__cpp.html#gae7bc4f56cd57ed3359060ff4f38bda81", null ],
+ [ "mi_new_nothrow", "group__cpp.html#gaeaded64eda71ed6b1d569d3e723abc4a", null ],
+ [ "mi_new_realloc", "group__cpp.html#gaab78a32f55149e9fbf432d5288e38e1e", null ],
+ [ "mi_new_reallocn", "group__cpp.html#ga756f4b2bc6a7ecd0a90baea8e90c7907", null ]
+];
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Extended Functions</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('group__extended.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="summary">
+<a href="#define-members">Macros</a> |
+<a href="#typedef-members">Typedefs</a> |
+<a href="#func-members">Functions</a> </div>
+ <div class="headertitle">
+<div class="title">Extended Functions</div> </div>
+</div><!--header-->
+<div class="contents">
+
+<p>Extended functionality.
+<a href="#details">More...</a></p>
+<table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="define-members"></a>
+Macros</h2></td></tr>
+<tr class="memitem:ga1ea64283508718d9d645c38efc2f4305"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga1ea64283508718d9d645c38efc2f4305">MI_SMALL_SIZE_MAX</a></td></tr>
+<tr class="memdesc:ga1ea64283508718d9d645c38efc2f4305"><td class="mdescLeft"> </td><td class="mdescRight">Maximum size allowed for small allocations in <a class="el" href="group__extended.html#ga7136c2e55cb22c98ecf95d08d6debb99" title="Allocate a small object.">mi_malloc_small</a> and <a class="el" href="group__extended.html#ga220f29f40a44404b0061c15bc1c31152" title="Allocate a zero initialized small object.">mi_zalloc_small</a> (usually <code>128*sizeof(void*)</code> (= 1KB on 64-bit systems)) <a href="#ga1ea64283508718d9d645c38efc2f4305">More...</a><br /></td></tr>
+<tr class="separator:ga1ea64283508718d9d645c38efc2f4305"><td class="memSeparator" colspan="2"> </td></tr>
+</table><table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="typedef-members"></a>
+Typedefs</h2></td></tr>
+<tr class="memitem:ga299dae78d25ce112e384a98b7309c5be"><td class="memItemLeft" align="right" valign="top">typedef void() </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga299dae78d25ce112e384a98b7309c5be">mi_deferred_free_fun</a>(bool force, unsigned long long heartbeat, void *arg)</td></tr>
+<tr class="memdesc:ga299dae78d25ce112e384a98b7309c5be"><td class="mdescLeft"> </td><td class="mdescRight">Type of deferred free functions. <a href="#ga299dae78d25ce112e384a98b7309c5be">More...</a><br /></td></tr>
+<tr class="separator:ga299dae78d25ce112e384a98b7309c5be"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gad823d23444a4b77a40f66bf075a98a0c"><td class="memItemLeft" align="right" valign="top">typedef void() </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c">mi_output_fun</a>(const char *msg, void *arg)</td></tr>
+<tr class="memdesc:gad823d23444a4b77a40f66bf075a98a0c"><td class="mdescLeft"> </td><td class="mdescRight">Type of output functions. <a href="#gad823d23444a4b77a40f66bf075a98a0c">More...</a><br /></td></tr>
+<tr class="separator:gad823d23444a4b77a40f66bf075a98a0c"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga251d369cda3f1c2a955c555486ed90e5"><td class="memItemLeft" align="right" valign="top">typedef void() </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga251d369cda3f1c2a955c555486ed90e5">mi_error_fun</a>(int err, void *arg)</td></tr>
+<tr class="memdesc:ga251d369cda3f1c2a955c555486ed90e5"><td class="mdescLeft"> </td><td class="mdescRight">Type of error callback functions. <a href="#ga251d369cda3f1c2a955c555486ed90e5">More...</a><br /></td></tr>
+<tr class="separator:ga251d369cda3f1c2a955c555486ed90e5"><td class="memSeparator" colspan="2"> </td></tr>
+</table><table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="func-members"></a>
+Functions</h2></td></tr>
+<tr class="memitem:ga7136c2e55cb22c98ecf95d08d6debb99"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga7136c2e55cb22c98ecf95d08d6debb99">mi_malloc_small</a> (size_t size)</td></tr>
+<tr class="memdesc:ga7136c2e55cb22c98ecf95d08d6debb99"><td class="mdescLeft"> </td><td class="mdescRight">Allocate a small object. <a href="#ga7136c2e55cb22c98ecf95d08d6debb99">More...</a><br /></td></tr>
+<tr class="separator:ga7136c2e55cb22c98ecf95d08d6debb99"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga220f29f40a44404b0061c15bc1c31152"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga220f29f40a44404b0061c15bc1c31152">mi_zalloc_small</a> (size_t size)</td></tr>
+<tr class="memdesc:ga220f29f40a44404b0061c15bc1c31152"><td class="mdescLeft"> </td><td class="mdescRight">Allocate a zero initialized small object. <a href="#ga220f29f40a44404b0061c15bc1c31152">More...</a><br /></td></tr>
+<tr class="separator:ga220f29f40a44404b0061c15bc1c31152"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga089c859d9eddc5f9b4bd946cd53cebee"><td class="memItemLeft" align="right" valign="top">size_t </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga089c859d9eddc5f9b4bd946cd53cebee">mi_usable_size</a> (void *p)</td></tr>
+<tr class="memdesc:ga089c859d9eddc5f9b4bd946cd53cebee"><td class="mdescLeft"> </td><td class="mdescRight">Return the available bytes in a memory block. <a href="#ga089c859d9eddc5f9b4bd946cd53cebee">More...</a><br /></td></tr>
+<tr class="separator:ga089c859d9eddc5f9b4bd946cd53cebee"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gac057927cd06c854b45fe7847e921bd47"><td class="memItemLeft" align="right" valign="top">size_t </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#gac057927cd06c854b45fe7847e921bd47">mi_good_size</a> (size_t size)</td></tr>
+<tr class="memdesc:gac057927cd06c854b45fe7847e921bd47"><td class="mdescLeft"> </td><td class="mdescRight">Return the used allocation size. <a href="#gac057927cd06c854b45fe7847e921bd47">More...</a><br /></td></tr>
+<tr class="separator:gac057927cd06c854b45fe7847e921bd47"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga421430e2226d7d468529cec457396756"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga421430e2226d7d468529cec457396756">mi_collect</a> (bool force)</td></tr>
+<tr class="memdesc:ga421430e2226d7d468529cec457396756"><td class="mdescLeft"> </td><td class="mdescRight">Eagerly free memory. <a href="#ga421430e2226d7d468529cec457396756">More...</a><br /></td></tr>
+<tr class="separator:ga421430e2226d7d468529cec457396756"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga2d126e5c62d3badc35445e5d84166df2"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga2d126e5c62d3badc35445e5d84166df2">mi_stats_print</a> (void *out)</td></tr>
+<tr class="memdesc:ga2d126e5c62d3badc35445e5d84166df2"><td class="mdescLeft"> </td><td class="mdescRight">Deprecated. <a href="#ga2d126e5c62d3badc35445e5d84166df2">More...</a><br /></td></tr>
+<tr class="separator:ga2d126e5c62d3badc35445e5d84166df2"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga537f13b299ddf801e49a5a94fde02c79"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga537f13b299ddf801e49a5a94fde02c79">mi_stats_print_out</a> (<a class="el" href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c">mi_output_fun</a> *out, void *arg)</td></tr>
+<tr class="memdesc:ga537f13b299ddf801e49a5a94fde02c79"><td class="mdescLeft"> </td><td class="mdescRight">Print the main statistics. <a href="#ga537f13b299ddf801e49a5a94fde02c79">More...</a><br /></td></tr>
+<tr class="separator:ga537f13b299ddf801e49a5a94fde02c79"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga3bb8468b8cfcc6e2a61d98aee85c5f99"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga3bb8468b8cfcc6e2a61d98aee85c5f99">mi_stats_reset</a> (void)</td></tr>
+<tr class="memdesc:ga3bb8468b8cfcc6e2a61d98aee85c5f99"><td class="mdescLeft"> </td><td class="mdescRight">Reset statistics. <a href="#ga3bb8468b8cfcc6e2a61d98aee85c5f99">More...</a><br /></td></tr>
+<tr class="separator:ga3bb8468b8cfcc6e2a61d98aee85c5f99"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga854b1de8cb067c7316286c28b2fcd3d1"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga854b1de8cb067c7316286c28b2fcd3d1">mi_stats_merge</a> (void)</td></tr>
+<tr class="memdesc:ga854b1de8cb067c7316286c28b2fcd3d1"><td class="mdescLeft"> </td><td class="mdescRight">Merge thread local statistics with the main statistics and reset. <a href="#ga854b1de8cb067c7316286c28b2fcd3d1">More...</a><br /></td></tr>
+<tr class="separator:ga854b1de8cb067c7316286c28b2fcd3d1"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaf8e73efc2cbca9ebfdfb166983a04c17"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#gaf8e73efc2cbca9ebfdfb166983a04c17">mi_thread_init</a> (void)</td></tr>
+<tr class="memdesc:gaf8e73efc2cbca9ebfdfb166983a04c17"><td class="mdescLeft"> </td><td class="mdescRight">Initialize mimalloc on a thread. <a href="#gaf8e73efc2cbca9ebfdfb166983a04c17">More...</a><br /></td></tr>
+<tr class="separator:gaf8e73efc2cbca9ebfdfb166983a04c17"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga0ae4581e85453456a0d658b2b98bf7bf"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga0ae4581e85453456a0d658b2b98bf7bf">mi_thread_done</a> (void)</td></tr>
+<tr class="memdesc:ga0ae4581e85453456a0d658b2b98bf7bf"><td class="mdescLeft"> </td><td class="mdescRight">Uninitialize mimalloc on a thread. <a href="#ga0ae4581e85453456a0d658b2b98bf7bf">More...</a><br /></td></tr>
+<tr class="separator:ga0ae4581e85453456a0d658b2b98bf7bf"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gab1dac8476c46cb9eecab767eb40c1525"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#gab1dac8476c46cb9eecab767eb40c1525">mi_thread_stats_print_out</a> (<a class="el" href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c">mi_output_fun</a> *out, void *arg)</td></tr>
+<tr class="memdesc:gab1dac8476c46cb9eecab767eb40c1525"><td class="mdescLeft"> </td><td class="mdescRight">Print out heap statistics for this thread. <a href="#gab1dac8476c46cb9eecab767eb40c1525">More...</a><br /></td></tr>
+<tr class="separator:gab1dac8476c46cb9eecab767eb40c1525"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga3460a6ca91af97be4058f523d3cb8ece"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga3460a6ca91af97be4058f523d3cb8ece">mi_register_deferred_free</a> (<a class="el" href="group__extended.html#ga299dae78d25ce112e384a98b7309c5be">mi_deferred_free_fun</a> *deferred_free, void *arg)</td></tr>
+<tr class="memdesc:ga3460a6ca91af97be4058f523d3cb8ece"><td class="mdescLeft"> </td><td class="mdescRight">Register a deferred free function. <a href="#ga3460a6ca91af97be4058f523d3cb8ece">More...</a><br /></td></tr>
+<tr class="separator:ga3460a6ca91af97be4058f523d3cb8ece"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gae5b17ff027cd2150b43a33040250cf3f"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#gae5b17ff027cd2150b43a33040250cf3f">mi_register_output</a> (<a class="el" href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c">mi_output_fun</a> *out, void *arg)</td></tr>
+<tr class="memdesc:gae5b17ff027cd2150b43a33040250cf3f"><td class="mdescLeft"> </td><td class="mdescRight">Register an output function. <a href="#gae5b17ff027cd2150b43a33040250cf3f">More...</a><br /></td></tr>
+<tr class="separator:gae5b17ff027cd2150b43a33040250cf3f"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaa1d55e0e894be240827e5d87ec3a1f45"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#gaa1d55e0e894be240827e5d87ec3a1f45">mi_register_error</a> (<a class="el" href="group__extended.html#ga251d369cda3f1c2a955c555486ed90e5">mi_error_fun</a> *errfun, void *arg)</td></tr>
+<tr class="memdesc:gaa1d55e0e894be240827e5d87ec3a1f45"><td class="mdescLeft"> </td><td class="mdescRight">Register an error callback function. <a href="#gaa1d55e0e894be240827e5d87ec3a1f45">More...</a><br /></td></tr>
+<tr class="separator:gaa1d55e0e894be240827e5d87ec3a1f45"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga5f071b10d4df1c3658e04e7fd67a94e6"><td class="memItemLeft" align="right" valign="top">bool </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga5f071b10d4df1c3658e04e7fd67a94e6">mi_is_in_heap_region</a> (const void *p)</td></tr>
+<tr class="memdesc:ga5f071b10d4df1c3658e04e7fd67a94e6"><td class="mdescLeft"> </td><td class="mdescRight">Is a pointer part of our heap? <a href="#ga5f071b10d4df1c3658e04e7fd67a94e6">More...</a><br /></td></tr>
+<tr class="separator:ga5f071b10d4df1c3658e04e7fd67a94e6"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga3132f521fb756fc0e8ec0b74fb58df50"><td class="memItemLeft" align="right" valign="top">int </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga3132f521fb756fc0e8ec0b74fb58df50">mi_reserve_huge_os_pages_interleave</a> (size_t pages, size_t numa_nodes, size_t timeout_msecs)</td></tr>
+<tr class="memdesc:ga3132f521fb756fc0e8ec0b74fb58df50"><td class="mdescLeft"> </td><td class="mdescRight">Reserve <em>pages</em> of huge OS pages (1GiB) evenly divided over <em>numa_nodes</em> nodes, but stops after at most <code>timeout_msecs</code> seconds. <a href="#ga3132f521fb756fc0e8ec0b74fb58df50">More...</a><br /></td></tr>
+<tr class="separator:ga3132f521fb756fc0e8ec0b74fb58df50"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga7795a13d20087447281858d2c771cca1"><td class="memItemLeft" align="right" valign="top">int </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#ga7795a13d20087447281858d2c771cca1">mi_reserve_huge_os_pages_at</a> (size_t pages, int numa_node, size_t timeout_msecs)</td></tr>
+<tr class="memdesc:ga7795a13d20087447281858d2c771cca1"><td class="mdescLeft"> </td><td class="mdescRight">Reserve <em>pages</em> of huge OS pages (1GiB) at a specific <em>numa_node</em>, but stops after at most <code>timeout_msecs</code> seconds. <a href="#ga7795a13d20087447281858d2c771cca1">More...</a><br /></td></tr>
+<tr class="separator:ga7795a13d20087447281858d2c771cca1"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaad25050b19f30cd79397b227e0157a3f"><td class="memItemLeft" align="right" valign="top">bool </td><td class="memItemRight" valign="bottom"><a class="el" href="group__extended.html#gaad25050b19f30cd79397b227e0157a3f">mi_is_redirected</a> ()</td></tr>
+<tr class="memdesc:gaad25050b19f30cd79397b227e0157a3f"><td class="mdescLeft"> </td><td class="mdescRight">Is the C runtime <em>malloc</em> API redirected? <a href="#gaad25050b19f30cd79397b227e0157a3f">More...</a><br /></td></tr>
+<tr class="separator:gaad25050b19f30cd79397b227e0157a3f"><td class="memSeparator" colspan="2"> </td></tr>
+</table>
+<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
+<p>Extended functionality. </p>
+<h2 class="groupheader">Macro Definition Documentation</h2>
+<a id="ga1ea64283508718d9d645c38efc2f4305"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga1ea64283508718d9d645c38efc2f4305">◆ </a></span>MI_SMALL_SIZE_MAX</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">#define MI_SMALL_SIZE_MAX</td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Maximum size allowed for small allocations in <a class="el" href="group__extended.html#ga7136c2e55cb22c98ecf95d08d6debb99" title="Allocate a small object.">mi_malloc_small</a> and <a class="el" href="group__extended.html#ga220f29f40a44404b0061c15bc1c31152" title="Allocate a zero initialized small object.">mi_zalloc_small</a> (usually <code>128*sizeof(void*)</code> (= 1KB on 64-bit systems)) </p>
+
+</div>
+</div>
+<h2 class="groupheader">Typedef Documentation</h2>
+<a id="ga299dae78d25ce112e384a98b7309c5be"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga299dae78d25ce112e384a98b7309c5be">◆ </a></span>mi_deferred_free_fun</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">typedef void() mi_deferred_free_fun(bool force, unsigned long long heartbeat, void *arg)</td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Type of deferred free functions. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">force</td><td>If <em>true</em> all outstanding items should be freed. </td></tr>
+ <tr><td class="paramname">heartbeat</td><td>A monotonically increasing count. </td></tr>
+ <tr><td class="paramname">arg</td><td>Argument that was passed at registration to hold extra state.</td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section see"><dt>See also</dt><dd><a class="el" href="group__extended.html#ga3460a6ca91af97be4058f523d3cb8ece" title="Register a deferred free function.">mi_register_deferred_free</a> </dd></dl>
+
+</div>
+</div>
+<a id="ga251d369cda3f1c2a955c555486ed90e5"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga251d369cda3f1c2a955c555486ed90e5">◆ </a></span>mi_error_fun</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">typedef void() mi_error_fun(int err, void *arg)</td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Type of error callback functions. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">err</td><td>Error code (see <a class="el" href="group__extended.html#gaa1d55e0e894be240827e5d87ec3a1f45" title="Register an error callback function.">mi_register_error()</a> for a complete list). </td></tr>
+ <tr><td class="paramname">arg</td><td>Argument that was passed at registration to hold extra state.</td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section see"><dt>See also</dt><dd><a class="el" href="group__extended.html#gaa1d55e0e894be240827e5d87ec3a1f45" title="Register an error callback function.">mi_register_error()</a> </dd></dl>
+
+</div>
+</div>
+<a id="gad823d23444a4b77a40f66bf075a98a0c"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gad823d23444a4b77a40f66bf075a98a0c">◆ </a></span>mi_output_fun</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">typedef void() mi_output_fun(const char *msg, void *arg)</td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Type of output functions. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">msg</td><td>Message to output. </td></tr>
+ <tr><td class="paramname">arg</td><td>Argument that was passed at registration to hold extra state.</td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section see"><dt>See also</dt><dd><a class="el" href="group__extended.html#gae5b17ff027cd2150b43a33040250cf3f" title="Register an output function.">mi_register_output()</a> </dd></dl>
+
+</div>
+</div>
+<h2 class="groupheader">Function Documentation</h2>
+<a id="ga421430e2226d7d468529cec457396756"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga421430e2226d7d468529cec457396756">◆ </a></span>mi_collect()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_collect </td>
+ <td>(</td>
+ <td class="paramtype">bool </td>
+ <td class="paramname"><em>force</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Eagerly free memory. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">force</td><td>If <em>true</em>, aggressively return memory to the OS (can be expensive!)</td></tr>
+ </table>
+ </dd>
+</dl>
+<p>Regular code should not have to call this function. It can be beneficial in very narrow circumstances; in particular, when a long running thread allocates a lot of blocks that are freed by other threads it may improve resource usage by calling this every once in a while. </p>
+
+</div>
+</div>
+<a id="gac057927cd06c854b45fe7847e921bd47"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gac057927cd06c854b45fe7847e921bd47">◆ </a></span>mi_good_size()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">size_t mi_good_size </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Return the used allocation size. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">size</td><td>The minimal required size in bytes. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>the size <code>n</code> that will be allocated, where <code>n >= size</code>.</dd></dl>
+<p>Generally, <code>mi_usable_size(mi_malloc(size)) == mi_good_size(size)</code>. This can be used to reduce internal wasted space when allocating buffers for example.</p>
+<dl class="section see"><dt>See also</dt><dd><a class="el" href="group__extended.html#ga089c859d9eddc5f9b4bd946cd53cebee" title="Return the available bytes in a memory block.">mi_usable_size()</a> </dd></dl>
+
+</div>
+</div>
+<a id="ga5f071b10d4df1c3658e04e7fd67a94e6"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga5f071b10d4df1c3658e04e7fd67a94e6">◆ </a></span>mi_is_in_heap_region()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">bool mi_is_in_heap_region </td>
+ <td>(</td>
+ <td class="paramtype">const void * </td>
+ <td class="paramname"><em>p</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Is a pointer part of our heap? </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">p</td><td>The pointer to check. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd><em>true</em> if this is a pointer into our heap. This function is relatively fast. </dd></dl>
+
+</div>
+</div>
+<a id="gaad25050b19f30cd79397b227e0157a3f"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaad25050b19f30cd79397b227e0157a3f">◆ </a></span>mi_is_redirected()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">bool mi_is_redirected </td>
+ <td>(</td>
+ <td class="paramname"></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Is the C runtime <em>malloc</em> API redirected? </p>
+<dl class="section return"><dt>Returns</dt><dd><em>true</em> if all malloc API calls are redirected to mimalloc.</dd></dl>
+<p>Currenty only used on Windows. </p>
+
+</div>
+</div>
+<a id="ga7136c2e55cb22c98ecf95d08d6debb99"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga7136c2e55cb22c98ecf95d08d6debb99">◆ </a></span>mi_malloc_small()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_malloc_small </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate a small object. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">size</td><td>The size in bytes, can be at most <a class="el" href="group__extended.html#ga1ea64283508718d9d645c38efc2f4305" title="Maximum size allowed for small allocations in mi_malloc_small and mi_zalloc_small (usually 128*sizeof...">MI_SMALL_SIZE_MAX</a>. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>a pointer to newly allocated memory of at least <em>size</em> bytes, or <em>NULL</em> if out of memory. This function is meant for use in run-time systems for best performance and does not check if <em>size</em> was indeed small – use with care! </dd></dl>
+
+</div>
+</div>
+<a id="ga3460a6ca91af97be4058f523d3cb8ece"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga3460a6ca91af97be4058f523d3cb8ece">◆ </a></span>mi_register_deferred_free()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_register_deferred_free </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__extended.html#ga299dae78d25ce112e384a98b7309c5be">mi_deferred_free_fun</a> * </td>
+ <td class="paramname"><em>deferred_free</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>arg</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Register a deferred free function. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">deferred_free</td><td>Address of a deferred free-ing function or <em>NULL</em> to unregister. </td></tr>
+ <tr><td class="paramname">arg</td><td>Argument that will be passed on to the deferred free function.</td></tr>
+ </table>
+ </dd>
+</dl>
+<p>Some runtime systems use deferred free-ing, for example when using reference counting to limit the worst case free time. Such systems can register (re-entrant) deferred free function to free more memory on demand. When the <em>force</em> parameter is <em>true</em> all possible memory should be freed. The per-thread <em>heartbeat</em> parameter is monotonically increasing and guaranteed to be deterministic if the program allocates deterministically. The <em>deferred_free</em> function is guaranteed to be called deterministically after some number of allocations (regardless of freeing or available free memory). At most one <em>deferred_free</em> function can be active. </p>
+
+</div>
+</div>
+<a id="gaa1d55e0e894be240827e5d87ec3a1f45"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaa1d55e0e894be240827e5d87ec3a1f45">◆ </a></span>mi_register_error()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_register_error </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__extended.html#ga251d369cda3f1c2a955c555486ed90e5">mi_error_fun</a> * </td>
+ <td class="paramname"><em>errfun</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>arg</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Register an error callback function. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">errfun</td><td>The error function that is called on an error (use <em>NULL</em> for default) </td></tr>
+ <tr><td class="paramname">arg</td><td>Extra argument that will be passed on to the error function.</td></tr>
+ </table>
+ </dd>
+</dl>
+<p>The <em>errfun</em> function is called on an error in mimalloc after emitting an error message (through the output function). It as always legal to just return from the <em>errfun</em> function in which case allocation functions generally return <em>NULL</em> or ignore the condition. The default function only calls abort() when compiled in secure mode with an <em>EFAULT</em> error. The possible error codes are:</p><ul>
+<li><em>EAGAIN:</em> Double free was detected (only in debug and secure mode).</li>
+<li><em>EFAULT:</em> Corrupted free list or meta-data was detected (only in debug and secure mode).</li>
+<li><em>ENOMEM:</em> Not enough memory available to satisfy the request.</li>
+<li><em>EOVERFLOW:</em> Too large a request, for example in <a class="el" href="group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d" title="Allocate zero-initialized count elements of size bytes.">mi_calloc()</a>, the <em>count</em> and <em>size</em> parameters are too large.</li>
+<li><em>EINVAL:</em> Trying to free or re-allocate an invalid pointer. </li>
+</ul>
+
+</div>
+</div>
+<a id="gae5b17ff027cd2150b43a33040250cf3f"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gae5b17ff027cd2150b43a33040250cf3f">◆ </a></span>mi_register_output()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_register_output </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c">mi_output_fun</a> * </td>
+ <td class="paramname"><em>out</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>arg</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Register an output function. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">out</td><td>The output function, use <code>NULL</code> to output to stderr. </td></tr>
+ <tr><td class="paramname">arg</td><td>Argument that will be passed on to the output function.</td></tr>
+ </table>
+ </dd>
+</dl>
+<p>The <code>out</code> function is called to output any information from mimalloc, like verbose or warning messages. </p>
+
+</div>
+</div>
+<a id="ga7795a13d20087447281858d2c771cca1"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga7795a13d20087447281858d2c771cca1">◆ </a></span>mi_reserve_huge_os_pages_at()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">int mi_reserve_huge_os_pages_at </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>pages</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">int </td>
+ <td class="paramname"><em>numa_node</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>timeout_msecs</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Reserve <em>pages</em> of huge OS pages (1GiB) at a specific <em>numa_node</em>, but stops after at most <code>timeout_msecs</code> seconds. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">pages</td><td>The number of 1GiB pages to reserve. </td></tr>
+ <tr><td class="paramname">numa_node</td><td>The NUMA node where the memory is reserved (start at 0). </td></tr>
+ <tr><td class="paramname">timeout_msecs</td><td>Maximum number of milli-seconds to try reserving, or 0 for no timeout. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>0 if successfull, <em>ENOMEM</em> if running out of memory, or <em>ETIMEDOUT</em> if timed out.</dd></dl>
+<p>The reserved memory is used by mimalloc to satisfy allocations. May quit before <em>timeout_msecs</em> are expired if it estimates it will take more than 1.5 times <em>timeout_msecs</em>. The time limit is needed because on some operating systems it can take a long time to reserve contiguous memory if the physical memory is fragmented. </p>
+
+</div>
+</div>
+<a id="ga3132f521fb756fc0e8ec0b74fb58df50"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga3132f521fb756fc0e8ec0b74fb58df50">◆ </a></span>mi_reserve_huge_os_pages_interleave()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">int mi_reserve_huge_os_pages_interleave </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>pages</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>numa_nodes</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>timeout_msecs</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Reserve <em>pages</em> of huge OS pages (1GiB) evenly divided over <em>numa_nodes</em> nodes, but stops after at most <code>timeout_msecs</code> seconds. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">pages</td><td>The number of 1GiB pages to reserve. </td></tr>
+ <tr><td class="paramname">numa_nodes</td><td>The number of nodes do evenly divide the pages over, or 0 for using the actual number of NUMA nodes. </td></tr>
+ <tr><td class="paramname">timeout_msecs</td><td>Maximum number of milli-seconds to try reserving, or 0 for no timeout. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>0 if successfull, <em>ENOMEM</em> if running out of memory, or <em>ETIMEDOUT</em> if timed out.</dd></dl>
+<p>The reserved memory is used by mimalloc to satisfy allocations. May quit before <em>timeout_msecs</em> are expired if it estimates it will take more than 1.5 times <em>timeout_msecs</em>. The time limit is needed because on some operating systems it can take a long time to reserve contiguous memory if the physical memory is fragmented. </p>
+
+</div>
+</div>
+<a id="ga854b1de8cb067c7316286c28b2fcd3d1"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga854b1de8cb067c7316286c28b2fcd3d1">◆ </a></span>mi_stats_merge()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_stats_merge </td>
+ <td>(</td>
+ <td class="paramtype">void </td>
+ <td class="paramname"></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Merge thread local statistics with the main statistics and reset. </p>
+
+</div>
+</div>
+<a id="ga2d126e5c62d3badc35445e5d84166df2"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga2d126e5c62d3badc35445e5d84166df2">◆ </a></span>mi_stats_print()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_stats_print </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>out</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Deprecated. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">out</td><td>Ignored, outputs to the registered output function or stderr by default.</td></tr>
+ </table>
+ </dd>
+</dl>
+<p>Most detailed when using a debug build. </p>
+
+</div>
+</div>
+<a id="ga537f13b299ddf801e49a5a94fde02c79"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga537f13b299ddf801e49a5a94fde02c79">◆ </a></span>mi_stats_print_out()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_stats_print_out </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c">mi_output_fun</a> * </td>
+ <td class="paramname"><em>out</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>arg</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Print the main statistics. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">out</td><td>An output function or <em>NULL</em> for the default. </td></tr>
+ <tr><td class="paramname">arg</td><td>Optional argument passed to <em>out</em> (if not <em>NULL</em>)</td></tr>
+ </table>
+ </dd>
+</dl>
+<p>Most detailed when using a debug build. </p>
+
+</div>
+</div>
+<a id="ga3bb8468b8cfcc6e2a61d98aee85c5f99"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga3bb8468b8cfcc6e2a61d98aee85c5f99">◆ </a></span>mi_stats_reset()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_stats_reset </td>
+ <td>(</td>
+ <td class="paramtype">void </td>
+ <td class="paramname"></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Reset statistics. </p>
+
+</div>
+</div>
+<a id="ga0ae4581e85453456a0d658b2b98bf7bf"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga0ae4581e85453456a0d658b2b98bf7bf">◆ </a></span>mi_thread_done()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_thread_done </td>
+ <td>(</td>
+ <td class="paramtype">void </td>
+ <td class="paramname"></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Uninitialize mimalloc on a thread. </p>
+<p>Should not be used as on most systems (pthreads, windows) this is done automatically. Ensures that any memory that is not freed yet (but will be freed by other threads in the future) is properly handled. </p>
+
+</div>
+</div>
+<a id="gaf8e73efc2cbca9ebfdfb166983a04c17"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaf8e73efc2cbca9ebfdfb166983a04c17">◆ </a></span>mi_thread_init()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_thread_init </td>
+ <td>(</td>
+ <td class="paramtype">void </td>
+ <td class="paramname"></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Initialize mimalloc on a thread. </p>
+<p>Should not be used as on most systems (pthreads, windows) this is done automatically. </p>
+
+</div>
+</div>
+<a id="gab1dac8476c46cb9eecab767eb40c1525"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gab1dac8476c46cb9eecab767eb40c1525">◆ </a></span>mi_thread_stats_print_out()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_thread_stats_print_out </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c">mi_output_fun</a> * </td>
+ <td class="paramname"><em>out</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>arg</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Print out heap statistics for this thread. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">out</td><td>An output function or <em>NULL</em> for the default. </td></tr>
+ <tr><td class="paramname">arg</td><td>Optional argument passed to <em>out</em> (if not <em>NULL</em>)</td></tr>
+ </table>
+ </dd>
+</dl>
+<p>Most detailed when using a debug build. </p>
+
+</div>
+</div>
+<a id="ga089c859d9eddc5f9b4bd946cd53cebee"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga089c859d9eddc5f9b4bd946cd53cebee">◆ </a></span>mi_usable_size()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">size_t mi_usable_size </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Return the available bytes in a memory block. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">p</td><td>Pointer to previously allocated memory (or <em>NULL</em>) </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>Returns the available bytes in the memory block, or 0 if <em>p</em> was <em>NULL</em>.</dd></dl>
+<p>The returned size can be used to call <em>mi_expand</em> successfully. The returned size is always at least equal to the allocated size of <em>p</em>, and, in the current design, should be less than 16.7% more.</p>
+<dl class="section see"><dt>See also</dt><dd><a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/msize?view=vs-2017">_msize</a> (Windows) </dd>
+<dd>
+<a href="http://man7.org/linux/man-pages/man3/malloc_usable_size.3.html">malloc_usable_size</a> (Linux) </dd>
+<dd>
+<a class="el" href="group__extended.html#gac057927cd06c854b45fe7847e921bd47" title="Return the used allocation size.">mi_good_size()</a> </dd></dl>
+
+</div>
+</div>
+<a id="ga220f29f40a44404b0061c15bc1c31152"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga220f29f40a44404b0061c15bc1c31152">◆ </a></span>mi_zalloc_small()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_zalloc_small </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate a zero initialized small object. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">size</td><td>The size in bytes, can be at most <a class="el" href="group__extended.html#ga1ea64283508718d9d645c38efc2f4305" title="Maximum size allowed for small allocations in mi_malloc_small and mi_zalloc_small (usually 128*sizeof...">MI_SMALL_SIZE_MAX</a>. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>a pointer to newly allocated zero-initialized memory of at least <em>size</em> bytes, or <em>NULL</em> if out of memory. This function is meant for use in run-time systems for best performance and does not check if <em>size</em> was indeed small – use with care! </dd></dl>
+
+</div>
+</div>
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+var group__extended =
+[
+ [ "MI_SMALL_SIZE_MAX", "group__extended.html#ga1ea64283508718d9d645c38efc2f4305", null ],
+ [ "mi_deferred_free_fun", "group__extended.html#ga299dae78d25ce112e384a98b7309c5be", null ],
+ [ "mi_error_fun", "group__extended.html#ga251d369cda3f1c2a955c555486ed90e5", null ],
+ [ "mi_output_fun", "group__extended.html#gad823d23444a4b77a40f66bf075a98a0c", null ],
+ [ "mi_collect", "group__extended.html#ga421430e2226d7d468529cec457396756", null ],
+ [ "mi_good_size", "group__extended.html#gac057927cd06c854b45fe7847e921bd47", null ],
+ [ "mi_is_in_heap_region", "group__extended.html#ga5f071b10d4df1c3658e04e7fd67a94e6", null ],
+ [ "mi_is_redirected", "group__extended.html#gaad25050b19f30cd79397b227e0157a3f", null ],
+ [ "mi_malloc_small", "group__extended.html#ga7136c2e55cb22c98ecf95d08d6debb99", null ],
+ [ "mi_register_deferred_free", "group__extended.html#ga3460a6ca91af97be4058f523d3cb8ece", null ],
+ [ "mi_register_error", "group__extended.html#gaa1d55e0e894be240827e5d87ec3a1f45", null ],
+ [ "mi_register_output", "group__extended.html#gae5b17ff027cd2150b43a33040250cf3f", null ],
+ [ "mi_reserve_huge_os_pages_at", "group__extended.html#ga7795a13d20087447281858d2c771cca1", null ],
+ [ "mi_reserve_huge_os_pages_interleave", "group__extended.html#ga3132f521fb756fc0e8ec0b74fb58df50", null ],
+ [ "mi_stats_merge", "group__extended.html#ga854b1de8cb067c7316286c28b2fcd3d1", null ],
+ [ "mi_stats_print", "group__extended.html#ga2d126e5c62d3badc35445e5d84166df2", null ],
+ [ "mi_stats_print_out", "group__extended.html#ga537f13b299ddf801e49a5a94fde02c79", null ],
+ [ "mi_stats_reset", "group__extended.html#ga3bb8468b8cfcc6e2a61d98aee85c5f99", null ],
+ [ "mi_thread_done", "group__extended.html#ga0ae4581e85453456a0d658b2b98bf7bf", null ],
+ [ "mi_thread_init", "group__extended.html#gaf8e73efc2cbca9ebfdfb166983a04c17", null ],
+ [ "mi_thread_stats_print_out", "group__extended.html#gab1dac8476c46cb9eecab767eb40c1525", null ],
+ [ "mi_usable_size", "group__extended.html#ga089c859d9eddc5f9b4bd946cd53cebee", null ],
+ [ "mi_zalloc_small", "group__extended.html#ga220f29f40a44404b0061c15bc1c31152", null ]
+];
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Heap Allocation</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('group__heap.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="summary">
+<a href="#typedef-members">Typedefs</a> |
+<a href="#func-members">Functions</a> </div>
+ <div class="headertitle">
+<div class="title">Heap Allocation</div> </div>
+</div><!--header-->
+<div class="contents">
+
+<p>First-class heaps that can be destroyed in one go.
+<a href="#details">More...</a></p>
+<table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="typedef-members"></a>
+Typedefs</h2></td></tr>
+<tr class="memitem:ga34a47cde5a5b38c29f1aa3c5e76943c2"><td class="memItemLeft" align="right" valign="top">typedef struct mi_heap_s </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a></td></tr>
+<tr class="memdesc:ga34a47cde5a5b38c29f1aa3c5e76943c2"><td class="mdescLeft"> </td><td class="mdescRight">Type of first-class heaps. <a href="#ga34a47cde5a5b38c29f1aa3c5e76943c2">More...</a><br /></td></tr>
+<tr class="separator:ga34a47cde5a5b38c29f1aa3c5e76943c2"><td class="memSeparator" colspan="2"> </td></tr>
+</table><table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="func-members"></a>
+Functions</h2></td></tr>
+<tr class="memitem:ga766f672ba56f2fbfeb9d9dbb0b7f6b11"><td class="memItemLeft" align="right" valign="top"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga766f672ba56f2fbfeb9d9dbb0b7f6b11">mi_heap_new</a> ()</td></tr>
+<tr class="memdesc:ga766f672ba56f2fbfeb9d9dbb0b7f6b11"><td class="mdescLeft"> </td><td class="mdescRight">Create a new heap that can be used for allocation. <a href="#ga766f672ba56f2fbfeb9d9dbb0b7f6b11">More...</a><br /></td></tr>
+<tr class="separator:ga766f672ba56f2fbfeb9d9dbb0b7f6b11"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga2ab1af8d438819b55319c7ef51d1e409"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga2ab1af8d438819b55319c7ef51d1e409">mi_heap_delete</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap)</td></tr>
+<tr class="memdesc:ga2ab1af8d438819b55319c7ef51d1e409"><td class="mdescLeft"> </td><td class="mdescRight">Delete a previously allocated heap. <a href="#ga2ab1af8d438819b55319c7ef51d1e409">More...</a><br /></td></tr>
+<tr class="separator:ga2ab1af8d438819b55319c7ef51d1e409"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga9f9c0844edb9717f4feacd79116b8e0d"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga9f9c0844edb9717f4feacd79116b8e0d">mi_heap_destroy</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap)</td></tr>
+<tr class="memdesc:ga9f9c0844edb9717f4feacd79116b8e0d"><td class="mdescLeft"> </td><td class="mdescRight">Destroy a heap, freeing all its still allocated blocks. <a href="#ga9f9c0844edb9717f4feacd79116b8e0d">More...</a><br /></td></tr>
+<tr class="separator:ga9f9c0844edb9717f4feacd79116b8e0d"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gab8631ec88c8d26641b68b5d25dcd4422"><td class="memItemLeft" align="right" valign="top"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#gab8631ec88c8d26641b68b5d25dcd4422">mi_heap_set_default</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap)</td></tr>
+<tr class="memdesc:gab8631ec88c8d26641b68b5d25dcd4422"><td class="mdescLeft"> </td><td class="mdescRight">Set the default heap to use for <a class="el" href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a" title="Allocate size bytes.">mi_malloc()</a> et al. <a href="#gab8631ec88c8d26641b68b5d25dcd4422">More...</a><br /></td></tr>
+<tr class="separator:gab8631ec88c8d26641b68b5d25dcd4422"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga8db4cbb87314a989a9a187464d6b5e05"><td class="memItemLeft" align="right" valign="top"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga8db4cbb87314a989a9a187464d6b5e05">mi_heap_get_default</a> ()</td></tr>
+<tr class="memdesc:ga8db4cbb87314a989a9a187464d6b5e05"><td class="mdescLeft"> </td><td class="mdescRight">Get the default heap that is used for <a class="el" href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a" title="Allocate size bytes.">mi_malloc()</a> et al. <a href="#ga8db4cbb87314a989a9a187464d6b5e05">More...</a><br /></td></tr>
+<tr class="separator:ga8db4cbb87314a989a9a187464d6b5e05"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga5d03fbe062ffcf38f0f417fd968357fc"><td class="memItemLeft" align="right" valign="top"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga5d03fbe062ffcf38f0f417fd968357fc">mi_heap_get_backing</a> ()</td></tr>
+<tr class="memdesc:ga5d03fbe062ffcf38f0f417fd968357fc"><td class="mdescLeft"> </td><td class="mdescRight">Get the backing heap. <a href="#ga5d03fbe062ffcf38f0f417fd968357fc">More...</a><br /></td></tr>
+<tr class="separator:ga5d03fbe062ffcf38f0f417fd968357fc"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga7922f7495cde30b1984d0e6072419298"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga7922f7495cde30b1984d0e6072419298">mi_heap_collect</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, bool force)</td></tr>
+<tr class="memdesc:ga7922f7495cde30b1984d0e6072419298"><td class="mdescLeft"> </td><td class="mdescRight">Release outstanding resources in a specific heap. <a href="#ga7922f7495cde30b1984d0e6072419298">More...</a><br /></td></tr>
+<tr class="separator:ga7922f7495cde30b1984d0e6072419298"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga9cbed01e42c0647907295de92c3fa296"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga9cbed01e42c0647907295de92c3fa296">mi_heap_malloc</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, size_t size)</td></tr>
+<tr class="memdesc:ga9cbed01e42c0647907295de92c3fa296"><td class="mdescLeft"> </td><td class="mdescRight">Allocate in a specific heap. <a href="#ga9cbed01e42c0647907295de92c3fa296">More...</a><br /></td></tr>
+<tr class="separator:ga9cbed01e42c0647907295de92c3fa296"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaa1a1c7a1f4da6826b5a25b70ef878368"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#gaa1a1c7a1f4da6826b5a25b70ef878368">mi_heap_malloc_small</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, size_t size)</td></tr>
+<tr class="memdesc:gaa1a1c7a1f4da6826b5a25b70ef878368"><td class="mdescLeft"> </td><td class="mdescRight">Allocate a small object in a specific heap. <a href="#gaa1a1c7a1f4da6826b5a25b70ef878368">More...</a><br /></td></tr>
+<tr class="separator:gaa1a1c7a1f4da6826b5a25b70ef878368"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga903104592c8ed53417a3762da6241133"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga903104592c8ed53417a3762da6241133">mi_heap_zalloc</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, size_t size)</td></tr>
+<tr class="memdesc:ga903104592c8ed53417a3762da6241133"><td class="mdescLeft"> </td><td class="mdescRight">Allocate zero-initialized in a specific heap. <a href="#ga903104592c8ed53417a3762da6241133">More...</a><br /></td></tr>
+<tr class="separator:ga903104592c8ed53417a3762da6241133"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaa6702b3c48e9e53e50e81b36f5011d55"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#gaa6702b3c48e9e53e50e81b36f5011d55">mi_heap_calloc</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, size_t count, size_t size)</td></tr>
+<tr class="memdesc:gaa6702b3c48e9e53e50e81b36f5011d55"><td class="mdescLeft"> </td><td class="mdescRight">Allocate <em>count</em> zero-initialized elements in a specific heap. <a href="#gaa6702b3c48e9e53e50e81b36f5011d55">More...</a><br /></td></tr>
+<tr class="separator:gaa6702b3c48e9e53e50e81b36f5011d55"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga851da6c43fe0b71c1376cee8aef90db0"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga851da6c43fe0b71c1376cee8aef90db0">mi_heap_mallocn</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, size_t count, size_t size)</td></tr>
+<tr class="memdesc:ga851da6c43fe0b71c1376cee8aef90db0"><td class="mdescLeft"> </td><td class="mdescRight">Allocate <em>count</em> elements in a specific heap. <a href="#ga851da6c43fe0b71c1376cee8aef90db0">More...</a><br /></td></tr>
+<tr class="separator:ga851da6c43fe0b71c1376cee8aef90db0"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga139d6b09dbf50c3c2523d0f4d1cfdeb5"><td class="memItemLeft" align="right" valign="top">char * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga139d6b09dbf50c3c2523d0f4d1cfdeb5">mi_heap_strdup</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, const char *s)</td></tr>
+<tr class="memdesc:ga139d6b09dbf50c3c2523d0f4d1cfdeb5"><td class="mdescLeft"> </td><td class="mdescRight">Duplicate a string in a specific heap. <a href="#ga139d6b09dbf50c3c2523d0f4d1cfdeb5">More...</a><br /></td></tr>
+<tr class="separator:ga139d6b09dbf50c3c2523d0f4d1cfdeb5"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga8e3dbd46650dd26573cf307a2c8f1f5a"><td class="memItemLeft" align="right" valign="top">char * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga8e3dbd46650dd26573cf307a2c8f1f5a">mi_heap_strndup</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, const char *s, size_t n)</td></tr>
+<tr class="memdesc:ga8e3dbd46650dd26573cf307a2c8f1f5a"><td class="mdescLeft"> </td><td class="mdescRight">Duplicate a string of at most length <em>n</em> in a specific heap. <a href="#ga8e3dbd46650dd26573cf307a2c8f1f5a">More...</a><br /></td></tr>
+<tr class="separator:ga8e3dbd46650dd26573cf307a2c8f1f5a"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga00e95ba1e01acac3cfd95bb7a357a6f0"><td class="memItemLeft" align="right" valign="top">char * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga00e95ba1e01acac3cfd95bb7a357a6f0">mi_heap_realpath</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, const char *fname, char *resolved_name)</td></tr>
+<tr class="memdesc:ga00e95ba1e01acac3cfd95bb7a357a6f0"><td class="mdescLeft"> </td><td class="mdescRight">Resolve a file path name using a specific <em>heap</em> to allocate the result. <a href="#ga00e95ba1e01acac3cfd95bb7a357a6f0">More...</a><br /></td></tr>
+<tr class="separator:ga00e95ba1e01acac3cfd95bb7a357a6f0"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaaef3395f66be48f37bdc8322509c5d81"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#gaaef3395f66be48f37bdc8322509c5d81">mi_heap_realloc</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, void *p, size_t newsize)</td></tr>
+<tr class="separator:gaaef3395f66be48f37bdc8322509c5d81"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gac74e94ad9b0c9b57c1c4d88b8825b7a8"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#gac74e94ad9b0c9b57c1c4d88b8825b7a8">mi_heap_reallocn</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, void *p, size_t count, size_t size)</td></tr>
+<tr class="separator:gac74e94ad9b0c9b57c1c4d88b8825b7a8"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga4a21070eb4e7cce018133c8d5f4b0527"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga4a21070eb4e7cce018133c8d5f4b0527">mi_heap_reallocf</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, void *p, size_t newsize)</td></tr>
+<tr class="separator:ga4a21070eb4e7cce018133c8d5f4b0527"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gab5b87e1805306f70df38789fcfcf6653"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#gab5b87e1805306f70df38789fcfcf6653">mi_heap_malloc_aligned</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, size_t size, size_t alignment)</td></tr>
+<tr class="separator:gab5b87e1805306f70df38789fcfcf6653"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga23acd7680fb0976dde3783254c6c874b"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga23acd7680fb0976dde3783254c6c874b">mi_heap_malloc_aligned_at</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, size_t size, size_t alignment, size_t offset)</td></tr>
+<tr class="separator:ga23acd7680fb0976dde3783254c6c874b"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaa450a59c6c7ae5fdbd1c2b80a8329ef0"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#gaa450a59c6c7ae5fdbd1c2b80a8329ef0">mi_heap_zalloc_aligned</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, size_t size, size_t alignment)</td></tr>
+<tr class="separator:gaa450a59c6c7ae5fdbd1c2b80a8329ef0"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga45fb43a62776fbebbdf1edd99b527954"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga45fb43a62776fbebbdf1edd99b527954">mi_heap_zalloc_aligned_at</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, size_t size, size_t alignment, size_t offset)</td></tr>
+<tr class="separator:ga45fb43a62776fbebbdf1edd99b527954"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga4af03a6e2b93fae77424d93f889705c3"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga4af03a6e2b93fae77424d93f889705c3">mi_heap_calloc_aligned</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, size_t count, size_t size, size_t alignment)</td></tr>
+<tr class="separator:ga4af03a6e2b93fae77424d93f889705c3"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga08ca6419a5c057a4d965868998eef487"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#ga08ca6419a5c057a4d965868998eef487">mi_heap_calloc_aligned_at</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, size_t count, size_t size, size_t alignment, size_t offset)</td></tr>
+<tr class="separator:ga08ca6419a5c057a4d965868998eef487"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gafc603b696bd14cae6da28658f950d98c"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#gafc603b696bd14cae6da28658f950d98c">mi_heap_realloc_aligned</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, void *p, size_t newsize, size_t alignment)</td></tr>
+<tr class="separator:gafc603b696bd14cae6da28658f950d98c"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaf96c788a1bf553fe2d371de9365e047c"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__heap.html#gaf96c788a1bf553fe2d371de9365e047c">mi_heap_realloc_aligned_at</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, void *p, size_t newsize, size_t alignment, size_t offset)</td></tr>
+<tr class="separator:gaf96c788a1bf553fe2d371de9365e047c"><td class="memSeparator" colspan="2"> </td></tr>
+</table>
+<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
+<p>First-class heaps that can be destroyed in one go. </p>
+<h2 class="groupheader">Typedef Documentation</h2>
+<a id="ga34a47cde5a5b38c29f1aa3c5e76943c2"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga34a47cde5a5b38c29f1aa3c5e76943c2">◆ </a></span>mi_heap_t</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">typedef struct mi_heap_s <a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Type of first-class heaps. </p>
+<p>A heap can only be used for (re)allocation in the thread that created this heap! Any allocated blocks can be freed by any other thread though. </p>
+
+</div>
+</div>
+<h2 class="groupheader">Function Documentation</h2>
+<a id="gaa6702b3c48e9e53e50e81b36f5011d55"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaa6702b3c48e9e53e50e81b36f5011d55">◆ </a></span>mi_heap_calloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_calloc </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>count</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate <em>count</em> zero-initialized elements in a specific heap. </p>
+<dl class="section see"><dt>See also</dt><dd><a class="el" href="group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d" title="Allocate zero-initialized count elements of size bytes.">mi_calloc()</a> </dd></dl>
+
+</div>
+</div>
+<a id="ga4af03a6e2b93fae77424d93f889705c3"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga4af03a6e2b93fae77424d93f889705c3">◆ </a></span>mi_heap_calloc_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_calloc_aligned </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>count</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga08ca6419a5c057a4d965868998eef487"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga08ca6419a5c057a4d965868998eef487">◆ </a></span>mi_heap_calloc_aligned_at()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_calloc_aligned_at </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>count</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>offset</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga7922f7495cde30b1984d0e6072419298"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga7922f7495cde30b1984d0e6072419298">◆ </a></span>mi_heap_collect()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_heap_collect </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">bool </td>
+ <td class="paramname"><em>force</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Release outstanding resources in a specific heap. </p>
+
+</div>
+</div>
+<a id="ga2ab1af8d438819b55319c7ef51d1e409"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga2ab1af8d438819b55319c7ef51d1e409">◆ </a></span>mi_heap_delete()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_heap_delete </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Delete a previously allocated heap. </p>
+<p>This will release resources and migrate any still allocated blocks in this heap (efficienty) to the default heap.</p>
+<p>If <em>heap</em> is the default heap, the default heap is set to the backing heap. </p>
+
+</div>
+</div>
+<a id="ga9f9c0844edb9717f4feacd79116b8e0d"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga9f9c0844edb9717f4feacd79116b8e0d">◆ </a></span>mi_heap_destroy()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_heap_destroy </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Destroy a heap, freeing all its still allocated blocks. </p>
+<p>Use with care as this will free all blocks still allocated in the heap. However, this can be a very efficient way to free all heap memory in one go.</p>
+<p>If <em>heap</em> is the default heap, the default heap is set to the backing heap. </p>
+
+</div>
+</div>
+<a id="ga5d03fbe062ffcf38f0f417fd968357fc"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga5d03fbe062ffcf38f0f417fd968357fc">◆ </a></span>mi_heap_get_backing()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* mi_heap_get_backing </td>
+ <td>(</td>
+ <td class="paramname"></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Get the backing heap. </p>
+<p>The <em>backing</em> heap is the initial default heap for a thread and always available for allocations. It cannot be destroyed or deleted except by exiting the thread. </p>
+
+</div>
+</div>
+<a id="ga8db4cbb87314a989a9a187464d6b5e05"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga8db4cbb87314a989a9a187464d6b5e05">◆ </a></span>mi_heap_get_default()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* mi_heap_get_default </td>
+ <td>(</td>
+ <td class="paramname"></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Get the default heap that is used for <a class="el" href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a" title="Allocate size bytes.">mi_malloc()</a> et al. </p>
+<dl class="section return"><dt>Returns</dt><dd>The current default heap. </dd></dl>
+
+</div>
+</div>
+<a id="ga9cbed01e42c0647907295de92c3fa296"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga9cbed01e42c0647907295de92c3fa296">◆ </a></span>mi_heap_malloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_malloc </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate in a specific heap. </p>
+<dl class="section see"><dt>See also</dt><dd><a class="el" href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a" title="Allocate size bytes.">mi_malloc()</a> </dd></dl>
+
+</div>
+</div>
+<a id="gab5b87e1805306f70df38789fcfcf6653"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gab5b87e1805306f70df38789fcfcf6653">◆ </a></span>mi_heap_malloc_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_malloc_aligned </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga23acd7680fb0976dde3783254c6c874b"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga23acd7680fb0976dde3783254c6c874b">◆ </a></span>mi_heap_malloc_aligned_at()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_malloc_aligned_at </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>offset</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gaa1a1c7a1f4da6826b5a25b70ef878368"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaa1a1c7a1f4da6826b5a25b70ef878368">◆ </a></span>mi_heap_malloc_small()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_malloc_small </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate a small object in a specific heap. </p>
+<p><em>size</em> must be smaller or equal to <a class="el" href="group__extended.html#ga1ea64283508718d9d645c38efc2f4305" title="Maximum size allowed for small allocations in mi_malloc_small and mi_zalloc_small (usually 128*sizeof...">MI_SMALL_SIZE_MAX()</a>. </p><dl class="section see"><dt>See also</dt><dd><a class="el" href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a" title="Allocate size bytes.">mi_malloc()</a> </dd></dl>
+
+</div>
+</div>
+<a id="ga851da6c43fe0b71c1376cee8aef90db0"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga851da6c43fe0b71c1376cee8aef90db0">◆ </a></span>mi_heap_mallocn()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_mallocn </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>count</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate <em>count</em> elements in a specific heap. </p>
+<dl class="section see"><dt>See also</dt><dd><a class="el" href="group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6" title="Allocate count elements of size bytes.">mi_mallocn()</a> </dd></dl>
+
+</div>
+</div>
+<a id="ga766f672ba56f2fbfeb9d9dbb0b7f6b11"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga766f672ba56f2fbfeb9d9dbb0b7f6b11">◆ </a></span>mi_heap_new()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* mi_heap_new </td>
+ <td>(</td>
+ <td class="paramname"></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Create a new heap that can be used for allocation. </p>
+
+</div>
+</div>
+<a id="gaaef3395f66be48f37bdc8322509c5d81"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaaef3395f66be48f37bdc8322509c5d81">◆ </a></span>mi_heap_realloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_realloc </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gafc603b696bd14cae6da28658f950d98c"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gafc603b696bd14cae6da28658f950d98c">◆ </a></span>mi_heap_realloc_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_realloc_aligned </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gaf96c788a1bf553fe2d371de9365e047c"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaf96c788a1bf553fe2d371de9365e047c">◆ </a></span>mi_heap_realloc_aligned_at()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_realloc_aligned_at </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>offset</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga4a21070eb4e7cce018133c8d5f4b0527"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga4a21070eb4e7cce018133c8d5f4b0527">◆ </a></span>mi_heap_reallocf()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_reallocf </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gac74e94ad9b0c9b57c1c4d88b8825b7a8"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gac74e94ad9b0c9b57c1c4d88b8825b7a8">◆ </a></span>mi_heap_reallocn()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_reallocn </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>count</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga00e95ba1e01acac3cfd95bb7a357a6f0"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga00e95ba1e01acac3cfd95bb7a357a6f0">◆ </a></span>mi_heap_realpath()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">char* mi_heap_realpath </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">const char * </td>
+ <td class="paramname"><em>fname</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">char * </td>
+ <td class="paramname"><em>resolved_name</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Resolve a file path name using a specific <em>heap</em> to allocate the result. </p>
+<dl class="section see"><dt>See also</dt><dd><a class="el" href="group__malloc.html#ga08cec32dd5bbe7da91c78d19f1b5bebe" title="Resolve a file path name.">mi_realpath()</a> </dd></dl>
+
+</div>
+</div>
+<a id="gab8631ec88c8d26641b68b5d25dcd4422"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gab8631ec88c8d26641b68b5d25dcd4422">◆ </a></span>mi_heap_set_default()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* mi_heap_set_default </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Set the default heap to use for <a class="el" href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a" title="Allocate size bytes.">mi_malloc()</a> et al. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">heap</td><td>The new default heap. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>The previous default heap. </dd></dl>
+
+</div>
+</div>
+<a id="ga139d6b09dbf50c3c2523d0f4d1cfdeb5"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga139d6b09dbf50c3c2523d0f4d1cfdeb5">◆ </a></span>mi_heap_strdup()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">char* mi_heap_strdup </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">const char * </td>
+ <td class="paramname"><em>s</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Duplicate a string in a specific heap. </p>
+<dl class="section see"><dt>See also</dt><dd><a class="el" href="group__malloc.html#gac7cffe13f1f458ed16789488bf92b9b2" title="Allocate and duplicate a string.">mi_strdup()</a> </dd></dl>
+
+</div>
+</div>
+<a id="ga8e3dbd46650dd26573cf307a2c8f1f5a"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga8e3dbd46650dd26573cf307a2c8f1f5a">◆ </a></span>mi_heap_strndup()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">char* mi_heap_strndup </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">const char * </td>
+ <td class="paramname"><em>s</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>n</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Duplicate a string of at most length <em>n</em> in a specific heap. </p>
+<dl class="section see"><dt>See also</dt><dd><a class="el" href="group__malloc.html#gaaabf971c2571891433477e2d21a35266" title="Allocate and duplicate a string up to n bytes.">mi_strndup()</a> </dd></dl>
+
+</div>
+</div>
+<a id="ga903104592c8ed53417a3762da6241133"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga903104592c8ed53417a3762da6241133">◆ </a></span>mi_heap_zalloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_zalloc </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate zero-initialized in a specific heap. </p>
+<dl class="section see"><dt>See also</dt><dd><a class="el" href="group__malloc.html#gafdd9d8bb2986e668ba9884f28af38000" title="Allocate zero-initialized size bytes.">mi_zalloc()</a> </dd></dl>
+
+</div>
+</div>
+<a id="gaa450a59c6c7ae5fdbd1c2b80a8329ef0"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaa450a59c6c7ae5fdbd1c2b80a8329ef0">◆ </a></span>mi_heap_zalloc_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_zalloc_aligned </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga45fb43a62776fbebbdf1edd99b527954"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga45fb43a62776fbebbdf1edd99b527954">◆ </a></span>mi_heap_zalloc_aligned_at()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_zalloc_aligned_at </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>offset</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+var group__heap =
+[
+ [ "mi_heap_t", "group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2", null ],
+ [ "mi_heap_calloc", "group__heap.html#gaa6702b3c48e9e53e50e81b36f5011d55", null ],
+ [ "mi_heap_calloc_aligned", "group__heap.html#ga4af03a6e2b93fae77424d93f889705c3", null ],
+ [ "mi_heap_calloc_aligned_at", "group__heap.html#ga08ca6419a5c057a4d965868998eef487", null ],
+ [ "mi_heap_collect", "group__heap.html#ga7922f7495cde30b1984d0e6072419298", null ],
+ [ "mi_heap_delete", "group__heap.html#ga2ab1af8d438819b55319c7ef51d1e409", null ],
+ [ "mi_heap_destroy", "group__heap.html#ga9f9c0844edb9717f4feacd79116b8e0d", null ],
+ [ "mi_heap_get_backing", "group__heap.html#ga5d03fbe062ffcf38f0f417fd968357fc", null ],
+ [ "mi_heap_get_default", "group__heap.html#ga8db4cbb87314a989a9a187464d6b5e05", null ],
+ [ "mi_heap_malloc", "group__heap.html#ga9cbed01e42c0647907295de92c3fa296", null ],
+ [ "mi_heap_malloc_aligned", "group__heap.html#gab5b87e1805306f70df38789fcfcf6653", null ],
+ [ "mi_heap_malloc_aligned_at", "group__heap.html#ga23acd7680fb0976dde3783254c6c874b", null ],
+ [ "mi_heap_malloc_small", "group__heap.html#gaa1a1c7a1f4da6826b5a25b70ef878368", null ],
+ [ "mi_heap_mallocn", "group__heap.html#ga851da6c43fe0b71c1376cee8aef90db0", null ],
+ [ "mi_heap_new", "group__heap.html#ga766f672ba56f2fbfeb9d9dbb0b7f6b11", null ],
+ [ "mi_heap_realloc", "group__heap.html#gaaef3395f66be48f37bdc8322509c5d81", null ],
+ [ "mi_heap_realloc_aligned", "group__heap.html#gafc603b696bd14cae6da28658f950d98c", null ],
+ [ "mi_heap_realloc_aligned_at", "group__heap.html#gaf96c788a1bf553fe2d371de9365e047c", null ],
+ [ "mi_heap_reallocf", "group__heap.html#ga4a21070eb4e7cce018133c8d5f4b0527", null ],
+ [ "mi_heap_reallocn", "group__heap.html#gac74e94ad9b0c9b57c1c4d88b8825b7a8", null ],
+ [ "mi_heap_realpath", "group__heap.html#ga00e95ba1e01acac3cfd95bb7a357a6f0", null ],
+ [ "mi_heap_set_default", "group__heap.html#gab8631ec88c8d26641b68b5d25dcd4422", null ],
+ [ "mi_heap_strdup", "group__heap.html#ga139d6b09dbf50c3c2523d0f4d1cfdeb5", null ],
+ [ "mi_heap_strndup", "group__heap.html#ga8e3dbd46650dd26573cf307a2c8f1f5a", null ],
+ [ "mi_heap_zalloc", "group__heap.html#ga903104592c8ed53417a3762da6241133", null ],
+ [ "mi_heap_zalloc_aligned", "group__heap.html#gaa450a59c6c7ae5fdbd1c2b80a8329ef0", null ],
+ [ "mi_heap_zalloc_aligned_at", "group__heap.html#ga45fb43a62776fbebbdf1edd99b527954", null ]
+];
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Basic Allocation</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('group__malloc.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="summary">
+<a href="#func-members">Functions</a> </div>
+ <div class="headertitle">
+<div class="title">Basic Allocation</div> </div>
+</div><!--header-->
+<div class="contents">
+
+<p>The basic allocation interface.
+<a href="#details">More...</a></p>
+<table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="func-members"></a>
+Functions</h2></td></tr>
+<tr class="memitem:gaf2c7b89c327d1f60f59e68b9ea644d95"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95">mi_free</a> (void *p)</td></tr>
+<tr class="memdesc:gaf2c7b89c327d1f60f59e68b9ea644d95"><td class="mdescLeft"> </td><td class="mdescRight">Free previously allocated memory. <a href="#gaf2c7b89c327d1f60f59e68b9ea644d95">More...</a><br /></td></tr>
+<tr class="separator:gaf2c7b89c327d1f60f59e68b9ea644d95"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga3406e8b168bc74c8637b11571a6da83a"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a">mi_malloc</a> (size_t size)</td></tr>
+<tr class="memdesc:ga3406e8b168bc74c8637b11571a6da83a"><td class="mdescLeft"> </td><td class="mdescRight">Allocate <em>size</em> bytes. <a href="#ga3406e8b168bc74c8637b11571a6da83a">More...</a><br /></td></tr>
+<tr class="separator:ga3406e8b168bc74c8637b11571a6da83a"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gafdd9d8bb2986e668ba9884f28af38000"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__malloc.html#gafdd9d8bb2986e668ba9884f28af38000">mi_zalloc</a> (size_t size)</td></tr>
+<tr class="memdesc:gafdd9d8bb2986e668ba9884f28af38000"><td class="mdescLeft"> </td><td class="mdescRight">Allocate zero-initialized <code>size</code> bytes. <a href="#gafdd9d8bb2986e668ba9884f28af38000">More...</a><br /></td></tr>
+<tr class="separator:gafdd9d8bb2986e668ba9884f28af38000"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga97fedb4f7107c592fd7f0f0a8949a57d"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d">mi_calloc</a> (size_t count, size_t size)</td></tr>
+<tr class="memdesc:ga97fedb4f7107c592fd7f0f0a8949a57d"><td class="mdescLeft"> </td><td class="mdescRight">Allocate zero-initialized <em>count</em> elements of <em>size</em> bytes. <a href="#ga97fedb4f7107c592fd7f0f0a8949a57d">More...</a><br /></td></tr>
+<tr class="separator:ga97fedb4f7107c592fd7f0f0a8949a57d"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaf11eb497da57bdfb2de65eb191c69db6"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6">mi_realloc</a> (void *p, size_t newsize)</td></tr>
+<tr class="memdesc:gaf11eb497da57bdfb2de65eb191c69db6"><td class="mdescLeft"> </td><td class="mdescRight">Re-allocate memory to <em>newsize</em> bytes. <a href="#gaf11eb497da57bdfb2de65eb191c69db6">More...</a><br /></td></tr>
+<tr class="separator:gaf11eb497da57bdfb2de65eb191c69db6"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga23a0fbb452b5dce8e31fab1a1958cacc"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__malloc.html#ga23a0fbb452b5dce8e31fab1a1958cacc">mi_recalloc</a> (void *p, size_t count, size_t size)</td></tr>
+<tr class="memdesc:ga23a0fbb452b5dce8e31fab1a1958cacc"><td class="mdescLeft"> </td><td class="mdescRight">Re-allocate memory to <em>count</em> elements of <em>size</em> bytes, with extra memory initialized to zero. <a href="#ga23a0fbb452b5dce8e31fab1a1958cacc">More...</a><br /></td></tr>
+<tr class="separator:ga23a0fbb452b5dce8e31fab1a1958cacc"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaaee66a1d483c3e28f585525fb96707e4"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__malloc.html#gaaee66a1d483c3e28f585525fb96707e4">mi_expand</a> (void *p, size_t newsize)</td></tr>
+<tr class="memdesc:gaaee66a1d483c3e28f585525fb96707e4"><td class="mdescLeft"> </td><td class="mdescRight">Try to re-allocate memory to <em>newsize</em> bytes <em>in place</em>. <a href="#gaaee66a1d483c3e28f585525fb96707e4">More...</a><br /></td></tr>
+<tr class="separator:gaaee66a1d483c3e28f585525fb96707e4"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga0b05e2bf0f73e7401ae08597ff782ac6"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6">mi_mallocn</a> (size_t count, size_t size)</td></tr>
+<tr class="memdesc:ga0b05e2bf0f73e7401ae08597ff782ac6"><td class="mdescLeft"> </td><td class="mdescRight">Allocate <em>count</em> elements of <em>size</em> bytes. <a href="#ga0b05e2bf0f73e7401ae08597ff782ac6">More...</a><br /></td></tr>
+<tr class="separator:ga0b05e2bf0f73e7401ae08597ff782ac6"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga61d57b4144ba24fba5c1e9b956d13853"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853">mi_reallocn</a> (void *p, size_t count, size_t size)</td></tr>
+<tr class="memdesc:ga61d57b4144ba24fba5c1e9b956d13853"><td class="mdescLeft"> </td><td class="mdescRight">Re-allocate memory to <em>count</em> elements of <em>size</em> bytes. <a href="#ga61d57b4144ba24fba5c1e9b956d13853">More...</a><br /></td></tr>
+<tr class="separator:ga61d57b4144ba24fba5c1e9b956d13853"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gafe68ac7c5e24a65cd55c9d6b152211a0"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__malloc.html#gafe68ac7c5e24a65cd55c9d6b152211a0">mi_reallocf</a> (void *p, size_t newsize)</td></tr>
+<tr class="memdesc:gafe68ac7c5e24a65cd55c9d6b152211a0"><td class="mdescLeft"> </td><td class="mdescRight">Re-allocate memory to <em>newsize</em> bytes,. <a href="#gafe68ac7c5e24a65cd55c9d6b152211a0">More...</a><br /></td></tr>
+<tr class="separator:gafe68ac7c5e24a65cd55c9d6b152211a0"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gac7cffe13f1f458ed16789488bf92b9b2"><td class="memItemLeft" align="right" valign="top">char * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__malloc.html#gac7cffe13f1f458ed16789488bf92b9b2">mi_strdup</a> (const char *s)</td></tr>
+<tr class="memdesc:gac7cffe13f1f458ed16789488bf92b9b2"><td class="mdescLeft"> </td><td class="mdescRight">Allocate and duplicate a string. <a href="#gac7cffe13f1f458ed16789488bf92b9b2">More...</a><br /></td></tr>
+<tr class="separator:gac7cffe13f1f458ed16789488bf92b9b2"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaaabf971c2571891433477e2d21a35266"><td class="memItemLeft" align="right" valign="top">char * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__malloc.html#gaaabf971c2571891433477e2d21a35266">mi_strndup</a> (const char *s, size_t n)</td></tr>
+<tr class="memdesc:gaaabf971c2571891433477e2d21a35266"><td class="mdescLeft"> </td><td class="mdescRight">Allocate and duplicate a string up to <em>n</em> bytes. <a href="#gaaabf971c2571891433477e2d21a35266">More...</a><br /></td></tr>
+<tr class="separator:gaaabf971c2571891433477e2d21a35266"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga08cec32dd5bbe7da91c78d19f1b5bebe"><td class="memItemLeft" align="right" valign="top">char * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__malloc.html#ga08cec32dd5bbe7da91c78d19f1b5bebe">mi_realpath</a> (const char *fname, char *resolved_name)</td></tr>
+<tr class="memdesc:ga08cec32dd5bbe7da91c78d19f1b5bebe"><td class="mdescLeft"> </td><td class="mdescRight">Resolve a file path name. <a href="#ga08cec32dd5bbe7da91c78d19f1b5bebe">More...</a><br /></td></tr>
+<tr class="separator:ga08cec32dd5bbe7da91c78d19f1b5bebe"><td class="memSeparator" colspan="2"> </td></tr>
+</table>
+<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
+<p>The basic allocation interface. </p>
+<h2 class="groupheader">Function Documentation</h2>
+<a id="ga97fedb4f7107c592fd7f0f0a8949a57d"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga97fedb4f7107c592fd7f0f0a8949a57d">◆ </a></span>mi_calloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_calloc </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>count</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate zero-initialized <em>count</em> elements of <em>size</em> bytes. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">count</td><td>number of elements. </td></tr>
+ <tr><td class="paramname">size</td><td>size of each element. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>pointer to the allocated memory of <em>size*<em>count</em> bytes</em>, or <em>NULL</em> if either out of memory or when <code>count*size</code> overflows.</dd></dl>
+<p>Returns a unique pointer if called with either <em>size</em> or <em>count</em> of 0. </p><dl class="section see"><dt>See also</dt><dd><a class="el" href="group__malloc.html#gafdd9d8bb2986e668ba9884f28af38000" title="Allocate zero-initialized size bytes.">mi_zalloc()</a> </dd></dl>
+
+</div>
+</div>
+<a id="gaaee66a1d483c3e28f585525fb96707e4"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaaee66a1d483c3e28f585525fb96707e4">◆ </a></span>mi_expand()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_expand </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Try to re-allocate memory to <em>newsize</em> bytes <em>in place</em>. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">p</td><td>pointer to previously allocated memory (or <em>NULL</em>). </td></tr>
+ <tr><td class="paramname">newsize</td><td>the new required size in bytes. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>pointer to the re-allocated memory of <em>newsize</em> bytes (always equal to <em>p</em>), or <em>NULL</em> if either out of memory or if the memory could not be expanded in place. If <em>NULL</em> is returned, the pointer <em>p</em> is not freed. Otherwise the original pointer is returned as the reallocated result since it fits in-place with the new size. If <em>newsize</em> is larger than the original <em>size</em> allocated for <em>p</em>, the bytes after <em>size</em> are uninitialized. </dd></dl>
+
+</div>
+</div>
+<a id="gaf2c7b89c327d1f60f59e68b9ea644d95"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaf2c7b89c327d1f60f59e68b9ea644d95">◆ </a></span>mi_free()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_free </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Free previously allocated memory. </p>
+<p>The pointer <code>p</code> must have been allocated before (or be <em>NULL</em>). </p><dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">p</td><td>pointer to free, or <em>NULL</em>. </td></tr>
+ </table>
+ </dd>
+</dl>
+
+</div>
+</div>
+<a id="ga3406e8b168bc74c8637b11571a6da83a"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga3406e8b168bc74c8637b11571a6da83a">◆ </a></span>mi_malloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_malloc </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate <em>size</em> bytes. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">size</td><td>number of bytes to allocate. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>pointer to the allocated memory or <em>NULL</em> if out of memory. Returns a unique pointer if called with <em>size</em> 0. </dd></dl>
+
+</div>
+</div>
+<a id="ga0b05e2bf0f73e7401ae08597ff782ac6"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga0b05e2bf0f73e7401ae08597ff782ac6">◆ </a></span>mi_mallocn()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_mallocn </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>count</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate <em>count</em> elements of <em>size</em> bytes. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">count</td><td>The number of elements. </td></tr>
+ <tr><td class="paramname">size</td><td>The size of each element. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>A pointer to a block of <em>count</em> * <em>size</em> bytes, or <em>NULL</em> if out of memory or if <em>count</em> * <em>size</em> overflows.</dd></dl>
+<p>If there is no overflow, it behaves exactly like <code>mi_malloc(p,count*size)</code>. </p><dl class="section see"><dt>See also</dt><dd><a class="el" href="group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d" title="Allocate zero-initialized count elements of size bytes.">mi_calloc()</a> </dd>
+<dd>
+mi_zallocn() </dd></dl>
+
+</div>
+</div>
+<a id="gaf11eb497da57bdfb2de65eb191c69db6"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaf11eb497da57bdfb2de65eb191c69db6">◆ </a></span>mi_realloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_realloc </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Re-allocate memory to <em>newsize</em> bytes. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">p</td><td>pointer to previously allocated memory (or <em>NULL</em>). </td></tr>
+ <tr><td class="paramname">newsize</td><td>the new required size in bytes. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>pointer to the re-allocated memory of <em>newsize</em> bytes, or <em>NULL</em> if out of memory. If <em>NULL</em> is returned, the pointer <em>p</em> is not freed. Otherwise the original pointer is either freed or returned as the reallocated result (in case it fits in-place with the new size). If the pointer <em>p</em> is <em>NULL</em>, it behaves as <em>mi_malloc</em>(<em>newsize</em>). If <em>newsize</em> is larger than the original <em>size</em> allocated for <em>p</em>, the bytes after <em>size</em> are uninitialized. </dd></dl>
+
+</div>
+</div>
+<a id="gafe68ac7c5e24a65cd55c9d6b152211a0"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gafe68ac7c5e24a65cd55c9d6b152211a0">◆ </a></span>mi_reallocf()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_reallocf </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Re-allocate memory to <em>newsize</em> bytes,. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">p</td><td>pointer to previously allocated memory (or <em>NULL</em>). </td></tr>
+ <tr><td class="paramname">newsize</td><td>the new required size in bytes. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>pointer to the re-allocated memory of <em>newsize</em> bytes, or <em>NULL</em> if out of memory.</dd></dl>
+<p>In contrast to <a class="el" href="group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6" title="Re-allocate memory to newsize bytes.">mi_realloc()</a>, if <em>NULL</em> is returned, the original pointer <em>p</em> is freed (if it was not <em>NULL</em> itself). Otherwise the original pointer is either freed or returned as the reallocated result (in case it fits in-place with the new size). If the pointer <em>p</em> is <em>NULL</em>, it behaves as <em>mi_malloc</em>(<em>newsize</em>). If <em>newsize</em> is larger than the original <em>size</em> allocated for <em>p</em>, the bytes after <em>size</em> are uninitialized.</p>
+<dl class="section see"><dt>See also</dt><dd><a href="https://www.freebsd.org/cgi/man.cgi?query=reallocf">reallocf</a> (on BSD) </dd></dl>
+
+</div>
+</div>
+<a id="ga61d57b4144ba24fba5c1e9b956d13853"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga61d57b4144ba24fba5c1e9b956d13853">◆ </a></span>mi_reallocn()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_reallocn </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>count</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Re-allocate memory to <em>count</em> elements of <em>size</em> bytes. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">p</td><td>Pointer to a previously allocated block (or <em>NULL</em>). </td></tr>
+ <tr><td class="paramname">count</td><td>The number of elements. </td></tr>
+ <tr><td class="paramname">size</td><td>The size of each element. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>A pointer to a re-allocated block of <em>count</em> * <em>size</em> bytes, or <em>NULL</em> if out of memory or if <em>count</em> * <em>size</em> overflows.</dd></dl>
+<p>If there is no overflow, it behaves exactly like <code>mi_realloc(p,count*size)</code>. </p><dl class="section see"><dt>See also</dt><dd><a href="http://man.openbsd.org/reallocarray">reallocarray()</a> (on BSD) </dd></dl>
+
+</div>
+</div>
+<a id="ga08cec32dd5bbe7da91c78d19f1b5bebe"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga08cec32dd5bbe7da91c78d19f1b5bebe">◆ </a></span>mi_realpath()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">char* mi_realpath </td>
+ <td>(</td>
+ <td class="paramtype">const char * </td>
+ <td class="paramname"><em>fname</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">char * </td>
+ <td class="paramname"><em>resolved_name</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Resolve a file path name. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">fname</td><td>File name. </td></tr>
+ <tr><td class="paramname">resolved_name</td><td>Should be <em>NULL</em> (but can also point to a buffer of at least <em>PATH_MAX</em> bytes). </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>If successful a pointer to the resolved absolute file name, or <em>NULL</em> on failure (with <em>errno</em> set to the error code).</dd></dl>
+<p>If <em>resolved_name</em> was <em>NULL</em>, the returned result should be freed with <a class="el" href="group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95" title="Free previously allocated memory.">mi_free()</a>.</p>
+<p>Replacement for the standard <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html">realpath()</a> such that <a class="el" href="group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95" title="Free previously allocated memory.">mi_free()</a> can be used on the returned result (if <em>resolved_name</em> was <em>NULL</em>). </p>
+
+</div>
+</div>
+<a id="ga23a0fbb452b5dce8e31fab1a1958cacc"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga23a0fbb452b5dce8e31fab1a1958cacc">◆ </a></span>mi_recalloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void * mi_recalloc </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>count</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Re-allocate memory to <em>count</em> elements of <em>size</em> bytes, with extra memory initialized to zero. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">p</td><td>Pointer to a previously allocated block (or <em>NULL</em>). </td></tr>
+ <tr><td class="paramname">count</td><td>The number of elements. </td></tr>
+ <tr><td class="paramname">size</td><td>The size of each element. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>A pointer to a re-allocated block of <em>count</em> * <em>size</em> bytes, or <em>NULL</em> if out of memory or if <em>count</em> * <em>size</em> overflows.</dd></dl>
+<p>If there is no overflow, it behaves exactly like <code>mi_rezalloc(p,count*size)</code>. </p><dl class="section see"><dt>See also</dt><dd><a class="el" href="group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853" title="Re-allocate memory to count elements of size bytes.">mi_reallocn()</a> </dd>
+<dd>
+<a href="http://man.openbsd.org/reallocarray">recallocarray()</a> (on BSD). </dd></dl>
+
+</div>
+</div>
+<a id="gac7cffe13f1f458ed16789488bf92b9b2"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gac7cffe13f1f458ed16789488bf92b9b2">◆ </a></span>mi_strdup()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">char* mi_strdup </td>
+ <td>(</td>
+ <td class="paramtype">const char * </td>
+ <td class="paramname"><em>s</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate and duplicate a string. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">s</td><td>string to duplicate (or <em>NULL</em>). </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>a pointer to newly allocated memory initialized to string <em>s</em>, or <em>NULL</em> if either out of memory or if <em>s</em> is <em>NULL</em>.</dd></dl>
+<p>Replacement for the standard <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/strdup.html">strdup()</a> such that <a class="el" href="group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95" title="Free previously allocated memory.">mi_free()</a> can be used on the returned result. </p>
+
+</div>
+</div>
+<a id="gaaabf971c2571891433477e2d21a35266"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaaabf971c2571891433477e2d21a35266">◆ </a></span>mi_strndup()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">char* mi_strndup </td>
+ <td>(</td>
+ <td class="paramtype">const char * </td>
+ <td class="paramname"><em>s</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>n</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate and duplicate a string up to <em>n</em> bytes. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">s</td><td>string to duplicate (or <em>NULL</em>). </td></tr>
+ <tr><td class="paramname">n</td><td>maximum number of bytes to copy (excluding the terminating zero). </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>a pointer to newly allocated memory initialized to string <em>s</em> up to the first <em>n</em> bytes (and always zero terminated), or <em>NULL</em> if either out of memory or if <em>s</em> is <em>NULL</em>.</dd></dl>
+<p>Replacement for the standard <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/strndup.html">strndup()</a> such that <a class="el" href="group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95" title="Free previously allocated memory.">mi_free()</a> can be used on the returned result. </p>
+
+</div>
+</div>
+<a id="gafdd9d8bb2986e668ba9884f28af38000"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gafdd9d8bb2986e668ba9884f28af38000">◆ </a></span>mi_zalloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_zalloc </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate zero-initialized <code>size</code> bytes. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">size</td><td>The size in bytes. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>Pointer to newly allocated zero initialized memory, or <em>NULL</em> if out of memory. </dd></dl>
+
+</div>
+</div>
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+var group__malloc =
+[
+ [ "mi_calloc", "group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d", null ],
+ [ "mi_expand", "group__malloc.html#gaaee66a1d483c3e28f585525fb96707e4", null ],
+ [ "mi_free", "group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95", null ],
+ [ "mi_malloc", "group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a", null ],
+ [ "mi_mallocn", "group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6", null ],
+ [ "mi_realloc", "group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6", null ],
+ [ "mi_reallocf", "group__malloc.html#gafe68ac7c5e24a65cd55c9d6b152211a0", null ],
+ [ "mi_reallocn", "group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853", null ],
+ [ "mi_realpath", "group__malloc.html#ga08cec32dd5bbe7da91c78d19f1b5bebe", null ],
+ [ "mi_recalloc", "group__malloc.html#ga23a0fbb452b5dce8e31fab1a1958cacc", null ],
+ [ "mi_strdup", "group__malloc.html#gac7cffe13f1f458ed16789488bf92b9b2", null ],
+ [ "mi_strndup", "group__malloc.html#gaaabf971c2571891433477e2d21a35266", null ],
+ [ "mi_zalloc", "group__malloc.html#gafdd9d8bb2986e668ba9884f28af38000", null ]
+];
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Runtime Options</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('group__options.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="summary">
+<a href="#enum-members">Enumerations</a> |
+<a href="#func-members">Functions</a> </div>
+ <div class="headertitle">
+<div class="title">Runtime Options</div> </div>
+</div><!--header-->
+<div class="contents">
+
+<p>Set runtime behavior.
+<a href="#details">More...</a></p>
+<table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="enum-members"></a>
+Enumerations</h2></td></tr>
+<tr class="memitem:gafebf7ed116adb38ae5218bc3ce06884c"><td class="memItemLeft" align="right" valign="top">enum  </td><td class="memItemRight" valign="bottom"><a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> { <br />
+  <a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0">mi_option_show_errors</a>,
+<a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda">mi_option_show_stats</a>,
+<a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777">mi_option_verbose</a>,
+<a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b">mi_option_eager_commit</a>,
+<br />
+  <a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad">mi_option_eager_region_commit</a>,
+<a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e">mi_option_large_os_pages</a>,
+<a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2">mi_option_reserve_huge_os_pages</a>,
+<a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1">mi_option_segment_cache</a>,
+<br />
+  <a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968">mi_option_page_reset</a>,
+<a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d">mi_option_segment_reset</a>,
+<a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5">mi_option_reset_delay</a>,
+<a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74">mi_option_use_numa_nodes</a>,
+<br />
+  <a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536">mi_option_reset_decommits</a>,
+<a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c">mi_option_eager_commit_delay</a>,
+<a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf">mi_option_os_tag</a>,
+<a class="el" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a">_mi_option_last</a>
+<br />
+ }</td></tr>
+<tr class="memdesc:gafebf7ed116adb38ae5218bc3ce06884c"><td class="mdescLeft"> </td><td class="mdescRight">Runtime options. <a href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">More...</a><br /></td></tr>
+<tr class="separator:gafebf7ed116adb38ae5218bc3ce06884c"><td class="memSeparator" colspan="2"> </td></tr>
+</table><table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="func-members"></a>
+Functions</h2></td></tr>
+<tr class="memitem:ga459ad98f18b3fc9275474807fe0ca188"><td class="memItemLeft" align="right" valign="top">bool </td><td class="memItemRight" valign="bottom"><a class="el" href="group__options.html#ga459ad98f18b3fc9275474807fe0ca188">mi_option_is_enabled</a> (<a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option)</td></tr>
+<tr class="separator:ga459ad98f18b3fc9275474807fe0ca188"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga04180ae41b0d601421dd62ced40ca050"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__options.html#ga04180ae41b0d601421dd62ced40ca050">mi_option_enable</a> (<a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option)</td></tr>
+<tr class="separator:ga04180ae41b0d601421dd62ced40ca050"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaebf6ff707a2e688ebb1a2296ca564054"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__options.html#gaebf6ff707a2e688ebb1a2296ca564054">mi_option_disable</a> (<a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option)</td></tr>
+<tr class="separator:gaebf6ff707a2e688ebb1a2296ca564054"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga9a13d05fcb77489cb06d4d017ebd8bed"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__options.html#ga9a13d05fcb77489cb06d4d017ebd8bed">mi_option_set_enabled</a> (<a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option, bool enable)</td></tr>
+<tr class="separator:ga9a13d05fcb77489cb06d4d017ebd8bed"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga65518b69ec5d32336b50e07f74b3f629"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__options.html#ga65518b69ec5d32336b50e07f74b3f629">mi_option_set_enabled_default</a> (<a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option, bool enable)</td></tr>
+<tr class="separator:ga65518b69ec5d32336b50e07f74b3f629"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga7e8af195cc81d3fa64ccf2662caa565a"><td class="memItemLeft" align="right" valign="top">long </td><td class="memItemRight" valign="bottom"><a class="el" href="group__options.html#ga7e8af195cc81d3fa64ccf2662caa565a">mi_option_get</a> (<a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option)</td></tr>
+<tr class="separator:ga7e8af195cc81d3fa64ccf2662caa565a"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaf84921c32375e25754dc2ee6a911fa60"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__options.html#gaf84921c32375e25754dc2ee6a911fa60">mi_option_set</a> (<a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option, long value)</td></tr>
+<tr class="separator:gaf84921c32375e25754dc2ee6a911fa60"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga7ef623e440e6e5545cb08c94e71e4b90"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__options.html#ga7ef623e440e6e5545cb08c94e71e4b90">mi_option_set_default</a> (<a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option, long value)</td></tr>
+<tr class="separator:ga7ef623e440e6e5545cb08c94e71e4b90"><td class="memSeparator" colspan="2"> </td></tr>
+</table>
+<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
+<p>Set runtime behavior. </p>
+<h2 class="groupheader">Enumeration Type Documentation</h2>
+<a id="gafebf7ed116adb38ae5218bc3ce06884c"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gafebf7ed116adb38ae5218bc3ce06884c">◆ </a></span>mi_option_t</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">enum <a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Runtime options. </p>
+<table class="fieldtable">
+<tr><th colspan="2">Enumerator</th></tr><tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0"></a>mi_option_show_errors </td><td class="fielddoc"><p>Print error messages to <code>stderr</code>. </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda"></a>mi_option_show_stats </td><td class="fielddoc"><p>Print statistics to <code>stderr</code> when the program is done. </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777"></a>mi_option_verbose </td><td class="fielddoc"><p>Print verbose messages to <code>stderr</code>. </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b"></a>mi_option_eager_commit </td><td class="fielddoc"><p>Eagerly commit segments (4MiB) (enabled by default). </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad"></a>mi_option_eager_region_commit </td><td class="fielddoc"><p>Eagerly commit large (256MiB) memory regions (enabled by default, except on Windows) </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e"></a>mi_option_large_os_pages </td><td class="fielddoc"><p>Use large OS pages (2MiB in size) if possible. </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2"></a>mi_option_reserve_huge_os_pages </td><td class="fielddoc"><p>The number of huge OS pages (1GiB in size) to reserve at the start of the program. </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1"></a>mi_option_segment_cache </td><td class="fielddoc"><p>The number of segments per thread to keep cached. </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968"></a>mi_option_page_reset </td><td class="fielddoc"><p>Reset page memory after <em>mi_option_reset_delay</em> milliseconds when it becomes free. </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d"></a>mi_option_segment_reset </td><td class="fielddoc"><p>Experimental. </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5"></a>mi_option_reset_delay </td><td class="fielddoc"><p>Delay in milli-seconds before resetting a page (100ms by default) </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74"></a>mi_option_use_numa_nodes </td><td class="fielddoc"><p>Pretend there are at most N NUMA nodes. </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536"></a>mi_option_reset_decommits </td><td class="fielddoc"><p>Experimental. </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c"></a>mi_option_eager_commit_delay </td><td class="fielddoc"><p>Experimental. </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf"></a>mi_option_os_tag </td><td class="fielddoc"><p>OS tag to assign to mimalloc'd memory. </p>
+</td></tr>
+<tr><td class="fieldname"><a id="ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a"></a>_mi_option_last </td><td class="fielddoc"></td></tr>
+</table>
+
+</div>
+</div>
+<h2 class="groupheader">Function Documentation</h2>
+<a id="gaebf6ff707a2e688ebb1a2296ca564054"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaebf6ff707a2e688ebb1a2296ca564054">◆ </a></span>mi_option_disable()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_option_disable </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> </td>
+ <td class="paramname"><em>option</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga04180ae41b0d601421dd62ced40ca050"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga04180ae41b0d601421dd62ced40ca050">◆ </a></span>mi_option_enable()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_option_enable </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> </td>
+ <td class="paramname"><em>option</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga7e8af195cc81d3fa64ccf2662caa565a"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga7e8af195cc81d3fa64ccf2662caa565a">◆ </a></span>mi_option_get()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">long mi_option_get </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> </td>
+ <td class="paramname"><em>option</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga459ad98f18b3fc9275474807fe0ca188"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga459ad98f18b3fc9275474807fe0ca188">◆ </a></span>mi_option_is_enabled()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">bool mi_option_is_enabled </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> </td>
+ <td class="paramname"><em>option</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gaf84921c32375e25754dc2ee6a911fa60"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaf84921c32375e25754dc2ee6a911fa60">◆ </a></span>mi_option_set()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_option_set </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> </td>
+ <td class="paramname"><em>option</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">long </td>
+ <td class="paramname"><em>value</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga7ef623e440e6e5545cb08c94e71e4b90"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga7ef623e440e6e5545cb08c94e71e4b90">◆ </a></span>mi_option_set_default()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_option_set_default </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> </td>
+ <td class="paramname"><em>option</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">long </td>
+ <td class="paramname"><em>value</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga9a13d05fcb77489cb06d4d017ebd8bed"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga9a13d05fcb77489cb06d4d017ebd8bed">◆ </a></span>mi_option_set_enabled()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_option_set_enabled </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> </td>
+ <td class="paramname"><em>option</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">bool </td>
+ <td class="paramname"><em>enable</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga65518b69ec5d32336b50e07f74b3f629"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga65518b69ec5d32336b50e07f74b3f629">◆ </a></span>mi_option_set_enabled_default()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_option_set_enabled_default </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> </td>
+ <td class="paramname"><em>option</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">bool </td>
+ <td class="paramname"><em>enable</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+var group__options =
+[
+ [ "mi_option_t", "group__options.html#gafebf7ed116adb38ae5218bc3ce06884c", [
+ [ "mi_option_show_errors", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0", null ],
+ [ "mi_option_show_stats", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda", null ],
+ [ "mi_option_verbose", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777", null ],
+ [ "mi_option_eager_commit", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b", null ],
+ [ "mi_option_eager_region_commit", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad", null ],
+ [ "mi_option_large_os_pages", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e", null ],
+ [ "mi_option_reserve_huge_os_pages", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2", null ],
+ [ "mi_option_segment_cache", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1", null ],
+ [ "mi_option_page_reset", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968", null ],
+ [ "mi_option_segment_reset", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d", null ],
+ [ "mi_option_reset_delay", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5", null ],
+ [ "mi_option_use_numa_nodes", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74", null ],
+ [ "mi_option_reset_decommits", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536", null ],
+ [ "mi_option_eager_commit_delay", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c", null ],
+ [ "mi_option_os_tag", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf", null ],
+ [ "_mi_option_last", "group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a", null ]
+ ] ],
+ [ "mi_option_disable", "group__options.html#gaebf6ff707a2e688ebb1a2296ca564054", null ],
+ [ "mi_option_enable", "group__options.html#ga04180ae41b0d601421dd62ced40ca050", null ],
+ [ "mi_option_get", "group__options.html#ga7e8af195cc81d3fa64ccf2662caa565a", null ],
+ [ "mi_option_is_enabled", "group__options.html#ga459ad98f18b3fc9275474807fe0ca188", null ],
+ [ "mi_option_set", "group__options.html#gaf84921c32375e25754dc2ee6a911fa60", null ],
+ [ "mi_option_set_default", "group__options.html#ga7ef623e440e6e5545cb08c94e71e4b90", null ],
+ [ "mi_option_set_enabled", "group__options.html#ga9a13d05fcb77489cb06d4d017ebd8bed", null ],
+ [ "mi_option_set_enabled_default", "group__options.html#ga65518b69ec5d32336b50e07f74b3f629", null ]
+];
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Posix</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('group__posix.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="summary">
+<a href="#func-members">Functions</a> </div>
+ <div class="headertitle">
+<div class="title">Posix</div> </div>
+</div><!--header-->
+<div class="contents">
+
+<p><code>mi_</code> prefixed implementations of various Posix, Unix, and C++ allocation functions.
+<a href="#details">More...</a></p>
+<table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="func-members"></a>
+Functions</h2></td></tr>
+<tr class="memitem:ga4531c9e775bb3ae12db57c1ba8a5d7de"><td class="memItemLeft" align="right" valign="top">size_t </td><td class="memItemRight" valign="bottom"><a class="el" href="group__posix.html#ga4531c9e775bb3ae12db57c1ba8a5d7de">mi_malloc_size</a> (const void *p)</td></tr>
+<tr class="separator:ga4531c9e775bb3ae12db57c1ba8a5d7de"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga06d07cf357bbac5c73ba5d0c0c421e17"><td class="memItemLeft" align="right" valign="top">size_t </td><td class="memItemRight" valign="bottom"><a class="el" href="group__posix.html#ga06d07cf357bbac5c73ba5d0c0c421e17">mi_malloc_usable_size</a> (const void *p)</td></tr>
+<tr class="separator:ga06d07cf357bbac5c73ba5d0c0c421e17"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga705dc7a64bffacfeeb0141501a5c35d7"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__posix.html#ga705dc7a64bffacfeeb0141501a5c35d7">mi_cfree</a> (void *p)</td></tr>
+<tr class="memdesc:ga705dc7a64bffacfeeb0141501a5c35d7"><td class="mdescLeft"> </td><td class="mdescRight">Just as <code>free</code> but also checks if the pointer <code>p</code> belongs to our heap. <a href="#ga705dc7a64bffacfeeb0141501a5c35d7">More...</a><br /></td></tr>
+<tr class="separator:ga705dc7a64bffacfeeb0141501a5c35d7"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gacff84f226ba9feb2031b8992e5579447"><td class="memItemLeft" align="right" valign="top">int </td><td class="memItemRight" valign="bottom"><a class="el" href="group__posix.html#gacff84f226ba9feb2031b8992e5579447">mi_posix_memalign</a> (void **p, size_t alignment, size_t size)</td></tr>
+<tr class="separator:gacff84f226ba9feb2031b8992e5579447"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gad5a69c8fea96aa2b7a7c818c2130090a"><td class="memItemLeft" align="right" valign="top">int </td><td class="memItemRight" valign="bottom"><a class="el" href="group__posix.html#gad5a69c8fea96aa2b7a7c818c2130090a">mi__posix_memalign</a> (void **p, size_t alignment, size_t size)</td></tr>
+<tr class="separator:gad5a69c8fea96aa2b7a7c818c2130090a"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaab7fa71ea93b96873f5d9883db57d40e"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__posix.html#gaab7fa71ea93b96873f5d9883db57d40e">mi_memalign</a> (size_t alignment, size_t size)</td></tr>
+<tr class="separator:gaab7fa71ea93b96873f5d9883db57d40e"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga73baaf5951f5165ba0763d0c06b6a93b"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__posix.html#ga73baaf5951f5165ba0763d0c06b6a93b">mi_valloc</a> (size_t size)</td></tr>
+<tr class="separator:ga73baaf5951f5165ba0763d0c06b6a93b"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaeb325c39b887d3b90d85d1eb1712fb1e"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__posix.html#gaeb325c39b887d3b90d85d1eb1712fb1e">mi_pvalloc</a> (size_t size)</td></tr>
+<tr class="separator:gaeb325c39b887d3b90d85d1eb1712fb1e"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga1326d2e4388630b5f81ca7206318b8e5"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__posix.html#ga1326d2e4388630b5f81ca7206318b8e5">mi_aligned_alloc</a> (size_t alignment, size_t size)</td></tr>
+<tr class="separator:ga1326d2e4388630b5f81ca7206318b8e5"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga48fad8648a2f1dab9c87ea9448a52088"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__posix.html#ga48fad8648a2f1dab9c87ea9448a52088">mi_reallocarray</a> (void *p, size_t count, size_t size)</td></tr>
+<tr class="separator:ga48fad8648a2f1dab9c87ea9448a52088"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gae01389eedab8d67341ff52e2aad80ebb"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__posix.html#gae01389eedab8d67341ff52e2aad80ebb">mi_free_size</a> (void *p, size_t size)</td></tr>
+<tr class="separator:gae01389eedab8d67341ff52e2aad80ebb"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga72e9d7ffb5fe94d69bc722c8506e27bc"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__posix.html#ga72e9d7ffb5fe94d69bc722c8506e27bc">mi_free_size_aligned</a> (void *p, size_t size, size_t alignment)</td></tr>
+<tr class="separator:ga72e9d7ffb5fe94d69bc722c8506e27bc"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga0d28d5cf61e6bfbb18c63092939fe5c9"><td class="memItemLeft" align="right" valign="top">void </td><td class="memItemRight" valign="bottom"><a class="el" href="group__posix.html#ga0d28d5cf61e6bfbb18c63092939fe5c9">mi_free_aligned</a> (void *p, size_t alignment)</td></tr>
+<tr class="separator:ga0d28d5cf61e6bfbb18c63092939fe5c9"><td class="memSeparator" colspan="2"> </td></tr>
+</table>
+<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
+<p><code>mi_</code> prefixed implementations of various Posix, Unix, and C++ allocation functions. </p>
+<p>Defined for convenience as all redirect to the regular mimalloc API. </p>
+<h2 class="groupheader">Function Documentation</h2>
+<a id="gad5a69c8fea96aa2b7a7c818c2130090a"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gad5a69c8fea96aa2b7a7c818c2130090a">◆ </a></span>mi__posix_memalign()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">int mi__posix_memalign </td>
+ <td>(</td>
+ <td class="paramtype">void ** </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga1326d2e4388630b5f81ca7206318b8e5"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga1326d2e4388630b5f81ca7206318b8e5">◆ </a></span>mi_aligned_alloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_aligned_alloc </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga705dc7a64bffacfeeb0141501a5c35d7"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga705dc7a64bffacfeeb0141501a5c35d7">◆ </a></span>mi_cfree()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_cfree </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Just as <code>free</code> but also checks if the pointer <code>p</code> belongs to our heap. </p>
+
+</div>
+</div>
+<a id="ga0d28d5cf61e6bfbb18c63092939fe5c9"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga0d28d5cf61e6bfbb18c63092939fe5c9">◆ </a></span>mi_free_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_free_aligned </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gae01389eedab8d67341ff52e2aad80ebb"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gae01389eedab8d67341ff52e2aad80ebb">◆ </a></span>mi_free_size()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_free_size </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga72e9d7ffb5fe94d69bc722c8506e27bc"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga72e9d7ffb5fe94d69bc722c8506e27bc">◆ </a></span>mi_free_size_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void mi_free_size_aligned </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga4531c9e775bb3ae12db57c1ba8a5d7de"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga4531c9e775bb3ae12db57c1ba8a5d7de">◆ </a></span>mi_malloc_size()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">size_t mi_malloc_size </td>
+ <td>(</td>
+ <td class="paramtype">const void * </td>
+ <td class="paramname"><em>p</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga06d07cf357bbac5c73ba5d0c0c421e17"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga06d07cf357bbac5c73ba5d0c0c421e17">◆ </a></span>mi_malloc_usable_size()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">size_t mi_malloc_usable_size </td>
+ <td>(</td>
+ <td class="paramtype">const void * </td>
+ <td class="paramname"><em>p</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gaab7fa71ea93b96873f5d9883db57d40e"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaab7fa71ea93b96873f5d9883db57d40e">◆ </a></span>mi_memalign()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_memalign </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gacff84f226ba9feb2031b8992e5579447"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gacff84f226ba9feb2031b8992e5579447">◆ </a></span>mi_posix_memalign()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">int mi_posix_memalign </td>
+ <td>(</td>
+ <td class="paramtype">void ** </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gaeb325c39b887d3b90d85d1eb1712fb1e"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaeb325c39b887d3b90d85d1eb1712fb1e">◆ </a></span>mi_pvalloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_pvalloc </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga48fad8648a2f1dab9c87ea9448a52088"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga48fad8648a2f1dab9c87ea9448a52088">◆ </a></span>mi_reallocarray()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_reallocarray </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>count</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga73baaf5951f5165ba0763d0c06b6a93b"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga73baaf5951f5165ba0763d0c06b6a93b">◆ </a></span>mi_valloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_valloc </td>
+ <td>(</td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em></td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+var group__posix =
+[
+ [ "mi__posix_memalign", "group__posix.html#gad5a69c8fea96aa2b7a7c818c2130090a", null ],
+ [ "mi_aligned_alloc", "group__posix.html#ga1326d2e4388630b5f81ca7206318b8e5", null ],
+ [ "mi_cfree", "group__posix.html#ga705dc7a64bffacfeeb0141501a5c35d7", null ],
+ [ "mi_free_aligned", "group__posix.html#ga0d28d5cf61e6bfbb18c63092939fe5c9", null ],
+ [ "mi_free_size", "group__posix.html#gae01389eedab8d67341ff52e2aad80ebb", null ],
+ [ "mi_free_size_aligned", "group__posix.html#ga72e9d7ffb5fe94d69bc722c8506e27bc", null ],
+ [ "mi_malloc_size", "group__posix.html#ga4531c9e775bb3ae12db57c1ba8a5d7de", null ],
+ [ "mi_malloc_usable_size", "group__posix.html#ga06d07cf357bbac5c73ba5d0c0c421e17", null ],
+ [ "mi_memalign", "group__posix.html#gaab7fa71ea93b96873f5d9883db57d40e", null ],
+ [ "mi_posix_memalign", "group__posix.html#gacff84f226ba9feb2031b8992e5579447", null ],
+ [ "mi_pvalloc", "group__posix.html#gaeb325c39b887d3b90d85d1eb1712fb1e", null ],
+ [ "mi_reallocarray", "group__posix.html#ga48fad8648a2f1dab9c87ea9448a52088", null ],
+ [ "mi_valloc", "group__posix.html#ga73baaf5951f5165ba0763d0c06b6a93b", null ]
+];
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Typed Macros</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('group__typed.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="summary">
+<a href="#define-members">Macros</a> </div>
+ <div class="headertitle">
+<div class="title">Typed Macros</div> </div>
+</div><!--header-->
+<div class="contents">
+
+<p>Typed allocation macros.
+<a href="#details">More...</a></p>
+<table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="define-members"></a>
+Macros</h2></td></tr>
+<tr class="memitem:ga0619a62c5fd886f1016030abe91f0557"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="group__typed.html#ga0619a62c5fd886f1016030abe91f0557">mi_malloc_tp</a>(tp)</td></tr>
+<tr class="memdesc:ga0619a62c5fd886f1016030abe91f0557"><td class="mdescLeft"> </td><td class="mdescRight">Allocate a block of type <em>tp</em>. <a href="#ga0619a62c5fd886f1016030abe91f0557">More...</a><br /></td></tr>
+<tr class="separator:ga0619a62c5fd886f1016030abe91f0557"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gac77a61bdaf680a803785fe307820b48c"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="group__typed.html#gac77a61bdaf680a803785fe307820b48c">mi_zalloc_tp</a>(tp)</td></tr>
+<tr class="memdesc:gac77a61bdaf680a803785fe307820b48c"><td class="mdescLeft"> </td><td class="mdescRight">Allocate a zero-initialized block of type <em>tp</em>. <a href="#gac77a61bdaf680a803785fe307820b48c">More...</a><br /></td></tr>
+<tr class="separator:gac77a61bdaf680a803785fe307820b48c"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gae80c47c9d4cab10961fff1a8ac98fc07"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="group__typed.html#gae80c47c9d4cab10961fff1a8ac98fc07">mi_calloc_tp</a>(tp, count)</td></tr>
+<tr class="memdesc:gae80c47c9d4cab10961fff1a8ac98fc07"><td class="mdescLeft"> </td><td class="mdescRight">Allocate <em>count</em> zero-initialized blocks of type <em>tp</em>. <a href="#gae80c47c9d4cab10961fff1a8ac98fc07">More...</a><br /></td></tr>
+<tr class="separator:gae80c47c9d4cab10961fff1a8ac98fc07"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gae5cb6e0fafc9f23169c5622e077afe8b"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="group__typed.html#gae5cb6e0fafc9f23169c5622e077afe8b">mi_mallocn_tp</a>(tp, count)</td></tr>
+<tr class="memdesc:gae5cb6e0fafc9f23169c5622e077afe8b"><td class="mdescLeft"> </td><td class="mdescRight">Allocate <em>count</em> blocks of type <em>tp</em>. <a href="#gae5cb6e0fafc9f23169c5622e077afe8b">More...</a><br /></td></tr>
+<tr class="separator:gae5cb6e0fafc9f23169c5622e077afe8b"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga1158b49a55dfa81f58a4426a7578f523"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="group__typed.html#ga1158b49a55dfa81f58a4426a7578f523">mi_reallocn_tp</a>(p, tp, count)</td></tr>
+<tr class="memdesc:ga1158b49a55dfa81f58a4426a7578f523"><td class="mdescLeft"> </td><td class="mdescRight">Re-allocate to <em>count</em> blocks of type <em>tp</em>. <a href="#ga1158b49a55dfa81f58a4426a7578f523">More...</a><br /></td></tr>
+<tr class="separator:ga1158b49a55dfa81f58a4426a7578f523"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga653bcb24ac495bc19940ecd6898f9cd7"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="group__typed.html#ga653bcb24ac495bc19940ecd6898f9cd7">mi_heap_malloc_tp</a>(hp, tp)</td></tr>
+<tr class="memdesc:ga653bcb24ac495bc19940ecd6898f9cd7"><td class="mdescLeft"> </td><td class="mdescRight">Allocate a block of type <em>tp</em> in a heap <em>hp</em>. <a href="#ga653bcb24ac495bc19940ecd6898f9cd7">More...</a><br /></td></tr>
+<tr class="separator:ga653bcb24ac495bc19940ecd6898f9cd7"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gad6e87e86e994aa14416ae9b5d4c188fe"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="group__typed.html#gad6e87e86e994aa14416ae9b5d4c188fe">mi_heap_zalloc_tp</a>(hp, tp)</td></tr>
+<tr class="memdesc:gad6e87e86e994aa14416ae9b5d4c188fe"><td class="mdescLeft"> </td><td class="mdescRight">Allocate a zero-initialized block of type <em>tp</em> in a heap <em>hp</em>. <a href="#gad6e87e86e994aa14416ae9b5d4c188fe">More...</a><br /></td></tr>
+<tr class="separator:gad6e87e86e994aa14416ae9b5d4c188fe"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga4e5d1f1707c90e5f55e023ac5f45fe74"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="group__typed.html#ga4e5d1f1707c90e5f55e023ac5f45fe74">mi_heap_calloc_tp</a>(hp, tp, count)</td></tr>
+<tr class="memdesc:ga4e5d1f1707c90e5f55e023ac5f45fe74"><td class="mdescLeft"> </td><td class="mdescRight">Allocate <em>count</em> zero-initialized blocks of type <em>tp</em> in a heap <em>hp</em>. <a href="#ga4e5d1f1707c90e5f55e023ac5f45fe74">More...</a><br /></td></tr>
+<tr class="separator:ga4e5d1f1707c90e5f55e023ac5f45fe74"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga6b75cb9c4b9c647661d0924552dc6e83"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="group__typed.html#ga6b75cb9c4b9c647661d0924552dc6e83">mi_heap_mallocn_tp</a>(hp, tp, count)</td></tr>
+<tr class="memdesc:ga6b75cb9c4b9c647661d0924552dc6e83"><td class="mdescLeft"> </td><td class="mdescRight">Allocate <em>count</em> blocks of type <em>tp</em> in a heap <em>hp</em>. <a href="#ga6b75cb9c4b9c647661d0924552dc6e83">More...</a><br /></td></tr>
+<tr class="separator:ga6b75cb9c4b9c647661d0924552dc6e83"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gaf213d5422ec35e7f6caad827c79bc948"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="group__typed.html#gaf213d5422ec35e7f6caad827c79bc948">mi_heap_reallocn_tp</a>(hp, p, tp, count)</td></tr>
+<tr class="memdesc:gaf213d5422ec35e7f6caad827c79bc948"><td class="mdescLeft"> </td><td class="mdescRight">Re-allocate to <em>count</em> blocks of type <em>tp</em> in a heap <em>hp</em>. <a href="#gaf213d5422ec35e7f6caad827c79bc948">More...</a><br /></td></tr>
+<tr class="separator:gaf213d5422ec35e7f6caad827c79bc948"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga3e50a1600958fcaf1a7f3560c9174f9e"><td class="memItemLeft" align="right" valign="top">#define </td><td class="memItemRight" valign="bottom"><a class="el" href="group__typed.html#ga3e50a1600958fcaf1a7f3560c9174f9e">mi_heap_recalloc_tp</a>(hp, p, tp, count)</td></tr>
+<tr class="memdesc:ga3e50a1600958fcaf1a7f3560c9174f9e"><td class="mdescLeft"> </td><td class="mdescRight">Re-allocate to <em>count</em> zero initialized blocks of type <em>tp</em> in a heap <em>hp</em>. <a href="#ga3e50a1600958fcaf1a7f3560c9174f9e">More...</a><br /></td></tr>
+<tr class="separator:ga3e50a1600958fcaf1a7f3560c9174f9e"><td class="memSeparator" colspan="2"> </td></tr>
+</table>
+<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
+<p>Typed allocation macros. </p>
+<p>For example: </p><div class="fragment"><div class="line"><span class="keywordtype">int</span>* p = <a class="code" href="group__typed.html#ga0619a62c5fd886f1016030abe91f0557">mi_malloc_tp</a>(<span class="keywordtype">int</span>)</div></div><!-- fragment --> <h2 class="groupheader">Macro Definition Documentation</h2>
+<a id="gae80c47c9d4cab10961fff1a8ac98fc07"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gae80c47c9d4cab10961fff1a8ac98fc07">◆ </a></span>mi_calloc_tp</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">#define mi_calloc_tp</td>
+ <td>(</td>
+ <td class="paramtype"> </td>
+ <td class="paramname">tp, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">count </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate <em>count</em> zero-initialized blocks of type <em>tp</em>. </p>
+
+</div>
+</div>
+<a id="ga4e5d1f1707c90e5f55e023ac5f45fe74"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga4e5d1f1707c90e5f55e023ac5f45fe74">◆ </a></span>mi_heap_calloc_tp</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">#define mi_heap_calloc_tp</td>
+ <td>(</td>
+ <td class="paramtype"> </td>
+ <td class="paramname">hp, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">tp, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">count </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate <em>count</em> zero-initialized blocks of type <em>tp</em> in a heap <em>hp</em>. </p>
+
+</div>
+</div>
+<a id="ga653bcb24ac495bc19940ecd6898f9cd7"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga653bcb24ac495bc19940ecd6898f9cd7">◆ </a></span>mi_heap_malloc_tp</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">#define mi_heap_malloc_tp</td>
+ <td>(</td>
+ <td class="paramtype"> </td>
+ <td class="paramname">hp, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">tp </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate a block of type <em>tp</em> in a heap <em>hp</em>. </p>
+
+</div>
+</div>
+<a id="ga6b75cb9c4b9c647661d0924552dc6e83"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga6b75cb9c4b9c647661d0924552dc6e83">◆ </a></span>mi_heap_mallocn_tp</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">#define mi_heap_mallocn_tp</td>
+ <td>(</td>
+ <td class="paramtype"> </td>
+ <td class="paramname">hp, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">tp, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">count </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate <em>count</em> blocks of type <em>tp</em> in a heap <em>hp</em>. </p>
+
+</div>
+</div>
+<a id="gaf213d5422ec35e7f6caad827c79bc948"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gaf213d5422ec35e7f6caad827c79bc948">◆ </a></span>mi_heap_reallocn_tp</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">#define mi_heap_reallocn_tp</td>
+ <td>(</td>
+ <td class="paramtype"> </td>
+ <td class="paramname">hp, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">p, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">tp, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">count </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Re-allocate to <em>count</em> blocks of type <em>tp</em> in a heap <em>hp</em>. </p>
+
+</div>
+</div>
+<a id="ga3e50a1600958fcaf1a7f3560c9174f9e"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga3e50a1600958fcaf1a7f3560c9174f9e">◆ </a></span>mi_heap_recalloc_tp</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">#define mi_heap_recalloc_tp</td>
+ <td>(</td>
+ <td class="paramtype"> </td>
+ <td class="paramname">hp, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">p, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">tp, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">count </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Re-allocate to <em>count</em> zero initialized blocks of type <em>tp</em> in a heap <em>hp</em>. </p>
+
+</div>
+</div>
+<a id="gad6e87e86e994aa14416ae9b5d4c188fe"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gad6e87e86e994aa14416ae9b5d4c188fe">◆ </a></span>mi_heap_zalloc_tp</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">#define mi_heap_zalloc_tp</td>
+ <td>(</td>
+ <td class="paramtype"> </td>
+ <td class="paramname">hp, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">tp </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate a zero-initialized block of type <em>tp</em> in a heap <em>hp</em>. </p>
+
+</div>
+</div>
+<a id="ga0619a62c5fd886f1016030abe91f0557"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga0619a62c5fd886f1016030abe91f0557">◆ </a></span>mi_malloc_tp</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">#define mi_malloc_tp</td>
+ <td>(</td>
+ <td class="paramtype"> </td>
+ <td class="paramname">tp</td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate a block of type <em>tp</em>. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+ <table class="params">
+ <tr><td class="paramname">tp</td><td>The type of the block to allocate. </td></tr>
+ </table>
+ </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>A pointer to an object of type <em>tp</em>, or <em>NULL</em> if out of memory.</dd></dl>
+<p><b>Example:</b> </p><div class="fragment"><div class="line"><span class="keywordtype">int</span>* p = <a class="code" href="group__typed.html#ga0619a62c5fd886f1016030abe91f0557">mi_malloc_tp</a>(<span class="keywordtype">int</span>)</div></div><!-- fragment --><dl class="section see"><dt>See also</dt><dd><a class="el" href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a" title="Allocate size bytes.">mi_malloc()</a> </dd></dl>
+
+</div>
+</div>
+<a id="gae5cb6e0fafc9f23169c5622e077afe8b"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gae5cb6e0fafc9f23169c5622e077afe8b">◆ </a></span>mi_mallocn_tp</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">#define mi_mallocn_tp</td>
+ <td>(</td>
+ <td class="paramtype"> </td>
+ <td class="paramname">tp, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">count </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate <em>count</em> blocks of type <em>tp</em>. </p>
+
+</div>
+</div>
+<a id="ga1158b49a55dfa81f58a4426a7578f523"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga1158b49a55dfa81f58a4426a7578f523">◆ </a></span>mi_reallocn_tp</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">#define mi_reallocn_tp</td>
+ <td>(</td>
+ <td class="paramtype"> </td>
+ <td class="paramname">p, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">tp, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype"> </td>
+ <td class="paramname">count </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Re-allocate to <em>count</em> blocks of type <em>tp</em>. </p>
+
+</div>
+</div>
+<a id="gac77a61bdaf680a803785fe307820b48c"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gac77a61bdaf680a803785fe307820b48c">◆ </a></span>mi_zalloc_tp</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">#define mi_zalloc_tp</td>
+ <td>(</td>
+ <td class="paramtype"> </td>
+ <td class="paramname">tp</td><td>)</td>
+ <td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+<p>Allocate a zero-initialized block of type <em>tp</em>. </p>
+
+</div>
+</div>
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+var group__typed =
+[
+ [ "mi_calloc_tp", "group__typed.html#gae80c47c9d4cab10961fff1a8ac98fc07", null ],
+ [ "mi_heap_calloc_tp", "group__typed.html#ga4e5d1f1707c90e5f55e023ac5f45fe74", null ],
+ [ "mi_heap_malloc_tp", "group__typed.html#ga653bcb24ac495bc19940ecd6898f9cd7", null ],
+ [ "mi_heap_mallocn_tp", "group__typed.html#ga6b75cb9c4b9c647661d0924552dc6e83", null ],
+ [ "mi_heap_reallocn_tp", "group__typed.html#gaf213d5422ec35e7f6caad827c79bc948", null ],
+ [ "mi_heap_recalloc_tp", "group__typed.html#ga3e50a1600958fcaf1a7f3560c9174f9e", null ],
+ [ "mi_heap_zalloc_tp", "group__typed.html#gad6e87e86e994aa14416ae9b5d4c188fe", null ],
+ [ "mi_malloc_tp", "group__typed.html#ga0619a62c5fd886f1016030abe91f0557", null ],
+ [ "mi_mallocn_tp", "group__typed.html#gae5cb6e0fafc9f23169c5622e077afe8b", null ],
+ [ "mi_reallocn_tp", "group__typed.html#ga1158b49a55dfa81f58a4426a7578f523", null ],
+ [ "mi_zalloc_tp", "group__typed.html#gac77a61bdaf680a803785fe307820b48c", null ]
+];
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Zero initialized re-allocation</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('group__zeroinit.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="summary">
+<a href="#func-members">Functions</a> </div>
+ <div class="headertitle">
+<div class="title">Zero initialized re-allocation</div> </div>
+</div><!--header-->
+<div class="contents">
+
+<p>The zero-initialized re-allocations are only valid on memory that was originally allocated with zero initialization too.
+<a href="#details">More...</a></p>
+<table class="memberdecls">
+<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="func-members"></a>
+Functions</h2></td></tr>
+<tr class="memitem:ga8c292e142110229a2980b37ab036dbc6"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__zeroinit.html#ga8c292e142110229a2980b37ab036dbc6">mi_rezalloc</a> (void *p, size_t newsize)</td></tr>
+<tr class="separator:ga8c292e142110229a2980b37ab036dbc6"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gacd71a7bce96aab38ae6de17af2eb2cf0"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__zeroinit.html#gacd71a7bce96aab38ae6de17af2eb2cf0">mi_rezalloc_aligned</a> (void *p, size_t newsize, size_t alignment)</td></tr>
+<tr class="separator:gacd71a7bce96aab38ae6de17af2eb2cf0"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gae8b358c417e61d5307da002702b0a8e1"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__zeroinit.html#gae8b358c417e61d5307da002702b0a8e1">mi_rezalloc_aligned_at</a> (void *p, size_t newsize, size_t alignment, size_t offset)</td></tr>
+<tr class="separator:gae8b358c417e61d5307da002702b0a8e1"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga3e7e5c291acf1c7fd7ffd9914a9f945f"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__zeroinit.html#ga3e7e5c291acf1c7fd7ffd9914a9f945f">mi_recalloc_aligned</a> (void *p, size_t newcount, size_t size, size_t alignment)</td></tr>
+<tr class="separator:ga3e7e5c291acf1c7fd7ffd9914a9f945f"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga4ff5e92ad73585418a072c9d059e5cf9"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__zeroinit.html#ga4ff5e92ad73585418a072c9d059e5cf9">mi_recalloc_aligned_at</a> (void *p, size_t newcount, size_t size, size_t alignment, size_t offset)</td></tr>
+<tr class="separator:ga4ff5e92ad73585418a072c9d059e5cf9"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gacfad83f14eb5d6a42a497a898e19fc76"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__zeroinit.html#gacfad83f14eb5d6a42a497a898e19fc76">mi_heap_rezalloc</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, void *p, size_t newsize)</td></tr>
+<tr class="separator:gacfad83f14eb5d6a42a497a898e19fc76"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga8648c5fbb22a80f0262859099f06dfbd"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__zeroinit.html#ga8648c5fbb22a80f0262859099f06dfbd">mi_heap_recalloc</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, void *p, size_t newcount, size_t size)</td></tr>
+<tr class="separator:ga8648c5fbb22a80f0262859099f06dfbd"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga375fa8a611c51905e592d5d467c49664"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__zeroinit.html#ga375fa8a611c51905e592d5d467c49664">mi_heap_rezalloc_aligned</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, void *p, size_t newsize, size_t alignment)</td></tr>
+<tr class="separator:ga375fa8a611c51905e592d5d467c49664"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:gac90da54fa7e5d10bdc97ce0b51dce2eb"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__zeroinit.html#gac90da54fa7e5d10bdc97ce0b51dce2eb">mi_heap_rezalloc_aligned_at</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, void *p, size_t newsize, size_t alignment, size_t offset)</td></tr>
+<tr class="separator:gac90da54fa7e5d10bdc97ce0b51dce2eb"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga9f3f999396c8f77ca5e80e7b40ac29e3"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__zeroinit.html#ga9f3f999396c8f77ca5e80e7b40ac29e3">mi_heap_recalloc_aligned</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, void *p, size_t newcount, size_t size, size_t alignment)</td></tr>
+<tr class="separator:ga9f3f999396c8f77ca5e80e7b40ac29e3"><td class="memSeparator" colspan="2"> </td></tr>
+<tr class="memitem:ga496452c96f1de8c500be9fddf52edaf7"><td class="memItemLeft" align="right" valign="top">void * </td><td class="memItemRight" valign="bottom"><a class="el" href="group__zeroinit.html#ga496452c96f1de8c500be9fddf52edaf7">mi_heap_recalloc_aligned_at</a> (<a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> *heap, void *p, size_t newcount, size_t size, size_t alignment, size_t offset)</td></tr>
+<tr class="separator:ga496452c96f1de8c500be9fddf52edaf7"><td class="memSeparator" colspan="2"> </td></tr>
+</table>
+<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
+<p>The zero-initialized re-allocations are only valid on memory that was originally allocated with zero initialization too. </p>
+<p>e.g. <code>mi_calloc</code>, <code>mi_zalloc</code>, <code>mi_zalloc_aligned</code> etc. see <a href="https://github.com/microsoft/mimalloc/issues/63#issuecomment-508272992">https://github.com/microsoft/mimalloc/issues/63#issuecomment-508272992</a> </p>
+<h2 class="groupheader">Function Documentation</h2>
+<a id="ga8648c5fbb22a80f0262859099f06dfbd"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga8648c5fbb22a80f0262859099f06dfbd">◆ </a></span>mi_heap_recalloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_recalloc </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newcount</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga9f3f999396c8f77ca5e80e7b40ac29e3"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga9f3f999396c8f77ca5e80e7b40ac29e3">◆ </a></span>mi_heap_recalloc_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_recalloc_aligned </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newcount</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga496452c96f1de8c500be9fddf52edaf7"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga496452c96f1de8c500be9fddf52edaf7">◆ </a></span>mi_heap_recalloc_aligned_at()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_recalloc_aligned_at </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newcount</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>offset</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gacfad83f14eb5d6a42a497a898e19fc76"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gacfad83f14eb5d6a42a497a898e19fc76">◆ </a></span>mi_heap_rezalloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_rezalloc </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga375fa8a611c51905e592d5d467c49664"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga375fa8a611c51905e592d5d467c49664">◆ </a></span>mi_heap_rezalloc_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_rezalloc_aligned </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gac90da54fa7e5d10bdc97ce0b51dce2eb"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gac90da54fa7e5d10bdc97ce0b51dce2eb">◆ </a></span>mi_heap_rezalloc_aligned_at()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_heap_rezalloc_aligned_at </td>
+ <td>(</td>
+ <td class="paramtype"><a class="el" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a> * </td>
+ <td class="paramname"><em>heap</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>offset</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga3e7e5c291acf1c7fd7ffd9914a9f945f"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga3e7e5c291acf1c7fd7ffd9914a9f945f">◆ </a></span>mi_recalloc_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_recalloc_aligned </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newcount</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga4ff5e92ad73585418a072c9d059e5cf9"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga4ff5e92ad73585418a072c9d059e5cf9">◆ </a></span>mi_recalloc_aligned_at()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_recalloc_aligned_at </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newcount</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>size</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>offset</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="ga8c292e142110229a2980b37ab036dbc6"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#ga8c292e142110229a2980b37ab036dbc6">◆ </a></span>mi_rezalloc()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_rezalloc </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gacd71a7bce96aab38ae6de17af2eb2cf0"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gacd71a7bce96aab38ae6de17af2eb2cf0">◆ </a></span>mi_rezalloc_aligned()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_rezalloc_aligned </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+<a id="gae8b358c417e61d5307da002702b0a8e1"></a>
+<h2 class="memtitle"><span class="permalink"><a href="#gae8b358c417e61d5307da002702b0a8e1">◆ </a></span>mi_rezalloc_aligned_at()</h2>
+
+<div class="memitem">
+<div class="memproto">
+ <table class="memname">
+ <tr>
+ <td class="memname">void* mi_rezalloc_aligned_at </td>
+ <td>(</td>
+ <td class="paramtype">void * </td>
+ <td class="paramname"><em>p</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>newsize</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>alignment</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">size_t </td>
+ <td class="paramname"><em>offset</em> </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>)</td>
+ <td></td><td></td>
+ </tr>
+ </table>
+</div><div class="memdoc">
+
+</div>
+</div>
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+var group__zeroinit =
+[
+ [ "mi_heap_recalloc", "group__zeroinit.html#ga8648c5fbb22a80f0262859099f06dfbd", null ],
+ [ "mi_heap_recalloc_aligned", "group__zeroinit.html#ga9f3f999396c8f77ca5e80e7b40ac29e3", null ],
+ [ "mi_heap_recalloc_aligned_at", "group__zeroinit.html#ga496452c96f1de8c500be9fddf52edaf7", null ],
+ [ "mi_heap_rezalloc", "group__zeroinit.html#gacfad83f14eb5d6a42a497a898e19fc76", null ],
+ [ "mi_heap_rezalloc_aligned", "group__zeroinit.html#ga375fa8a611c51905e592d5d467c49664", null ],
+ [ "mi_heap_rezalloc_aligned_at", "group__zeroinit.html#gac90da54fa7e5d10bdc97ce0b51dce2eb", null ],
+ [ "mi_recalloc_aligned", "group__zeroinit.html#ga3e7e5c291acf1c7fd7ffd9914a9f945f", null ],
+ [ "mi_recalloc_aligned_at", "group__zeroinit.html#ga4ff5e92ad73585418a072c9d059e5cf9", null ],
+ [ "mi_rezalloc", "group__zeroinit.html#ga8c292e142110229a2980b37ab036dbc6", null ],
+ [ "mi_rezalloc_aligned", "group__zeroinit.html#gacd71a7bce96aab38ae6de17af2eb2cf0", null ],
+ [ "mi_rezalloc_aligned_at", "group__zeroinit.html#gae8b358c417e61d5307da002702b0a8e1", null ]
+];
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Main Page</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('index.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="PageDoc"><div class="header">
+ <div class="headertitle">
+<div class="title">mi-malloc Documentation</div> </div>
+</div><!--header-->
+<div class="contents">
+<div class="textblock"><p>This is the API documentation of the <a href="https://github.com/microsoft/mimalloc">mimalloc</a> allocator (pronounced "me-malloc") – a general purpose allocator with excellent <a href="bench.html">performance</a> characteristics. Initially developed by Daan Leijen for the run-time systems of the <a href="https://github.com/koka-lang/koka">Koka</a> and <a href="https://github.com/leanprover/lean">Lean</a> languages.</p>
+<p>It is a drop-in replacement for <code>malloc</code> and can be used in other programs without code changes, for example, on Unix you can use it as: </p><div class="fragment"><div class="line">> LD_PRELOAD=/usr/bin/libmimalloc.so myprogram</div></div><!-- fragment --><p>Notable aspects of the design include:</p>
+<ul>
+<li><b>small and consistent</b>: the library is less than 6k LOC using simple and consistent data structures. This makes it very suitable to integrate and adapt in other projects. For runtime systems it provides hooks for a monotonic <em>heartbeat</em> and deferred freeing (for bounded worst-case times with reference counting).</li>
+<li><b>free list sharding</b>: the big idea: instead of one big free list (per size class) we have many smaller lists per memory "page" which both reduces fragmentation and increases locality – things that are allocated close in time get allocated close in memory. (A memory "page" in <em>mimalloc</em> contains blocks of one size class and is usually 64KiB on a 64-bit system).</li>
+<li><b>eager page reset</b>: when a "page" becomes empty (with increased chance due to free list sharding) the memory is marked to the OS as unused ("reset" or "purged") reducing (real) memory pressure and fragmentation, especially in long running programs.</li>
+<li><b>secure</b>: <em>mimalloc</em> can be build in secure mode, adding guard pages, randomized allocation, encrypted free lists, etc. to protect against various heap vulnerabilities. The performance penalty is only around 3% on average over our benchmarks.</li>
+<li><b>first-class heaps</b>: efficiently create and use multiple heaps to allocate across different regions. A heap can be destroyed at once instead of deallocating each object separately.</li>
+<li><b>bounded</b>: it does not suffer from <em>blowup</em> [1], has bounded worst-case allocation times (<em>wcat</em>), bounded space overhead (~0.2% meta-data, with at most 12.5% waste in allocation sizes), and has no internal points of contention using only atomic operations.</li>
+<li><b>fast</b>: In our benchmarks (see <a href="#performance">below</a>), <em>mimalloc</em> always outperforms all other leading allocators (<em>jemalloc</em>, <em>tcmalloc</em>, <em>Hoard</em>, etc), and usually uses less memory (up to 25% more in the worst case). A nice property is that it does consistently well over a wide range of benchmarks.</li>
+</ul>
+<p>You can read more on the design of <em>mimalloc</em> in the <a href="https://www.microsoft.com/en-us/research/publication/mimalloc-free-list-sharding-in-action">technical report</a> which also has detailed benchmark results.</p>
+<p>Further information:</p>
+<ul>
+<li><a class="el" href="build.html">Building</a></li>
+<li><a class="el" href="using.html">Using the library</a></li>
+<li><a class="el" href="environment.html">Environment Options</a></li>
+<li><a class="el" href="overrides.html">Overriding Malloc</a></li>
+<li><a class="el" href="bench.html">Performance</a></li>
+<li><a class="el" href="group__malloc.html">Basic Allocation</a></li>
+<li><a class="el" href="group__extended.html">Extended Functions</a></li>
+<li><a class="el" href="group__aligned.html">Aligned Allocation</a></li>
+<li><a class="el" href="group__heap.html">Heap Allocation</a></li>
+<li><a class="el" href="group__typed.html">Typed Macros</a></li>
+<li><a class="el" href="group__analysis.html">Heap Introspection</a></li>
+<li><a class="el" href="group__options.html">Runtime Options</a></li>
+<li><a class="el" href="group__posix.html">Posix</a></li>
+<li><a class="el" href="group__cpp.html">C++ wrappers</a> </li>
+</ul>
+</div></div><!-- PageDoc -->
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+/*!
+ * jQuery JavaScript Library v1.7.2
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Wed Mar 21 12:46:34 2012 -0700
+ */
+(function(bd,L){var av=bd.document,bu=bd.navigator,bm=bd.location;var b=(function(){var bF=function(b0,b1){return new bF.fn.init(b0,b1,bD)},bU=bd.jQuery,bH=bd.$,bD,bY=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,bM=/\S/,bI=/^\s+/,bE=/\s+$/,bA=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,bN=/^[\],:{}\s]*$/,bW=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,bP=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,bJ=/(?:^|:|,)(?:\s*\[)+/g,by=/(webkit)[ \/]([\w.]+)/,bR=/(opera)(?:.*version)?[ \/]([\w.]+)/,bQ=/(msie) ([\w.]+)/,bS=/(mozilla)(?:.*? rv:([\w.]+))?/,bB=/-([a-z]|[0-9])/ig,bZ=/^-ms-/,bT=function(b0,b1){return(b1+"").toUpperCase()},bX=bu.userAgent,bV,bC,e,bL=Object.prototype.toString,bG=Object.prototype.hasOwnProperty,bz=Array.prototype.push,bK=Array.prototype.slice,bO=String.prototype.trim,bv=Array.prototype.indexOf,bx={};bF.fn=bF.prototype={constructor:bF,init:function(b0,b4,b3){var b2,b5,b1,b6;if(!b0){return this}if(b0.nodeType){this.context=this[0]=b0;this.length=1;return this}if(b0==="body"&&!b4&&av.body){this.context=av;this[0]=av.body;this.selector=b0;this.length=1;return this}if(typeof b0==="string"){if(b0.charAt(0)==="<"&&b0.charAt(b0.length-1)===">"&&b0.length>=3){b2=[null,b0,null]}else{b2=bY.exec(b0)}if(b2&&(b2[1]||!b4)){if(b2[1]){b4=b4 instanceof bF?b4[0]:b4;b6=(b4?b4.ownerDocument||b4:av);b1=bA.exec(b0);if(b1){if(bF.isPlainObject(b4)){b0=[av.createElement(b1[1])];bF.fn.attr.call(b0,b4,true)}else{b0=[b6.createElement(b1[1])]}}else{b1=bF.buildFragment([b2[1]],[b6]);b0=(b1.cacheable?bF.clone(b1.fragment):b1.fragment).childNodes}return bF.merge(this,b0)}else{b5=av.getElementById(b2[2]);if(b5&&b5.parentNode){if(b5.id!==b2[2]){return b3.find(b0)}this.length=1;this[0]=b5}this.context=av;this.selector=b0;return this}}else{if(!b4||b4.jquery){return(b4||b3).find(b0)}else{return this.constructor(b4).find(b0)}}}else{if(bF.isFunction(b0)){return b3.ready(b0)}}if(b0.selector!==L){this.selector=b0.selector;this.context=b0.context}return bF.makeArray(b0,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return bK.call(this,0)},get:function(b0){return b0==null?this.toArray():(b0<0?this[this.length+b0]:this[b0])},pushStack:function(b1,b3,b0){var b2=this.constructor();if(bF.isArray(b1)){bz.apply(b2,b1)}else{bF.merge(b2,b1)}b2.prevObject=this;b2.context=this.context;if(b3==="find"){b2.selector=this.selector+(this.selector?" ":"")+b0}else{if(b3){b2.selector=this.selector+"."+b3+"("+b0+")"}}return b2},each:function(b1,b0){return bF.each(this,b1,b0)},ready:function(b0){bF.bindReady();bC.add(b0);return this},eq:function(b0){b0=+b0;return b0===-1?this.slice(b0):this.slice(b0,b0+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(bK.apply(this,arguments),"slice",bK.call(arguments).join(","))},map:function(b0){return this.pushStack(bF.map(this,function(b2,b1){return b0.call(b2,b1,b2)}))},end:function(){return this.prevObject||this.constructor(null)},push:bz,sort:[].sort,splice:[].splice};bF.fn.init.prototype=bF.fn;bF.extend=bF.fn.extend=function(){var b9,b2,b0,b1,b6,b7,b5=arguments[0]||{},b4=1,b3=arguments.length,b8=false;if(typeof b5==="boolean"){b8=b5;b5=arguments[1]||{};b4=2}if(typeof b5!=="object"&&!bF.isFunction(b5)){b5={}}if(b3===b4){b5=this;--b4}for(;b4<b3;b4++){if((b9=arguments[b4])!=null){for(b2 in b9){b0=b5[b2];b1=b9[b2];if(b5===b1){continue}if(b8&&b1&&(bF.isPlainObject(b1)||(b6=bF.isArray(b1)))){if(b6){b6=false;b7=b0&&bF.isArray(b0)?b0:[]}else{b7=b0&&bF.isPlainObject(b0)?b0:{}}b5[b2]=bF.extend(b8,b7,b1)}else{if(b1!==L){b5[b2]=b1}}}}}return b5};bF.extend({noConflict:function(b0){if(bd.$===bF){bd.$=bH}if(b0&&bd.jQuery===bF){bd.jQuery=bU}return bF},isReady:false,readyWait:1,holdReady:function(b0){if(b0){bF.readyWait++}else{bF.ready(true)}},ready:function(b0){if((b0===true&&!--bF.readyWait)||(b0!==true&&!bF.isReady)){if(!av.body){return setTimeout(bF.ready,1)}bF.isReady=true;if(b0!==true&&--bF.readyWait>0){return}bC.fireWith(av,[bF]);if(bF.fn.trigger){bF(av).trigger("ready").off("ready")}}},bindReady:function(){if(bC){return}bC=bF.Callbacks("once memory");if(av.readyState==="complete"){return setTimeout(bF.ready,1)}if(av.addEventListener){av.addEventListener("DOMContentLoaded",e,false);bd.addEventListener("load",bF.ready,false)}else{if(av.attachEvent){av.attachEvent("onreadystatechange",e);bd.attachEvent("onload",bF.ready);var b0=false;try{b0=bd.frameElement==null}catch(b1){}if(av.documentElement.doScroll&&b0){bw()}}}},isFunction:function(b0){return bF.type(b0)==="function"},isArray:Array.isArray||function(b0){return bF.type(b0)==="array"},isWindow:function(b0){return b0!=null&&b0==b0.window},isNumeric:function(b0){return !isNaN(parseFloat(b0))&&isFinite(b0)},type:function(b0){return b0==null?String(b0):bx[bL.call(b0)]||"object"},isPlainObject:function(b2){if(!b2||bF.type(b2)!=="object"||b2.nodeType||bF.isWindow(b2)){return false}try{if(b2.constructor&&!bG.call(b2,"constructor")&&!bG.call(b2.constructor.prototype,"isPrototypeOf")){return false}}catch(b1){return false}var b0;for(b0 in b2){}return b0===L||bG.call(b2,b0)},isEmptyObject:function(b1){for(var b0 in b1){return false}return true},error:function(b0){throw new Error(b0)},parseJSON:function(b0){if(typeof b0!=="string"||!b0){return null}b0=bF.trim(b0);if(bd.JSON&&bd.JSON.parse){return bd.JSON.parse(b0)}if(bN.test(b0.replace(bW,"@").replace(bP,"]").replace(bJ,""))){return(new Function("return "+b0))()}bF.error("Invalid JSON: "+b0)},parseXML:function(b2){if(typeof b2!=="string"||!b2){return null}var b0,b1;try{if(bd.DOMParser){b1=new DOMParser();b0=b1.parseFromString(b2,"text/xml")}else{b0=new ActiveXObject("Microsoft.XMLDOM");b0.async="false";b0.loadXML(b2)}}catch(b3){b0=L}if(!b0||!b0.documentElement||b0.getElementsByTagName("parsererror").length){bF.error("Invalid XML: "+b2)}return b0},noop:function(){},globalEval:function(b0){if(b0&&bM.test(b0)){(bd.execScript||function(b1){bd["eval"].call(bd,b1)})(b0)}},camelCase:function(b0){return b0.replace(bZ,"ms-").replace(bB,bT)},nodeName:function(b1,b0){return b1.nodeName&&b1.nodeName.toUpperCase()===b0.toUpperCase()},each:function(b3,b6,b2){var b1,b4=0,b5=b3.length,b0=b5===L||bF.isFunction(b3);if(b2){if(b0){for(b1 in b3){if(b6.apply(b3[b1],b2)===false){break}}}else{for(;b4<b5;){if(b6.apply(b3[b4++],b2)===false){break}}}}else{if(b0){for(b1 in b3){if(b6.call(b3[b1],b1,b3[b1])===false){break}}}else{for(;b4<b5;){if(b6.call(b3[b4],b4,b3[b4++])===false){break}}}}return b3},trim:bO?function(b0){return b0==null?"":bO.call(b0)}:function(b0){return b0==null?"":b0.toString().replace(bI,"").replace(bE,"")},makeArray:function(b3,b1){var b0=b1||[];if(b3!=null){var b2=bF.type(b3);if(b3.length==null||b2==="string"||b2==="function"||b2==="regexp"||bF.isWindow(b3)){bz.call(b0,b3)}else{bF.merge(b0,b3)}}return b0},inArray:function(b2,b3,b1){var b0;if(b3){if(bv){return bv.call(b3,b2,b1)}b0=b3.length;b1=b1?b1<0?Math.max(0,b0+b1):b1:0;for(;b1<b0;b1++){if(b1 in b3&&b3[b1]===b2){return b1}}}return -1},merge:function(b4,b2){var b3=b4.length,b1=0;if(typeof b2.length==="number"){for(var b0=b2.length;b1<b0;b1++){b4[b3++]=b2[b1]}}else{while(b2[b1]!==L){b4[b3++]=b2[b1++]}}b4.length=b3;return b4},grep:function(b1,b6,b0){var b2=[],b5;b0=!!b0;for(var b3=0,b4=b1.length;b3<b4;b3++){b5=!!b6(b1[b3],b3);if(b0!==b5){b2.push(b1[b3])}}return b2},map:function(b0,b7,b8){var b5,b6,b4=[],b2=0,b1=b0.length,b3=b0 instanceof bF||b1!==L&&typeof b1==="number"&&((b1>0&&b0[0]&&b0[b1-1])||b1===0||bF.isArray(b0));if(b3){for(;b2<b1;b2++){b5=b7(b0[b2],b2,b8);if(b5!=null){b4[b4.length]=b5}}}else{for(b6 in b0){b5=b7(b0[b6],b6,b8);if(b5!=null){b4[b4.length]=b5}}}return b4.concat.apply([],b4)},guid:1,proxy:function(b4,b3){if(typeof b3==="string"){var b2=b4[b3];b3=b4;b4=b2}if(!bF.isFunction(b4)){return L}var b0=bK.call(arguments,2),b1=function(){return b4.apply(b3,b0.concat(bK.call(arguments)))};b1.guid=b4.guid=b4.guid||b1.guid||bF.guid++;return b1},access:function(b0,b6,b9,b7,b4,ca,b8){var b2,b5=b9==null,b3=0,b1=b0.length;if(b9&&typeof b9==="object"){for(b3 in b9){bF.access(b0,b6,b3,b9[b3],1,ca,b7)}b4=1}else{if(b7!==L){b2=b8===L&&bF.isFunction(b7);if(b5){if(b2){b2=b6;b6=function(cc,cb,cd){return b2.call(bF(cc),cd)}}else{b6.call(b0,b7);b6=null}}if(b6){for(;b3<b1;b3++){b6(b0[b3],b9,b2?b7.call(b0[b3],b3,b6(b0[b3],b9)):b7,b8)}}b4=1}}return b4?b0:b5?b6.call(b0):b1?b6(b0[0],b9):ca},now:function(){return(new Date()).getTime()},uaMatch:function(b1){b1=b1.toLowerCase();var b0=by.exec(b1)||bR.exec(b1)||bQ.exec(b1)||b1.indexOf("compatible")<0&&bS.exec(b1)||[];return{browser:b0[1]||"",version:b0[2]||"0"}},sub:function(){function b0(b3,b4){return new b0.fn.init(b3,b4)}bF.extend(true,b0,this);b0.superclass=this;b0.fn=b0.prototype=this();b0.fn.constructor=b0;b0.sub=this.sub;b0.fn.init=function b2(b3,b4){if(b4&&b4 instanceof bF&&!(b4 instanceof b0)){b4=b0(b4)}return bF.fn.init.call(this,b3,b4,b1)};b0.fn.init.prototype=b0.fn;var b1=b0(av);return b0},browser:{}});bF.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(b1,b0){bx["[object "+b0+"]"]=b0.toLowerCase()});bV=bF.uaMatch(bX);if(bV.browser){bF.browser[bV.browser]=true;bF.browser.version=bV.version}if(bF.browser.webkit){bF.browser.safari=true}if(bM.test("\xA0")){bI=/^[\s\xA0]+/;bE=/[\s\xA0]+$/}bD=bF(av);if(av.addEventListener){e=function(){av.removeEventListener("DOMContentLoaded",e,false);bF.ready()}}else{if(av.attachEvent){e=function(){if(av.readyState==="complete"){av.detachEvent("onreadystatechange",e);bF.ready()}}}}function bw(){if(bF.isReady){return}try{av.documentElement.doScroll("left")}catch(b0){setTimeout(bw,1);return}bF.ready()}return bF})();var a3={};function X(e){var bv=a3[e]={},bw,bx;e=e.split(/\s+/);for(bw=0,bx=e.length;bw<bx;bw++){bv[e[bw]]=true}return bv}b.Callbacks=function(bx){bx=bx?(a3[bx]||X(bx)):{};var bC=[],bD=[],by,e,bz,bw,bA,bB,bF=function(bG){var bH,bK,bJ,bI,bL;for(bH=0,bK=bG.length;bH<bK;bH++){bJ=bG[bH];bI=b.type(bJ);if(bI==="array"){bF(bJ)}else{if(bI==="function"){if(!bx.unique||!bE.has(bJ)){bC.push(bJ)}}}}},bv=function(bH,bG){bG=bG||[];by=!bx.memory||[bH,bG];e=true;bz=true;bB=bw||0;bw=0;bA=bC.length;for(;bC&&bB<bA;bB++){if(bC[bB].apply(bH,bG)===false&&bx.stopOnFalse){by=true;break}}bz=false;if(bC){if(!bx.once){if(bD&&bD.length){by=bD.shift();bE.fireWith(by[0],by[1])}}else{if(by===true){bE.disable()}else{bC=[]}}}},bE={add:function(){if(bC){var bG=bC.length;bF(arguments);if(bz){bA=bC.length}else{if(by&&by!==true){bw=bG;bv(by[0],by[1])}}}return this},remove:function(){if(bC){var bG=arguments,bI=0,bJ=bG.length;for(;bI<bJ;bI++){for(var bH=0;bH<bC.length;bH++){if(bG[bI]===bC[bH]){if(bz){if(bH<=bA){bA--;if(bH<=bB){bB--}}}bC.splice(bH--,1);if(bx.unique){break}}}}}return this},has:function(bH){if(bC){var bG=0,bI=bC.length;for(;bG<bI;bG++){if(bH===bC[bG]){return true}}}return false},empty:function(){bC=[];return this},disable:function(){bC=bD=by=L;return this},disabled:function(){return !bC},lock:function(){bD=L;if(!by||by===true){bE.disable()}return this},locked:function(){return !bD},fireWith:function(bH,bG){if(bD){if(bz){if(!bx.once){bD.push([bH,bG])}}else{if(!(bx.once&&by)){bv(bH,bG)}}}return this},fire:function(){bE.fireWith(this,arguments);return this},fired:function(){return !!e}};return bE};var aK=[].slice;b.extend({Deferred:function(by){var bx=b.Callbacks("once memory"),bw=b.Callbacks("once memory"),bv=b.Callbacks("memory"),e="pending",bA={resolve:bx,reject:bw,notify:bv},bC={done:bx.add,fail:bw.add,progress:bv.add,state:function(){return e},isResolved:bx.fired,isRejected:bw.fired,then:function(bE,bD,bF){bB.done(bE).fail(bD).progress(bF);return this},always:function(){bB.done.apply(bB,arguments).fail.apply(bB,arguments);return this},pipe:function(bF,bE,bD){return b.Deferred(function(bG){b.each({done:[bF,"resolve"],fail:[bE,"reject"],progress:[bD,"notify"]},function(bI,bL){var bH=bL[0],bK=bL[1],bJ;if(b.isFunction(bH)){bB[bI](function(){bJ=bH.apply(this,arguments);if(bJ&&b.isFunction(bJ.promise)){bJ.promise().then(bG.resolve,bG.reject,bG.notify)}else{bG[bK+"With"](this===bB?bG:this,[bJ])}})}else{bB[bI](bG[bK])}})}).promise()},promise:function(bE){if(bE==null){bE=bC}else{for(var bD in bC){bE[bD]=bC[bD]}}return bE}},bB=bC.promise({}),bz;for(bz in bA){bB[bz]=bA[bz].fire;bB[bz+"With"]=bA[bz].fireWith}bB.done(function(){e="resolved"},bw.disable,bv.lock).fail(function(){e="rejected"},bx.disable,bv.lock);if(by){by.call(bB,bB)}return bB},when:function(bA){var bx=aK.call(arguments,0),bv=0,e=bx.length,bB=new Array(e),bw=e,by=e,bC=e<=1&&bA&&b.isFunction(bA.promise)?bA:b.Deferred(),bE=bC.promise();function bD(bF){return function(bG){bx[bF]=arguments.length>1?aK.call(arguments,0):bG;if(!(--bw)){bC.resolveWith(bC,bx)}}}function bz(bF){return function(bG){bB[bF]=arguments.length>1?aK.call(arguments,0):bG;bC.notifyWith(bE,bB)}}if(e>1){for(;bv<e;bv++){if(bx[bv]&&bx[bv].promise&&b.isFunction(bx[bv].promise)){bx[bv].promise().then(bD(bv),bC.reject,bz(bv))}else{--bw}}if(!bw){bC.resolveWith(bC,bx)}}else{if(bC!==bA){bC.resolveWith(bC,e?[bA]:[])}}return bE}});b.support=(function(){var bI,bH,bE,bF,bx,bD,bC,bz,bJ,bA,by,bw,bv=av.createElement("div"),bG=av.documentElement;bv.setAttribute("className","t");bv.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";bH=bv.getElementsByTagName("*");bE=bv.getElementsByTagName("a")[0];if(!bH||!bH.length||!bE){return{}}bF=av.createElement("select");bx=bF.appendChild(av.createElement("option"));bD=bv.getElementsByTagName("input")[0];bI={leadingWhitespace:(bv.firstChild.nodeType===3),tbody:!bv.getElementsByTagName("tbody").length,htmlSerialize:!!bv.getElementsByTagName("link").length,style:/top/.test(bE.getAttribute("style")),hrefNormalized:(bE.getAttribute("href")==="/a"),opacity:/^0.55/.test(bE.style.opacity),cssFloat:!!bE.style.cssFloat,checkOn:(bD.value==="on"),optSelected:bx.selected,getSetAttribute:bv.className!=="t",enctype:!!av.createElement("form").enctype,html5Clone:av.createElement("nav").cloneNode(true).outerHTML!=="<:nav></:nav>",submitBubbles:true,changeBubbles:true,focusinBubbles:false,deleteExpando:true,noCloneEvent:true,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableMarginRight:true,pixelMargin:true};b.boxModel=bI.boxModel=(av.compatMode==="CSS1Compat");bD.checked=true;bI.noCloneChecked=bD.cloneNode(true).checked;bF.disabled=true;bI.optDisabled=!bx.disabled;try{delete bv.test}catch(bB){bI.deleteExpando=false}if(!bv.addEventListener&&bv.attachEvent&&bv.fireEvent){bv.attachEvent("onclick",function(){bI.noCloneEvent=false});bv.cloneNode(true).fireEvent("onclick")}bD=av.createElement("input");bD.value="t";bD.setAttribute("type","radio");bI.radioValue=bD.value==="t";bD.setAttribute("checked","checked");bD.setAttribute("name","t");bv.appendChild(bD);bC=av.createDocumentFragment();bC.appendChild(bv.lastChild);bI.checkClone=bC.cloneNode(true).cloneNode(true).lastChild.checked;bI.appendChecked=bD.checked;bC.removeChild(bD);bC.appendChild(bv);if(bv.attachEvent){for(by in {submit:1,change:1,focusin:1}){bA="on"+by;bw=(bA in bv);if(!bw){bv.setAttribute(bA,"return;");bw=(typeof bv[bA]==="function")}bI[by+"Bubbles"]=bw}}bC.removeChild(bv);bC=bF=bx=bv=bD=null;b(function(){var bM,bV,bW,bU,bO,bP,bR,bL,bK,bQ,bN,e,bT,bS=av.getElementsByTagName("body")[0];if(!bS){return}bL=1;bT="padding:0;margin:0;border:";bN="position:absolute;top:0;left:0;width:1px;height:1px;";e=bT+"0;visibility:hidden;";bK="style='"+bN+bT+"5px solid #000;";bQ="<div "+bK+"display:block;'><div style='"+bT+"0;display:block;overflow:hidden;'></div></div><table "+bK+"' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";bM=av.createElement("div");bM.style.cssText=e+"width:0;height:0;position:static;top:0;margin-top:"+bL+"px";bS.insertBefore(bM,bS.firstChild);bv=av.createElement("div");bM.appendChild(bv);bv.innerHTML="<table><tr><td style='"+bT+"0;display:none'></td><td>t</td></tr></table>";bz=bv.getElementsByTagName("td");bw=(bz[0].offsetHeight===0);bz[0].style.display="";bz[1].style.display="none";bI.reliableHiddenOffsets=bw&&(bz[0].offsetHeight===0);if(bd.getComputedStyle){bv.innerHTML="";bR=av.createElement("div");bR.style.width="0";bR.style.marginRight="0";bv.style.width="2px";bv.appendChild(bR);bI.reliableMarginRight=(parseInt((bd.getComputedStyle(bR,null)||{marginRight:0}).marginRight,10)||0)===0}if(typeof bv.style.zoom!=="undefined"){bv.innerHTML="";bv.style.width=bv.style.padding="1px";bv.style.border=0;bv.style.overflow="hidden";bv.style.display="inline";bv.style.zoom=1;bI.inlineBlockNeedsLayout=(bv.offsetWidth===3);bv.style.display="block";bv.style.overflow="visible";bv.innerHTML="<div style='width:5px;'></div>";bI.shrinkWrapBlocks=(bv.offsetWidth!==3)}bv.style.cssText=bN+e;bv.innerHTML=bQ;bV=bv.firstChild;bW=bV.firstChild;bO=bV.nextSibling.firstChild.firstChild;bP={doesNotAddBorder:(bW.offsetTop!==5),doesAddBorderForTableAndCells:(bO.offsetTop===5)};bW.style.position="fixed";bW.style.top="20px";bP.fixedPosition=(bW.offsetTop===20||bW.offsetTop===15);bW.style.position=bW.style.top="";bV.style.overflow="hidden";bV.style.position="relative";bP.subtractsBorderForOverflowNotVisible=(bW.offsetTop===-5);bP.doesNotIncludeMarginInBodyOffset=(bS.offsetTop!==bL);if(bd.getComputedStyle){bv.style.marginTop="1%";bI.pixelMargin=(bd.getComputedStyle(bv,null)||{marginTop:0}).marginTop!=="1%"}if(typeof bM.style.zoom!=="undefined"){bM.style.zoom=1}bS.removeChild(bM);bR=bv=bM=null;b.extend(bI,bP)});return bI})();var aT=/^(?:\{.*\}|\[.*\])$/,aA=/([A-Z])/g;b.extend({cache:{},uuid:0,expando:"jQuery"+(b.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},hasData:function(e){e=e.nodeType?b.cache[e[b.expando]]:e[b.expando];return !!e&&!S(e)},data:function(bx,bv,bz,by){if(!b.acceptData(bx)){return}var bG,bA,bD,bE=b.expando,bC=typeof bv==="string",bF=bx.nodeType,e=bF?b.cache:bx,bw=bF?bx[bE]:bx[bE]&&bE,bB=bv==="events";if((!bw||!e[bw]||(!bB&&!by&&!e[bw].data))&&bC&&bz===L){return}if(!bw){if(bF){bx[bE]=bw=++b.uuid}else{bw=bE}}if(!e[bw]){e[bw]={};if(!bF){e[bw].toJSON=b.noop}}if(typeof bv==="object"||typeof bv==="function"){if(by){e[bw]=b.extend(e[bw],bv)}else{e[bw].data=b.extend(e[bw].data,bv)}}bG=bA=e[bw];if(!by){if(!bA.data){bA.data={}}bA=bA.data}if(bz!==L){bA[b.camelCase(bv)]=bz}if(bB&&!bA[bv]){return bG.events}if(bC){bD=bA[bv];if(bD==null){bD=bA[b.camelCase(bv)]}}else{bD=bA}return bD},removeData:function(bx,bv,by){if(!b.acceptData(bx)){return}var bB,bA,bz,bC=b.expando,bD=bx.nodeType,e=bD?b.cache:bx,bw=bD?bx[bC]:bC;if(!e[bw]){return}if(bv){bB=by?e[bw]:e[bw].data;if(bB){if(!b.isArray(bv)){if(bv in bB){bv=[bv]}else{bv=b.camelCase(bv);if(bv in bB){bv=[bv]}else{bv=bv.split(" ")}}}for(bA=0,bz=bv.length;bA<bz;bA++){delete bB[bv[bA]]}if(!(by?S:b.isEmptyObject)(bB)){return}}}if(!by){delete e[bw].data;if(!S(e[bw])){return}}if(b.support.deleteExpando||!e.setInterval){delete e[bw]}else{e[bw]=null}if(bD){if(b.support.deleteExpando){delete bx[bC]}else{if(bx.removeAttribute){bx.removeAttribute(bC)}else{bx[bC]=null}}}},_data:function(bv,e,bw){return b.data(bv,e,bw,true)},acceptData:function(bv){if(bv.nodeName){var e=b.noData[bv.nodeName.toLowerCase()];if(e){return !(e===true||bv.getAttribute("classid")!==e)}}return true}});b.fn.extend({data:function(bD,bC){var by,bv,bB,e,bx,bw=this[0],bA=0,bz=null;if(bD===L){if(this.length){bz=b.data(bw);if(bw.nodeType===1&&!b._data(bw,"parsedAttrs")){bB=bw.attributes;for(bx=bB.length;bA<bx;bA++){e=bB[bA].name;if(e.indexOf("data-")===0){e=b.camelCase(e.substring(5));a6(bw,e,bz[e])}}b._data(bw,"parsedAttrs",true)}}return bz}if(typeof bD==="object"){return this.each(function(){b.data(this,bD)})}by=bD.split(".",2);by[1]=by[1]?"."+by[1]:"";bv=by[1]+"!";return b.access(this,function(bE){if(bE===L){bz=this.triggerHandler("getData"+bv,[by[0]]);if(bz===L&&bw){bz=b.data(bw,bD);bz=a6(bw,bD,bz)}return bz===L&&by[1]?this.data(by[0]):bz}by[1]=bE;this.each(function(){var bF=b(this);bF.triggerHandler("setData"+bv,by);b.data(this,bD,bE);bF.triggerHandler("changeData"+bv,by)})},null,bC,arguments.length>1,null,false)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function a6(bx,bw,by){if(by===L&&bx.nodeType===1){var bv="data-"+bw.replace(aA,"-$1").toLowerCase();by=bx.getAttribute(bv);if(typeof by==="string"){try{by=by==="true"?true:by==="false"?false:by==="null"?null:b.isNumeric(by)?+by:aT.test(by)?b.parseJSON(by):by}catch(bz){}b.data(bx,bw,by)}else{by=L}}return by}function S(bv){for(var e in bv){if(e==="data"&&b.isEmptyObject(bv[e])){continue}if(e!=="toJSON"){return false}}return true}function bj(by,bx,bA){var bw=bx+"defer",bv=bx+"queue",e=bx+"mark",bz=b._data(by,bw);if(bz&&(bA==="queue"||!b._data(by,bv))&&(bA==="mark"||!b._data(by,e))){setTimeout(function(){if(!b._data(by,bv)&&!b._data(by,e)){b.removeData(by,bw,true);bz.fire()}},0)}}b.extend({_mark:function(bv,e){if(bv){e=(e||"fx")+"mark";b._data(bv,e,(b._data(bv,e)||0)+1)}},_unmark:function(by,bx,bv){if(by!==true){bv=bx;bx=by;by=false}if(bx){bv=bv||"fx";var e=bv+"mark",bw=by?0:((b._data(bx,e)||1)-1);if(bw){b._data(bx,e,bw)}else{b.removeData(bx,e,true);bj(bx,bv,"mark")}}},queue:function(bv,e,bx){var bw;if(bv){e=(e||"fx")+"queue";bw=b._data(bv,e);if(bx){if(!bw||b.isArray(bx)){bw=b._data(bv,e,b.makeArray(bx))}else{bw.push(bx)}}return bw||[]}},dequeue:function(by,bx){bx=bx||"fx";var bv=b.queue(by,bx),bw=bv.shift(),e={};if(bw==="inprogress"){bw=bv.shift()}if(bw){if(bx==="fx"){bv.unshift("inprogress")}b._data(by,bx+".run",e);bw.call(by,function(){b.dequeue(by,bx)},e)}if(!bv.length){b.removeData(by,bx+"queue "+bx+".run",true);bj(by,bx,"queue")}}});b.fn.extend({queue:function(e,bv){var bw=2;if(typeof e!=="string"){bv=e;e="fx";bw--}if(arguments.length<bw){return b.queue(this[0],e)}return bv===L?this:this.each(function(){var bx=b.queue(this,e,bv);if(e==="fx"&&bx[0]!=="inprogress"){b.dequeue(this,e)}})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(bv,e){bv=b.fx?b.fx.speeds[bv]||bv:bv;e=e||"fx";return this.queue(e,function(bx,bw){var by=setTimeout(bx,bv);bw.stop=function(){clearTimeout(by)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(bD,bw){if(typeof bD!=="string"){bw=bD;bD=L}bD=bD||"fx";var e=b.Deferred(),bv=this,by=bv.length,bB=1,bz=bD+"defer",bA=bD+"queue",bC=bD+"mark",bx;function bE(){if(!(--bB)){e.resolveWith(bv,[bv])}}while(by--){if((bx=b.data(bv[by],bz,L,true)||(b.data(bv[by],bA,L,true)||b.data(bv[by],bC,L,true))&&b.data(bv[by],bz,b.Callbacks("once memory"),true))){bB++;bx.add(bE)}}bE();return e.promise(bw)}});var aQ=/[\n\t\r]/g,ag=/\s+/,aV=/\r/g,g=/^(?:button|input)$/i,C=/^(?:button|input|object|select|textarea)$/i,l=/^a(?:rea)?$/i,ao=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,E=b.support.getSetAttribute,bf,aZ,aG;b.fn.extend({attr:function(e,bv){return b.access(this,b.attr,e,bv,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,bv){return b.access(this,b.prop,e,bv,arguments.length>1)},removeProp:function(e){e=b.propFix[e]||e;return this.each(function(){try{this[e]=L;delete this[e]}catch(bv){}})},addClass:function(by){var bA,bw,bv,bx,bz,bB,e;if(b.isFunction(by)){return this.each(function(bC){b(this).addClass(by.call(this,bC,this.className))})}if(by&&typeof by==="string"){bA=by.split(ag);for(bw=0,bv=this.length;bw<bv;bw++){bx=this[bw];if(bx.nodeType===1){if(!bx.className&&bA.length===1){bx.className=by}else{bz=" "+bx.className+" ";for(bB=0,e=bA.length;bB<e;bB++){if(!~bz.indexOf(" "+bA[bB]+" ")){bz+=bA[bB]+" "}}bx.className=b.trim(bz)}}}}return this},removeClass:function(bz){var bA,bw,bv,by,bx,bB,e;if(b.isFunction(bz)){return this.each(function(bC){b(this).removeClass(bz.call(this,bC,this.className))})}if((bz&&typeof bz==="string")||bz===L){bA=(bz||"").split(ag);for(bw=0,bv=this.length;bw<bv;bw++){by=this[bw];if(by.nodeType===1&&by.className){if(bz){bx=(" "+by.className+" ").replace(aQ," ");for(bB=0,e=bA.length;bB<e;bB++){bx=bx.replace(" "+bA[bB]+" "," ")}by.className=b.trim(bx)}else{by.className=""}}}}return this},toggleClass:function(bx,bv){var bw=typeof bx,e=typeof bv==="boolean";if(b.isFunction(bx)){return this.each(function(by){b(this).toggleClass(bx.call(this,by,this.className,bv),bv)})}return this.each(function(){if(bw==="string"){var bA,bz=0,by=b(this),bB=bv,bC=bx.split(ag);while((bA=bC[bz++])){bB=e?bB:!by.hasClass(bA);by[bB?"addClass":"removeClass"](bA)}}else{if(bw==="undefined"||bw==="boolean"){if(this.className){b._data(this,"__className__",this.className)}this.className=this.className||bx===false?"":b._data(this,"__className__")||""}}})},hasClass:function(e){var bx=" "+e+" ",bw=0,bv=this.length;for(;bw<bv;bw++){if(this[bw].nodeType===1&&(" "+this[bw].className+" ").replace(aQ," ").indexOf(bx)>-1){return true}}return false},val:function(bx){var e,bv,by,bw=this[0];if(!arguments.length){if(bw){e=b.valHooks[bw.type]||b.valHooks[bw.nodeName.toLowerCase()];if(e&&"get" in e&&(bv=e.get(bw,"value"))!==L){return bv}bv=bw.value;return typeof bv==="string"?bv.replace(aV,""):bv==null?"":bv}return}by=b.isFunction(bx);return this.each(function(bA){var bz=b(this),bB;if(this.nodeType!==1){return}if(by){bB=bx.call(this,bA,bz.val())}else{bB=bx}if(bB==null){bB=""}else{if(typeof bB==="number"){bB+=""}else{if(b.isArray(bB)){bB=b.map(bB,function(bC){return bC==null?"":bC+""})}}}e=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()];if(!e||!("set" in e)||e.set(this,bB,"value")===L){this.value=bB}})}});b.extend({valHooks:{option:{get:function(e){var bv=e.attributes.value;return !bv||bv.specified?e.value:e.text}},select:{get:function(e){var bA,bv,bz,bx,by=e.selectedIndex,bB=[],bC=e.options,bw=e.type==="select-one";if(by<0){return null}bv=bw?by:0;bz=bw?by+1:bC.length;for(;bv<bz;bv++){bx=bC[bv];if(bx.selected&&(b.support.optDisabled?!bx.disabled:bx.getAttribute("disabled")===null)&&(!bx.parentNode.disabled||!b.nodeName(bx.parentNode,"optgroup"))){bA=b(bx).val();if(bw){return bA}bB.push(bA)}}if(bw&&!bB.length&&bC.length){return b(bC[by]).val()}return bB},set:function(bv,bw){var e=b.makeArray(bw);b(bv).find("option").each(function(){this.selected=b.inArray(b(this).val(),e)>=0});if(!e.length){bv.selectedIndex=-1}return e}}},attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(bA,bx,bB,bz){var bw,e,by,bv=bA.nodeType;if(!bA||bv===3||bv===8||bv===2){return}if(bz&&bx in b.attrFn){return b(bA)[bx](bB)}if(typeof bA.getAttribute==="undefined"){return b.prop(bA,bx,bB)}by=bv!==1||!b.isXMLDoc(bA);if(by){bx=bx.toLowerCase();e=b.attrHooks[bx]||(ao.test(bx)?aZ:bf)}if(bB!==L){if(bB===null){b.removeAttr(bA,bx);return}else{if(e&&"set" in e&&by&&(bw=e.set(bA,bB,bx))!==L){return bw}else{bA.setAttribute(bx,""+bB);return bB}}}else{if(e&&"get" in e&&by&&(bw=e.get(bA,bx))!==null){return bw}else{bw=bA.getAttribute(bx);return bw===null?L:bw}}},removeAttr:function(by,bA){var bz,bB,bw,e,bv,bx=0;if(bA&&by.nodeType===1){bB=bA.toLowerCase().split(ag);e=bB.length;for(;bx<e;bx++){bw=bB[bx];if(bw){bz=b.propFix[bw]||bw;bv=ao.test(bw);if(!bv){b.attr(by,bw,"")}by.removeAttribute(E?bw:bz);if(bv&&bz in by){by[bz]=false}}}}},attrHooks:{type:{set:function(e,bv){if(g.test(e.nodeName)&&e.parentNode){b.error("type property can't be changed")}else{if(!b.support.radioValue&&bv==="radio"&&b.nodeName(e,"input")){var bw=e.value;e.setAttribute("type",bv);if(bw){e.value=bw}return bv}}}},value:{get:function(bv,e){if(bf&&b.nodeName(bv,"button")){return bf.get(bv,e)}return e in bv?bv.value:null},set:function(bv,bw,e){if(bf&&b.nodeName(bv,"button")){return bf.set(bv,bw,e)}bv.value=bw}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(bz,bx,bA){var bw,e,by,bv=bz.nodeType;if(!bz||bv===3||bv===8||bv===2){return}by=bv!==1||!b.isXMLDoc(bz);if(by){bx=b.propFix[bx]||bx;e=b.propHooks[bx]}if(bA!==L){if(e&&"set" in e&&(bw=e.set(bz,bA,bx))!==L){return bw}else{return(bz[bx]=bA)}}else{if(e&&"get" in e&&(bw=e.get(bz,bx))!==null){return bw}else{return bz[bx]}}},propHooks:{tabIndex:{get:function(bv){var e=bv.getAttributeNode("tabindex");return e&&e.specified?parseInt(e.value,10):C.test(bv.nodeName)||l.test(bv.nodeName)&&bv.href?0:L}}}});b.attrHooks.tabindex=b.propHooks.tabIndex;aZ={get:function(bv,e){var bx,bw=b.prop(bv,e);return bw===true||typeof bw!=="boolean"&&(bx=bv.getAttributeNode(e))&&bx.nodeValue!==false?e.toLowerCase():L},set:function(bv,bx,e){var bw;if(bx===false){b.removeAttr(bv,e)}else{bw=b.propFix[e]||e;if(bw in bv){bv[bw]=true}bv.setAttribute(e,e.toLowerCase())}return e}};if(!E){aG={name:true,id:true,coords:true};bf=b.valHooks.button={get:function(bw,bv){var e;e=bw.getAttributeNode(bv);return e&&(aG[bv]?e.nodeValue!=="":e.specified)?e.nodeValue:L},set:function(bw,bx,bv){var e=bw.getAttributeNode(bv);if(!e){e=av.createAttribute(bv);bw.setAttributeNode(e)}return(e.nodeValue=bx+"")}};b.attrHooks.tabindex.set=bf.set;b.each(["width","height"],function(bv,e){b.attrHooks[e]=b.extend(b.attrHooks[e],{set:function(bw,bx){if(bx===""){bw.setAttribute(e,"auto");return bx}}})});b.attrHooks.contenteditable={get:bf.get,set:function(bv,bw,e){if(bw===""){bw="false"}bf.set(bv,bw,e)}}}if(!b.support.hrefNormalized){b.each(["href","src","width","height"],function(bv,e){b.attrHooks[e]=b.extend(b.attrHooks[e],{get:function(bx){var bw=bx.getAttribute(e,2);return bw===null?L:bw}})})}if(!b.support.style){b.attrHooks.style={get:function(e){return e.style.cssText.toLowerCase()||L},set:function(e,bv){return(e.style.cssText=""+bv)}}}if(!b.support.optSelected){b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(bv){var e=bv.parentNode;if(e){e.selectedIndex;if(e.parentNode){e.parentNode.selectedIndex}}return null}})}if(!b.support.enctype){b.propFix.enctype="encoding"}if(!b.support.checkOn){b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return e.getAttribute("value")===null?"on":e.value}}})}b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,bv){if(b.isArray(bv)){return(e.checked=b.inArray(b(e).val(),bv)>=0)}}})});var be=/^(?:textarea|input|select)$/i,n=/^([^\.]*)?(?:\.(.+))?$/,J=/(?:^|\s)hover(\.\S+)?\b/,aP=/^key/,bg=/^(?:mouse|contextmenu)|click/,T=/^(?:focusinfocus|focusoutblur)$/,U=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,Y=function(e){var bv=U.exec(e);if(bv){bv[1]=(bv[1]||"").toLowerCase();bv[3]=bv[3]&&new RegExp("(?:^|\\s)"+bv[3]+"(?:\\s|$)")}return bv},j=function(bw,e){var bv=bw.attributes||{};return((!e[1]||bw.nodeName.toLowerCase()===e[1])&&(!e[2]||(bv.id||{}).value===e[2])&&(!e[3]||e[3].test((bv["class"]||{}).value)))},bt=function(e){return b.event.special.hover?e:e.replace(J,"mouseenter$1 mouseleave$1")};b.event={add:function(bx,bC,bJ,bA,by){var bD,bB,bK,bI,bH,bF,e,bG,bv,bz,bw,bE;if(bx.nodeType===3||bx.nodeType===8||!bC||!bJ||!(bD=b._data(bx))){return}if(bJ.handler){bv=bJ;bJ=bv.handler;by=bv.selector}if(!bJ.guid){bJ.guid=b.guid++}bK=bD.events;if(!bK){bD.events=bK={}}bB=bD.handle;if(!bB){bD.handle=bB=function(bL){return typeof b!=="undefined"&&(!bL||b.event.triggered!==bL.type)?b.event.dispatch.apply(bB.elem,arguments):L};bB.elem=bx}bC=b.trim(bt(bC)).split(" ");for(bI=0;bI<bC.length;bI++){bH=n.exec(bC[bI])||[];bF=bH[1];e=(bH[2]||"").split(".").sort();bE=b.event.special[bF]||{};bF=(by?bE.delegateType:bE.bindType)||bF;bE=b.event.special[bF]||{};bG=b.extend({type:bF,origType:bH[1],data:bA,handler:bJ,guid:bJ.guid,selector:by,quick:by&&Y(by),namespace:e.join(".")},bv);bw=bK[bF];if(!bw){bw=bK[bF]=[];bw.delegateCount=0;if(!bE.setup||bE.setup.call(bx,bA,e,bB)===false){if(bx.addEventListener){bx.addEventListener(bF,bB,false)}else{if(bx.attachEvent){bx.attachEvent("on"+bF,bB)}}}}if(bE.add){bE.add.call(bx,bG);if(!bG.handler.guid){bG.handler.guid=bJ.guid}}if(by){bw.splice(bw.delegateCount++,0,bG)}else{bw.push(bG)}b.event.global[bF]=true}bx=null},global:{},remove:function(bJ,bE,bv,bH,bB){var bI=b.hasData(bJ)&&b._data(bJ),bF,bx,bz,bL,bC,bA,bG,bw,by,bK,bD,e;if(!bI||!(bw=bI.events)){return}bE=b.trim(bt(bE||"")).split(" ");for(bF=0;bF<bE.length;bF++){bx=n.exec(bE[bF])||[];bz=bL=bx[1];bC=bx[2];if(!bz){for(bz in bw){b.event.remove(bJ,bz+bE[bF],bv,bH,true)}continue}by=b.event.special[bz]||{};bz=(bH?by.delegateType:by.bindType)||bz;bD=bw[bz]||[];bA=bD.length;bC=bC?new RegExp("(^|\\.)"+bC.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(bG=0;bG<bD.length;bG++){e=bD[bG];if((bB||bL===e.origType)&&(!bv||bv.guid===e.guid)&&(!bC||bC.test(e.namespace))&&(!bH||bH===e.selector||bH==="**"&&e.selector)){bD.splice(bG--,1);if(e.selector){bD.delegateCount--}if(by.remove){by.remove.call(bJ,e)}}}if(bD.length===0&&bA!==bD.length){if(!by.teardown||by.teardown.call(bJ,bC)===false){b.removeEvent(bJ,bz,bI.handle)}delete bw[bz]}}if(b.isEmptyObject(bw)){bK=bI.handle;if(bK){bK.elem=null}b.removeData(bJ,["events","handle"],true)}},customEvent:{getData:true,setData:true,changeData:true},trigger:function(bv,bD,bA,bJ){if(bA&&(bA.nodeType===3||bA.nodeType===8)){return}var bG=bv.type||bv,bx=[],e,bw,bC,bH,bz,by,bF,bE,bB,bI;if(T.test(bG+b.event.triggered)){return}if(bG.indexOf("!")>=0){bG=bG.slice(0,-1);bw=true}if(bG.indexOf(".")>=0){bx=bG.split(".");bG=bx.shift();bx.sort()}if((!bA||b.event.customEvent[bG])&&!b.event.global[bG]){return}bv=typeof bv==="object"?bv[b.expando]?bv:new b.Event(bG,bv):new b.Event(bG);bv.type=bG;bv.isTrigger=true;bv.exclusive=bw;bv.namespace=bx.join(".");bv.namespace_re=bv.namespace?new RegExp("(^|\\.)"+bx.join("\\.(?:.*\\.)?")+"(\\.|$)"):null;by=bG.indexOf(":")<0?"on"+bG:"";if(!bA){e=b.cache;for(bC in e){if(e[bC].events&&e[bC].events[bG]){b.event.trigger(bv,bD,e[bC].handle.elem,true)}}return}bv.result=L;if(!bv.target){bv.target=bA}bD=bD!=null?b.makeArray(bD):[];bD.unshift(bv);bF=b.event.special[bG]||{};if(bF.trigger&&bF.trigger.apply(bA,bD)===false){return}bB=[[bA,bF.bindType||bG]];if(!bJ&&!bF.noBubble&&!b.isWindow(bA)){bI=bF.delegateType||bG;bH=T.test(bI+bG)?bA:bA.parentNode;bz=null;for(;bH;bH=bH.parentNode){bB.push([bH,bI]);bz=bH}if(bz&&bz===bA.ownerDocument){bB.push([bz.defaultView||bz.parentWindow||bd,bI])}}for(bC=0;bC<bB.length&&!bv.isPropagationStopped();bC++){bH=bB[bC][0];bv.type=bB[bC][1];bE=(b._data(bH,"events")||{})[bv.type]&&b._data(bH,"handle");if(bE){bE.apply(bH,bD)}bE=by&&bH[by];if(bE&&b.acceptData(bH)&&bE.apply(bH,bD)===false){bv.preventDefault()}}bv.type=bG;if(!bJ&&!bv.isDefaultPrevented()){if((!bF._default||bF._default.apply(bA.ownerDocument,bD)===false)&&!(bG==="click"&&b.nodeName(bA,"a"))&&b.acceptData(bA)){if(by&&bA[bG]&&((bG!=="focus"&&bG!=="blur")||bv.target.offsetWidth!==0)&&!b.isWindow(bA)){bz=bA[by];if(bz){bA[by]=null}b.event.triggered=bG;bA[bG]();b.event.triggered=L;if(bz){bA[by]=bz}}}}return bv.result},dispatch:function(bH){bH=b.event.fix(bH||bd.event);var bD=((b._data(this,"events")||{})[bH.type]||[]),bC=bD.delegateCount,bx=[].slice.call(arguments,0),bE=!bH.exclusive&&!bH.namespace,bz=b.event.special[bH.type]||{},bv=[],bJ,bG,by,bA,bK,bI,bB,bw,e,bF,bL;bx[0]=bH;bH.delegateTarget=this;if(bz.preDispatch&&bz.preDispatch.call(this,bH)===false){return}if(bC&&!(bH.button&&bH.type==="click")){bA=b(this);bA.context=this.ownerDocument||this;for(by=bH.target;by!=this;by=by.parentNode||this){if(by.disabled!==true){bI={};bw=[];bA[0]=by;for(bJ=0;bJ<bC;bJ++){e=bD[bJ];bF=e.selector;if(bI[bF]===L){bI[bF]=(e.quick?j(by,e.quick):bA.is(bF))}if(bI[bF]){bw.push(e)}}if(bw.length){bv.push({elem:by,matches:bw})}}}}if(bD.length>bC){bv.push({elem:this,matches:bD.slice(bC)})}for(bJ=0;bJ<bv.length&&!bH.isPropagationStopped();bJ++){bB=bv[bJ];bH.currentTarget=bB.elem;for(bG=0;bG<bB.matches.length&&!bH.isImmediatePropagationStopped();bG++){e=bB.matches[bG];if(bE||(!bH.namespace&&!e.namespace)||bH.namespace_re&&bH.namespace_re.test(e.namespace)){bH.data=e.data;bH.handleObj=e;bK=((b.event.special[e.origType]||{}).handle||e.handler).apply(bB.elem,bx);if(bK!==L){bH.result=bK;if(bK===false){bH.preventDefault();bH.stopPropagation()}}}}}if(bz.postDispatch){bz.postDispatch.call(this,bH)}return bH.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(bv,e){if(bv.which==null){bv.which=e.charCode!=null?e.charCode:e.keyCode}return bv}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(bx,bw){var by,bz,e,bv=bw.button,bA=bw.fromElement;if(bx.pageX==null&&bw.clientX!=null){by=bx.target.ownerDocument||av;bz=by.documentElement;e=by.body;bx.pageX=bw.clientX+(bz&&bz.scrollLeft||e&&e.scrollLeft||0)-(bz&&bz.clientLeft||e&&e.clientLeft||0);bx.pageY=bw.clientY+(bz&&bz.scrollTop||e&&e.scrollTop||0)-(bz&&bz.clientTop||e&&e.clientTop||0)}if(!bx.relatedTarget&&bA){bx.relatedTarget=bA===bx.target?bw.toElement:bA}if(!bx.which&&bv!==L){bx.which=(bv&1?1:(bv&2?3:(bv&4?2:0)))}return bx}},fix:function(bw){if(bw[b.expando]){return bw}var bv,bz,e=bw,bx=b.event.fixHooks[bw.type]||{},by=bx.props?this.props.concat(bx.props):this.props;bw=b.Event(e);for(bv=by.length;bv;){bz=by[--bv];bw[bz]=e[bz]}if(!bw.target){bw.target=e.srcElement||av}if(bw.target.nodeType===3){bw.target=bw.target.parentNode}if(bw.metaKey===L){bw.metaKey=bw.ctrlKey}return bx.filter?bx.filter(bw,e):bw},special:{ready:{setup:b.bindReady},load:{noBubble:true},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(bw,bv,e){if(b.isWindow(this)){this.onbeforeunload=e}},teardown:function(bv,e){if(this.onbeforeunload===e){this.onbeforeunload=null}}}},simulate:function(bw,by,bx,bv){var bz=b.extend(new b.Event(),bx,{type:bw,isSimulated:true,originalEvent:{}});if(bv){b.event.trigger(bz,null,by)}else{b.event.dispatch.call(by,bz)}if(bz.isDefaultPrevented()){bx.preventDefault()}}};b.event.handle=b.event.dispatch;b.removeEvent=av.removeEventListener?function(bv,e,bw){if(bv.removeEventListener){bv.removeEventListener(e,bw,false)}}:function(bv,e,bw){if(bv.detachEvent){bv.detachEvent("on"+e,bw)}};b.Event=function(bv,e){if(!(this instanceof b.Event)){return new b.Event(bv,e)}if(bv&&bv.type){this.originalEvent=bv;this.type=bv.type;this.isDefaultPrevented=(bv.defaultPrevented||bv.returnValue===false||bv.getPreventDefault&&bv.getPreventDefault())?i:bl}else{this.type=bv}if(e){b.extend(this,e)}this.timeStamp=bv&&bv.timeStamp||b.now();this[b.expando]=true};function bl(){return false}function i(){return true}b.Event.prototype={preventDefault:function(){this.isDefaultPrevented=i;var bv=this.originalEvent;if(!bv){return}if(bv.preventDefault){bv.preventDefault()}else{bv.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=i;var bv=this.originalEvent;if(!bv){return}if(bv.stopPropagation){bv.stopPropagation()}bv.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=i;this.stopPropagation()},isDefaultPrevented:bl,isPropagationStopped:bl,isImmediatePropagationStopped:bl};b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(bv,e){b.event.special[bv]={delegateType:e,bindType:e,handle:function(bz){var bB=this,bA=bz.relatedTarget,by=bz.handleObj,bw=by.selector,bx;if(!bA||(bA!==bB&&!b.contains(bB,bA))){bz.type=by.origType;bx=by.handler.apply(this,arguments);bz.type=e}return bx}}});if(!b.support.submitBubbles){b.event.special.submit={setup:function(){if(b.nodeName(this,"form")){return false}b.event.add(this,"click._submit keypress._submit",function(bx){var bw=bx.target,bv=b.nodeName(bw,"input")||b.nodeName(bw,"button")?bw.form:L;if(bv&&!bv._submit_attached){b.event.add(bv,"submit._submit",function(e){e._submit_bubble=true});bv._submit_attached=true}})},postDispatch:function(e){if(e._submit_bubble){delete e._submit_bubble;if(this.parentNode&&!e.isTrigger){b.event.simulate("submit",this.parentNode,e,true)}}},teardown:function(){if(b.nodeName(this,"form")){return false}b.event.remove(this,"._submit")}}}if(!b.support.changeBubbles){b.event.special.change={setup:function(){if(be.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio"){b.event.add(this,"propertychange._change",function(e){if(e.originalEvent.propertyName==="checked"){this._just_changed=true}});b.event.add(this,"click._change",function(e){if(this._just_changed&&!e.isTrigger){this._just_changed=false;b.event.simulate("change",this,e,true)}})}return false}b.event.add(this,"beforeactivate._change",function(bw){var bv=bw.target;if(be.test(bv.nodeName)&&!bv._change_attached){b.event.add(bv,"change._change",function(e){if(this.parentNode&&!e.isSimulated&&!e.isTrigger){b.event.simulate("change",this.parentNode,e,true)}});bv._change_attached=true}})},handle:function(bv){var e=bv.target;if(this!==e||bv.isSimulated||bv.isTrigger||(e.type!=="radio"&&e.type!=="checkbox")){return bv.handleObj.handler.apply(this,arguments)}},teardown:function(){b.event.remove(this,"._change");return be.test(this.nodeName)}}}if(!b.support.focusinBubbles){b.each({focus:"focusin",blur:"focusout"},function(bx,e){var bv=0,bw=function(by){b.event.simulate(e,by.target,b.event.fix(by),true)};b.event.special[e]={setup:function(){if(bv++===0){av.addEventListener(bx,bw,true)}},teardown:function(){if(--bv===0){av.removeEventListener(bx,bw,true)}}}})}b.fn.extend({on:function(bw,e,bz,by,bv){var bA,bx;if(typeof bw==="object"){if(typeof e!=="string"){bz=bz||e;e=L}for(bx in bw){this.on(bx,e,bz,bw[bx],bv)}return this}if(bz==null&&by==null){by=e;bz=e=L}else{if(by==null){if(typeof e==="string"){by=bz;bz=L}else{by=bz;bz=e;e=L}}}if(by===false){by=bl}else{if(!by){return this}}if(bv===1){bA=by;by=function(bB){b().off(bB);return bA.apply(this,arguments)};by.guid=bA.guid||(bA.guid=b.guid++)}return this.each(function(){b.event.add(this,bw,by,bz,e)})},one:function(bv,e,bx,bw){return this.on(bv,e,bx,bw,1)},off:function(bw,e,by){if(bw&&bw.preventDefault&&bw.handleObj){var bv=bw.handleObj;b(bw.delegateTarget).off(bv.namespace?bv.origType+"."+bv.namespace:bv.origType,bv.selector,bv.handler);return this}if(typeof bw==="object"){for(var bx in bw){this.off(bx,e,bw[bx])}return this}if(e===false||typeof e==="function"){by=e;e=L}if(by===false){by=bl}return this.each(function(){b.event.remove(this,bw,by,e)})},bind:function(e,bw,bv){return this.on(e,null,bw,bv)},unbind:function(e,bv){return this.off(e,null,bv)},live:function(e,bw,bv){b(this.context).on(e,this.selector,bw,bv);return this},die:function(e,bv){b(this.context).off(e,this.selector||"**",bv);return this},delegate:function(e,bv,bx,bw){return this.on(bv,e,bx,bw)},undelegate:function(e,bv,bw){return arguments.length==1?this.off(e,"**"):this.off(bv,e,bw)},trigger:function(e,bv){return this.each(function(){b.event.trigger(e,bv,this)})},triggerHandler:function(e,bv){if(this[0]){return b.event.trigger(e,bv,this[0],true)}},toggle:function(bx){var bv=arguments,e=bx.guid||b.guid++,bw=0,by=function(bz){var bA=(b._data(this,"lastToggle"+bx.guid)||0)%bw;b._data(this,"lastToggle"+bx.guid,bA+1);bz.preventDefault();return bv[bA].apply(this,arguments)||false};by.guid=e;while(bw<bv.length){bv[bw++].guid=e}return this.click(by)},hover:function(e,bv){return this.mouseenter(e).mouseleave(bv||e)}});b.each(("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu").split(" "),function(bv,e){b.fn[e]=function(bx,bw){if(bw==null){bw=bx;bx=null}return arguments.length>0?this.on(e,null,bx,bw):this.trigger(e)};if(b.attrFn){b.attrFn[e]=true}if(aP.test(e)){b.event.fixHooks[e]=b.event.keyHooks}if(bg.test(e)){b.event.fixHooks[e]=b.event.mouseHooks}});
+/*!
+ * Sizzle CSS Selector Engine
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){var bH=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,bC="sizcache"+(Math.random()+"").replace(".",""),bI=0,bL=Object.prototype.toString,bB=false,bA=true,bK=/\\/g,bO=/\r\n/g,bQ=/\W/;[0,0].sort(function(){bA=false;return 0});var by=function(bV,e,bY,bZ){bY=bY||[];e=e||av;var b1=e;if(e.nodeType!==1&&e.nodeType!==9){return[]}if(!bV||typeof bV!=="string"){return bY}var bS,b3,b6,bR,b2,b5,b4,bX,bU=true,bT=by.isXML(e),bW=[],b0=bV;do{bH.exec("");bS=bH.exec(b0);if(bS){b0=bS[3];bW.push(bS[1]);if(bS[2]){bR=bS[3];break}}}while(bS);if(bW.length>1&&bD.exec(bV)){if(bW.length===2&&bE.relative[bW[0]]){b3=bM(bW[0]+bW[1],e,bZ)}else{b3=bE.relative[bW[0]]?[e]:by(bW.shift(),e);while(bW.length){bV=bW.shift();if(bE.relative[bV]){bV+=bW.shift()}b3=bM(bV,b3,bZ)}}}else{if(!bZ&&bW.length>1&&e.nodeType===9&&!bT&&bE.match.ID.test(bW[0])&&!bE.match.ID.test(bW[bW.length-1])){b2=by.find(bW.shift(),e,bT);e=b2.expr?by.filter(b2.expr,b2.set)[0]:b2.set[0]}if(e){b2=bZ?{expr:bW.pop(),set:bF(bZ)}:by.find(bW.pop(),bW.length===1&&(bW[0]==="~"||bW[0]==="+")&&e.parentNode?e.parentNode:e,bT);b3=b2.expr?by.filter(b2.expr,b2.set):b2.set;if(bW.length>0){b6=bF(b3)}else{bU=false}while(bW.length){b5=bW.pop();b4=b5;if(!bE.relative[b5]){b5=""}else{b4=bW.pop()}if(b4==null){b4=e}bE.relative[b5](b6,b4,bT)}}else{b6=bW=[]}}if(!b6){b6=b3}if(!b6){by.error(b5||bV)}if(bL.call(b6)==="[object Array]"){if(!bU){bY.push.apply(bY,b6)}else{if(e&&e.nodeType===1){for(bX=0;b6[bX]!=null;bX++){if(b6[bX]&&(b6[bX]===true||b6[bX].nodeType===1&&by.contains(e,b6[bX]))){bY.push(b3[bX])}}}else{for(bX=0;b6[bX]!=null;bX++){if(b6[bX]&&b6[bX].nodeType===1){bY.push(b3[bX])}}}}}else{bF(b6,bY)}if(bR){by(bR,b1,bY,bZ);by.uniqueSort(bY)}return bY};by.uniqueSort=function(bR){if(bJ){bB=bA;bR.sort(bJ);if(bB){for(var e=1;e<bR.length;e++){if(bR[e]===bR[e-1]){bR.splice(e--,1)}}}}return bR};by.matches=function(e,bR){return by(e,null,null,bR)};by.matchesSelector=function(e,bR){return by(bR,null,null,[e]).length>0};by.find=function(bX,e,bY){var bW,bS,bU,bT,bV,bR;if(!bX){return[]}for(bS=0,bU=bE.order.length;bS<bU;bS++){bV=bE.order[bS];if((bT=bE.leftMatch[bV].exec(bX))){bR=bT[1];bT.splice(1,1);if(bR.substr(bR.length-1)!=="\\"){bT[1]=(bT[1]||"").replace(bK,"");bW=bE.find[bV](bT,e,bY);if(bW!=null){bX=bX.replace(bE.match[bV],"");break}}}}if(!bW){bW=typeof e.getElementsByTagName!=="undefined"?e.getElementsByTagName("*"):[]}return{set:bW,expr:bX}};by.filter=function(b1,b0,b4,bU){var bW,e,bZ,b6,b3,bR,bT,bV,b2,bS=b1,b5=[],bY=b0,bX=b0&&b0[0]&&by.isXML(b0[0]);while(b1&&b0.length){for(bZ in bE.filter){if((bW=bE.leftMatch[bZ].exec(b1))!=null&&bW[2]){bR=bE.filter[bZ];bT=bW[1];e=false;bW.splice(1,1);if(bT.substr(bT.length-1)==="\\"){continue}if(bY===b5){b5=[]}if(bE.preFilter[bZ]){bW=bE.preFilter[bZ](bW,bY,b4,b5,bU,bX);if(!bW){e=b6=true}else{if(bW===true){continue}}}if(bW){for(bV=0;(b3=bY[bV])!=null;bV++){if(b3){b6=bR(b3,bW,bV,bY);b2=bU^b6;if(b4&&b6!=null){if(b2){e=true}else{bY[bV]=false}}else{if(b2){b5.push(b3);e=true}}}}}if(b6!==L){if(!b4){bY=b5}b1=b1.replace(bE.match[bZ],"");if(!e){return[]}break}}}if(b1===bS){if(e==null){by.error(b1)}else{break}}bS=b1}return bY};by.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)};var bw=by.getText=function(bU){var bS,bT,e=bU.nodeType,bR="";if(e){if(e===1||e===9||e===11){if(typeof bU.textContent==="string"){return bU.textContent}else{if(typeof bU.innerText==="string"){return bU.innerText.replace(bO,"")}else{for(bU=bU.firstChild;bU;bU=bU.nextSibling){bR+=bw(bU)}}}}else{if(e===3||e===4){return bU.nodeValue}}}else{for(bS=0;(bT=bU[bS]);bS++){if(bT.nodeType!==8){bR+=bw(bT)}}}return bR};var bE=by.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(e){return e.getAttribute("href")},type:function(e){return e.getAttribute("type")}},relative:{"+":function(bW,bR){var bT=typeof bR==="string",bV=bT&&!bQ.test(bR),bX=bT&&!bV;if(bV){bR=bR.toLowerCase()}for(var bS=0,e=bW.length,bU;bS<e;bS++){if((bU=bW[bS])){while((bU=bU.previousSibling)&&bU.nodeType!==1){}bW[bS]=bX||bU&&bU.nodeName.toLowerCase()===bR?bU||false:bU===bR}}if(bX){by.filter(bR,bW,true)}},">":function(bW,bR){var bV,bU=typeof bR==="string",bS=0,e=bW.length;if(bU&&!bQ.test(bR)){bR=bR.toLowerCase();for(;bS<e;bS++){bV=bW[bS];if(bV){var bT=bV.parentNode;bW[bS]=bT.nodeName.toLowerCase()===bR?bT:false}}}else{for(;bS<e;bS++){bV=bW[bS];if(bV){bW[bS]=bU?bV.parentNode:bV.parentNode===bR}}if(bU){by.filter(bR,bW,true)}}},"":function(bT,bR,bV){var bU,bS=bI++,e=bN;if(typeof bR==="string"&&!bQ.test(bR)){bR=bR.toLowerCase();bU=bR;e=bv}e("parentNode",bR,bS,bT,bU,bV)},"~":function(bT,bR,bV){var bU,bS=bI++,e=bN;if(typeof bR==="string"&&!bQ.test(bR)){bR=bR.toLowerCase();bU=bR;e=bv}e("previousSibling",bR,bS,bT,bU,bV)}},find:{ID:function(bR,bS,bT){if(typeof bS.getElementById!=="undefined"&&!bT){var e=bS.getElementById(bR[1]);return e&&e.parentNode?[e]:[]}},NAME:function(bS,bV){if(typeof bV.getElementsByName!=="undefined"){var bR=[],bU=bV.getElementsByName(bS[1]);for(var bT=0,e=bU.length;bT<e;bT++){if(bU[bT].getAttribute("name")===bS[1]){bR.push(bU[bT])}}return bR.length===0?null:bR}},TAG:function(e,bR){if(typeof bR.getElementsByTagName!=="undefined"){return bR.getElementsByTagName(e[1])}}},preFilter:{CLASS:function(bT,bR,bS,e,bW,bX){bT=" "+bT[1].replace(bK,"")+" ";if(bX){return bT}for(var bU=0,bV;(bV=bR[bU])!=null;bU++){if(bV){if(bW^(bV.className&&(" "+bV.className+" ").replace(/[\t\n\r]/g," ").indexOf(bT)>=0)){if(!bS){e.push(bV)}}else{if(bS){bR[bU]=false}}}}return false},ID:function(e){return e[1].replace(bK,"")},TAG:function(bR,e){return bR[1].replace(bK,"").toLowerCase()},CHILD:function(e){if(e[1]==="nth"){if(!e[2]){by.error(e[0])}e[2]=e[2].replace(/^\+|\s*/g,"");var bR=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(e[2]==="even"&&"2n"||e[2]==="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=(bR[1]+(bR[2]||1))-0;e[3]=bR[3]-0}else{if(e[2]){by.error(e[0])}}e[0]=bI++;return e},ATTR:function(bU,bR,bS,e,bV,bW){var bT=bU[1]=bU[1].replace(bK,"");if(!bW&&bE.attrMap[bT]){bU[1]=bE.attrMap[bT]}bU[4]=(bU[4]||bU[5]||"").replace(bK,"");if(bU[2]==="~="){bU[4]=" "+bU[4]+" "}return bU},PSEUDO:function(bU,bR,bS,e,bV){if(bU[1]==="not"){if((bH.exec(bU[3])||"").length>1||/^\w/.test(bU[3])){bU[3]=by(bU[3],null,null,bR)}else{var bT=by.filter(bU[3],bR,bS,true^bV);if(!bS){e.push.apply(e,bT)}return false}}else{if(bE.match.POS.test(bU[0])||bE.match.CHILD.test(bU[0])){return true}}return bU},POS:function(e){e.unshift(true);return e}},filters:{enabled:function(e){return e.disabled===false&&e.type!=="hidden"},disabled:function(e){return e.disabled===true},checked:function(e){return e.checked===true},selected:function(e){if(e.parentNode){e.parentNode.selectedIndex}return e.selected===true},parent:function(e){return !!e.firstChild},empty:function(e){return !e.firstChild},has:function(bS,bR,e){return !!by(e[3],bS).length},header:function(e){return(/h\d/i).test(e.nodeName)},text:function(bS){var e=bS.getAttribute("type"),bR=bS.type;return bS.nodeName.toLowerCase()==="input"&&"text"===bR&&(e===bR||e===null)},radio:function(e){return e.nodeName.toLowerCase()==="input"&&"radio"===e.type},checkbox:function(e){return e.nodeName.toLowerCase()==="input"&&"checkbox"===e.type},file:function(e){return e.nodeName.toLowerCase()==="input"&&"file"===e.type},password:function(e){return e.nodeName.toLowerCase()==="input"&&"password"===e.type},submit:function(bR){var e=bR.nodeName.toLowerCase();return(e==="input"||e==="button")&&"submit"===bR.type},image:function(e){return e.nodeName.toLowerCase()==="input"&&"image"===e.type},reset:function(bR){var e=bR.nodeName.toLowerCase();return(e==="input"||e==="button")&&"reset"===bR.type},button:function(bR){var e=bR.nodeName.toLowerCase();return e==="input"&&"button"===bR.type||e==="button"},input:function(e){return(/input|select|textarea|button/i).test(e.nodeName)},focus:function(e){return e===e.ownerDocument.activeElement}},setFilters:{first:function(bR,e){return e===0},last:function(bS,bR,e,bT){return bR===bT.length-1},even:function(bR,e){return e%2===0},odd:function(bR,e){return e%2===1},lt:function(bS,bR,e){return bR<e[3]-0},gt:function(bS,bR,e){return bR>e[3]-0},nth:function(bS,bR,e){return e[3]-0===bR},eq:function(bS,bR,e){return e[3]-0===bR}},filter:{PSEUDO:function(bS,bX,bW,bY){var e=bX[1],bR=bE.filters[e];if(bR){return bR(bS,bW,bX,bY)}else{if(e==="contains"){return(bS.textContent||bS.innerText||bw([bS])||"").indexOf(bX[3])>=0}else{if(e==="not"){var bT=bX[3];for(var bV=0,bU=bT.length;bV<bU;bV++){if(bT[bV]===bS){return false}}return true}else{by.error(e)}}}},CHILD:function(bS,bU){var bT,b0,bW,bZ,e,bV,bY,bX=bU[1],bR=bS;switch(bX){case"only":case"first":while((bR=bR.previousSibling)){if(bR.nodeType===1){return false}}if(bX==="first"){return true}bR=bS;case"last":while((bR=bR.nextSibling)){if(bR.nodeType===1){return false}}return true;case"nth":bT=bU[2];b0=bU[3];if(bT===1&&b0===0){return true}bW=bU[0];bZ=bS.parentNode;if(bZ&&(bZ[bC]!==bW||!bS.nodeIndex)){bV=0;for(bR=bZ.firstChild;bR;bR=bR.nextSibling){if(bR.nodeType===1){bR.nodeIndex=++bV}}bZ[bC]=bW}bY=bS.nodeIndex-b0;if(bT===0){return bY===0}else{return(bY%bT===0&&bY/bT>=0)}}},ID:function(bR,e){return bR.nodeType===1&&bR.getAttribute("id")===e},TAG:function(bR,e){return(e==="*"&&bR.nodeType===1)||!!bR.nodeName&&bR.nodeName.toLowerCase()===e},CLASS:function(bR,e){return(" "+(bR.className||bR.getAttribute("class"))+" ").indexOf(e)>-1},ATTR:function(bV,bT){var bS=bT[1],e=by.attr?by.attr(bV,bS):bE.attrHandle[bS]?bE.attrHandle[bS](bV):bV[bS]!=null?bV[bS]:bV.getAttribute(bS),bW=e+"",bU=bT[2],bR=bT[4];return e==null?bU==="!=":!bU&&by.attr?e!=null:bU==="="?bW===bR:bU==="*="?bW.indexOf(bR)>=0:bU==="~="?(" "+bW+" ").indexOf(bR)>=0:!bR?bW&&e!==false:bU==="!="?bW!==bR:bU==="^="?bW.indexOf(bR)===0:bU==="$="?bW.substr(bW.length-bR.length)===bR:bU==="|="?bW===bR||bW.substr(0,bR.length+1)===bR+"-":false},POS:function(bU,bR,bS,bV){var e=bR[2],bT=bE.setFilters[e];if(bT){return bT(bU,bS,bR,bV)}}}};var bD=bE.match.POS,bx=function(bR,e){return"\\"+(e-0+1)};for(var bz in bE.match){bE.match[bz]=new RegExp(bE.match[bz].source+(/(?![^\[]*\])(?![^\(]*\))/.source));bE.leftMatch[bz]=new RegExp(/(^(?:.|\r|\n)*?)/.source+bE.match[bz].source.replace(/\\(\d+)/g,bx))}bE.match.globalPOS=bD;var bF=function(bR,e){bR=Array.prototype.slice.call(bR,0);if(e){e.push.apply(e,bR);return e}return bR};try{Array.prototype.slice.call(av.documentElement.childNodes,0)[0].nodeType}catch(bP){bF=function(bU,bT){var bS=0,bR=bT||[];if(bL.call(bU)==="[object Array]"){Array.prototype.push.apply(bR,bU)}else{if(typeof bU.length==="number"){for(var e=bU.length;bS<e;bS++){bR.push(bU[bS])}}else{for(;bU[bS];bS++){bR.push(bU[bS])}}}return bR}}var bJ,bG;if(av.documentElement.compareDocumentPosition){bJ=function(bR,e){if(bR===e){bB=true;return 0}if(!bR.compareDocumentPosition||!e.compareDocumentPosition){return bR.compareDocumentPosition?-1:1}return bR.compareDocumentPosition(e)&4?-1:1}}else{bJ=function(bY,bX){if(bY===bX){bB=true;return 0}else{if(bY.sourceIndex&&bX.sourceIndex){return bY.sourceIndex-bX.sourceIndex}}var bV,bR,bS=[],e=[],bU=bY.parentNode,bW=bX.parentNode,bZ=bU;if(bU===bW){return bG(bY,bX)}else{if(!bU){return -1}else{if(!bW){return 1}}}while(bZ){bS.unshift(bZ);bZ=bZ.parentNode}bZ=bW;while(bZ){e.unshift(bZ);bZ=bZ.parentNode}bV=bS.length;bR=e.length;for(var bT=0;bT<bV&&bT<bR;bT++){if(bS[bT]!==e[bT]){return bG(bS[bT],e[bT])}}return bT===bV?bG(bY,e[bT],-1):bG(bS[bT],bX,1)};bG=function(bR,e,bS){if(bR===e){return bS}var bT=bR.nextSibling;while(bT){if(bT===e){return -1}bT=bT.nextSibling}return 1}}(function(){var bR=av.createElement("div"),bS="script"+(new Date()).getTime(),e=av.documentElement;bR.innerHTML="<a name='"+bS+"'/>";e.insertBefore(bR,e.firstChild);if(av.getElementById(bS)){bE.find.ID=function(bU,bV,bW){if(typeof bV.getElementById!=="undefined"&&!bW){var bT=bV.getElementById(bU[1]);return bT?bT.id===bU[1]||typeof bT.getAttributeNode!=="undefined"&&bT.getAttributeNode("id").nodeValue===bU[1]?[bT]:L:[]}};bE.filter.ID=function(bV,bT){var bU=typeof bV.getAttributeNode!=="undefined"&&bV.getAttributeNode("id");return bV.nodeType===1&&bU&&bU.nodeValue===bT}}e.removeChild(bR);e=bR=null})();(function(){var e=av.createElement("div");e.appendChild(av.createComment(""));if(e.getElementsByTagName("*").length>0){bE.find.TAG=function(bR,bV){var bU=bV.getElementsByTagName(bR[1]);if(bR[1]==="*"){var bT=[];for(var bS=0;bU[bS];bS++){if(bU[bS].nodeType===1){bT.push(bU[bS])}}bU=bT}return bU}}e.innerHTML="<a href='#'></a>";if(e.firstChild&&typeof e.firstChild.getAttribute!=="undefined"&&e.firstChild.getAttribute("href")!=="#"){bE.attrHandle.href=function(bR){return bR.getAttribute("href",2)}}e=null})();if(av.querySelectorAll){(function(){var e=by,bT=av.createElement("div"),bS="__sizzle__";bT.innerHTML="<p class='TEST'></p>";if(bT.querySelectorAll&&bT.querySelectorAll(".TEST").length===0){return}by=function(b4,bV,bZ,b3){bV=bV||av;if(!b3&&!by.isXML(bV)){var b2=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b4);if(b2&&(bV.nodeType===1||bV.nodeType===9)){if(b2[1]){return bF(bV.getElementsByTagName(b4),bZ)}else{if(b2[2]&&bE.find.CLASS&&bV.getElementsByClassName){return bF(bV.getElementsByClassName(b2[2]),bZ)}}}if(bV.nodeType===9){if(b4==="body"&&bV.body){return bF([bV.body],bZ)}else{if(b2&&b2[3]){var bY=bV.getElementById(b2[3]);if(bY&&bY.parentNode){if(bY.id===b2[3]){return bF([bY],bZ)}}else{return bF([],bZ)}}}try{return bF(bV.querySelectorAll(b4),bZ)}catch(b0){}}else{if(bV.nodeType===1&&bV.nodeName.toLowerCase()!=="object"){var bW=bV,bX=bV.getAttribute("id"),bU=bX||bS,b6=bV.parentNode,b5=/^\s*[+~]/.test(b4);if(!bX){bV.setAttribute("id",bU)}else{bU=bU.replace(/'/g,"\\$&")}if(b5&&b6){bV=bV.parentNode}try{if(!b5||b6){return bF(bV.querySelectorAll("[id='"+bU+"'] "+b4),bZ)}}catch(b1){}finally{if(!bX){bW.removeAttribute("id")}}}}}return e(b4,bV,bZ,b3)};for(var bR in e){by[bR]=e[bR]}bT=null})()}(function(){var e=av.documentElement,bS=e.matchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.msMatchesSelector;if(bS){var bU=!bS.call(av.createElement("div"),"div"),bR=false;try{bS.call(av.documentElement,"[test!='']:sizzle")}catch(bT){bR=true}by.matchesSelector=function(bW,bY){bY=bY.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!by.isXML(bW)){try{if(bR||!bE.match.PSEUDO.test(bY)&&!/!=/.test(bY)){var bV=bS.call(bW,bY);if(bV||!bU||bW.document&&bW.document.nodeType!==11){return bV}}}catch(bX){}}return by(bY,null,null,[bW]).length>0}}})();(function(){var e=av.createElement("div");e.innerHTML="<div class='test e'></div><div class='test'></div>";if(!e.getElementsByClassName||e.getElementsByClassName("e").length===0){return}e.lastChild.className="e";if(e.getElementsByClassName("e").length===1){return}bE.order.splice(1,0,"CLASS");bE.find.CLASS=function(bR,bS,bT){if(typeof bS.getElementsByClassName!=="undefined"&&!bT){return bS.getElementsByClassName(bR[1])}};e=null})();function bv(bR,bW,bV,bZ,bX,bY){for(var bT=0,bS=bZ.length;bT<bS;bT++){var e=bZ[bT];if(e){var bU=false;e=e[bR];while(e){if(e[bC]===bV){bU=bZ[e.sizset];break}if(e.nodeType===1&&!bY){e[bC]=bV;e.sizset=bT}if(e.nodeName.toLowerCase()===bW){bU=e;break}e=e[bR]}bZ[bT]=bU}}}function bN(bR,bW,bV,bZ,bX,bY){for(var bT=0,bS=bZ.length;bT<bS;bT++){var e=bZ[bT];if(e){var bU=false;e=e[bR];while(e){if(e[bC]===bV){bU=bZ[e.sizset];break}if(e.nodeType===1){if(!bY){e[bC]=bV;e.sizset=bT}if(typeof bW!=="string"){if(e===bW){bU=true;break}}else{if(by.filter(bW,[e]).length>0){bU=e;break}}}e=e[bR]}bZ[bT]=bU}}}if(av.documentElement.contains){by.contains=function(bR,e){return bR!==e&&(bR.contains?bR.contains(e):true)}}else{if(av.documentElement.compareDocumentPosition){by.contains=function(bR,e){return !!(bR.compareDocumentPosition(e)&16)}}else{by.contains=function(){return false}}}by.isXML=function(e){var bR=(e?e.ownerDocument||e:0).documentElement;return bR?bR.nodeName!=="HTML":false};var bM=function(bS,e,bW){var bV,bX=[],bU="",bY=e.nodeType?[e]:e;while((bV=bE.match.PSEUDO.exec(bS))){bU+=bV[0];bS=bS.replace(bE.match.PSEUDO,"")}bS=bE.relative[bS]?bS+"*":bS;for(var bT=0,bR=bY.length;bT<bR;bT++){by(bS,bY[bT],bX,bW)}return by.filter(bU,bX)};by.attr=b.attr;by.selectors.attrMap={};b.find=by;b.expr=by.selectors;b.expr[":"]=b.expr.filters;b.unique=by.uniqueSort;b.text=by.getText;b.isXMLDoc=by.isXML;b.contains=by.contains})();var ab=/Until$/,aq=/^(?:parents|prevUntil|prevAll)/,bb=/,/,bp=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,H=b.expr.match.globalPOS,ay={children:true,contents:true,next:true,prev:true};b.fn.extend({find:function(e){var bw=this,by,bv;if(typeof e!=="string"){return b(e).filter(function(){for(by=0,bv=bw.length;by<bv;by++){if(b.contains(bw[by],this)){return true}}})}var bx=this.pushStack("","find",e),bA,bB,bz;for(by=0,bv=this.length;by<bv;by++){bA=bx.length;b.find(e,this[by],bx);if(by>0){for(bB=bA;bB<bx.length;bB++){for(bz=0;bz<bA;bz++){if(bx[bz]===bx[bB]){bx.splice(bB--,1);break}}}}}return bx},has:function(bv){var e=b(bv);return this.filter(function(){for(var bx=0,bw=e.length;bx<bw;bx++){if(b.contains(this,e[bx])){return true}}})},not:function(e){return this.pushStack(aH(this,e,false),"not",e)},filter:function(e){return this.pushStack(aH(this,e,true),"filter",e)},is:function(e){return !!e&&(typeof e==="string"?H.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(by,bx){var bv=[],bw,e,bz=this[0];if(b.isArray(by)){var bB=1;while(bz&&bz.ownerDocument&&bz!==bx){for(bw=0;bw<by.length;bw++){if(b(bz).is(by[bw])){bv.push({selector:by[bw],elem:bz,level:bB})}}bz=bz.parentNode;bB++}return bv}var bA=H.test(by)||typeof by!=="string"?b(by,bx||this.context):0;for(bw=0,e=this.length;bw<e;bw++){bz=this[bw];while(bz){if(bA?bA.index(bz)>-1:b.find.matchesSelector(bz,by)){bv.push(bz);break}else{bz=bz.parentNode;if(!bz||!bz.ownerDocument||bz===bx||bz.nodeType===11){break}}}}bv=bv.length>1?b.unique(bv):bv;return this.pushStack(bv,"closest",by)},index:function(e){if(!e){return(this[0]&&this[0].parentNode)?this.prevAll().length:-1}if(typeof e==="string"){return b.inArray(this[0],b(e))}return b.inArray(e.jquery?e[0]:e,this)},add:function(e,bv){var bx=typeof e==="string"?b(e,bv):b.makeArray(e&&e.nodeType?[e]:e),bw=b.merge(this.get(),bx);return this.pushStack(B(bx[0])||B(bw[0])?bw:b.unique(bw))},andSelf:function(){return this.add(this.prevObject)}});function B(e){return !e||!e.parentNode||e.parentNode.nodeType===11}b.each({parent:function(bv){var e=bv.parentNode;return e&&e.nodeType!==11?e:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(bv,e,bw){return b.dir(bv,"parentNode",bw)},next:function(e){return b.nth(e,2,"nextSibling")},prev:function(e){return b.nth(e,2,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(bv,e,bw){return b.dir(bv,"nextSibling",bw)},prevUntil:function(bv,e,bw){return b.dir(bv,"previousSibling",bw)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.makeArray(e.childNodes)}},function(e,bv){b.fn[e]=function(by,bw){var bx=b.map(this,bv,by);if(!ab.test(e)){bw=by}if(bw&&typeof bw==="string"){bx=b.filter(bw,bx)}bx=this.length>1&&!ay[e]?b.unique(bx):bx;if((this.length>1||bb.test(bw))&&aq.test(e)){bx=bx.reverse()}return this.pushStack(bx,e,P.call(arguments).join(","))}});b.extend({filter:function(bw,e,bv){if(bv){bw=":not("+bw+")"}return e.length===1?b.find.matchesSelector(e[0],bw)?[e[0]]:[]:b.find.matches(bw,e)},dir:function(bw,bv,by){var e=[],bx=bw[bv];while(bx&&bx.nodeType!==9&&(by===L||bx.nodeType!==1||!b(bx).is(by))){if(bx.nodeType===1){e.push(bx)}bx=bx[bv]}return e},nth:function(by,e,bw,bx){e=e||1;var bv=0;for(;by;by=by[bw]){if(by.nodeType===1&&++bv===e){break}}return by},sibling:function(bw,bv){var e=[];for(;bw;bw=bw.nextSibling){if(bw.nodeType===1&&bw!==bv){e.push(bw)}}return e}});function aH(bx,bw,e){bw=bw||0;if(b.isFunction(bw)){return b.grep(bx,function(bz,by){var bA=!!bw.call(bz,by,bz);return bA===e})}else{if(bw.nodeType){return b.grep(bx,function(bz,by){return(bz===bw)===e})}else{if(typeof bw==="string"){var bv=b.grep(bx,function(by){return by.nodeType===1});if(bp.test(bw)){return b.filter(bw,bv,!e)}else{bw=b.filter(bw,bv)}}}}return b.grep(bx,function(bz,by){return(b.inArray(bz,bw)>=0)===e})}function a(e){var bw=aS.split("|"),bv=e.createDocumentFragment();if(bv.createElement){while(bw.length){bv.createElement(bw.pop())}}return bv}var aS="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ah=/ jQuery\d+="(?:\d+|null)"/g,ar=/^\s+/,R=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,d=/<([\w:]+)/,v=/<tbody/i,W=/<|&#?\w+;/,ae=/<(?:script|style)/i,O=/<(?:script|object|embed|option|style)/i,ai=new RegExp("<(?:"+aS+")[\\s/>]","i"),o=/checked\s*(?:[^=]|=\s*.checked.)/i,bn=/\/(java|ecma)script/i,aO=/^\s*<!(?:\[CDATA\[|\-\-)/,ax={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},ac=a(av);ax.optgroup=ax.option;ax.tbody=ax.tfoot=ax.colgroup=ax.caption=ax.thead;ax.th=ax.td;if(!b.support.htmlSerialize){ax._default=[1,"div<div>","</div>"]}b.fn.extend({text:function(e){return b.access(this,function(bv){return bv===L?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||av).createTextNode(bv))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e)){return this.each(function(bw){b(this).wrapAll(e.call(this,bw))})}if(this[0]){var bv=b(e,this[0].ownerDocument).eq(0).clone(true);if(this[0].parentNode){bv.insertBefore(this[0])}bv.map(function(){var bw=this;while(bw.firstChild&&bw.firstChild.nodeType===1){bw=bw.firstChild}return bw}).append(this)}return this},wrapInner:function(e){if(b.isFunction(e)){return this.each(function(bv){b(this).wrapInner(e.call(this,bv))})}return this.each(function(){var bv=b(this),bw=bv.contents();if(bw.length){bw.wrapAll(e)}else{bv.append(e)}})},wrap:function(e){var bv=b.isFunction(e);return this.each(function(bw){b(this).wrapAll(bv?e.call(this,bw):e)})},unwrap:function(){return this.parent().each(function(){if(!b.nodeName(this,"body")){b(this).replaceWith(this.childNodes)}}).end()},append:function(){return this.domManip(arguments,true,function(e){if(this.nodeType===1){this.appendChild(e)}})},prepend:function(){return this.domManip(arguments,true,function(e){if(this.nodeType===1){this.insertBefore(e,this.firstChild)}})},before:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,false,function(bv){this.parentNode.insertBefore(bv,this)})}else{if(arguments.length){var e=b.clean(arguments);e.push.apply(e,this.toArray());return this.pushStack(e,"before",arguments)}}},after:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,false,function(bv){this.parentNode.insertBefore(bv,this.nextSibling)})}else{if(arguments.length){var e=this.pushStack(this,"after",arguments);e.push.apply(e,b.clean(arguments));return e}}},remove:function(e,bx){for(var bv=0,bw;(bw=this[bv])!=null;bv++){if(!e||b.filter(e,[bw]).length){if(!bx&&bw.nodeType===1){b.cleanData(bw.getElementsByTagName("*"));b.cleanData([bw])}if(bw.parentNode){bw.parentNode.removeChild(bw)}}}return this},empty:function(){for(var e=0,bv;(bv=this[e])!=null;e++){if(bv.nodeType===1){b.cleanData(bv.getElementsByTagName("*"))}while(bv.firstChild){bv.removeChild(bv.firstChild)}}return this},clone:function(bv,e){bv=bv==null?false:bv;e=e==null?bv:e;return this.map(function(){return b.clone(this,bv,e)})},html:function(e){return b.access(this,function(by){var bx=this[0]||{},bw=0,bv=this.length;if(by===L){return bx.nodeType===1?bx.innerHTML.replace(ah,""):null}if(typeof by==="string"&&!ae.test(by)&&(b.support.leadingWhitespace||!ar.test(by))&&!ax[(d.exec(by)||["",""])[1].toLowerCase()]){by=by.replace(R,"<$1></$2>");try{for(;bw<bv;bw++){bx=this[bw]||{};if(bx.nodeType===1){b.cleanData(bx.getElementsByTagName("*"));bx.innerHTML=by}}bx=0}catch(bz){}}if(bx){this.empty().append(by)}},null,e,arguments.length)},replaceWith:function(e){if(this[0]&&this[0].parentNode){if(b.isFunction(e)){return this.each(function(bx){var bw=b(this),bv=bw.html();bw.replaceWith(e.call(this,bx,bv))})}if(typeof e!=="string"){e=b(e).detach()}return this.each(function(){var bw=this.nextSibling,bv=this.parentNode;b(this).remove();if(bw){b(bw).before(e)}else{b(bv).append(e)}})}else{return this.length?this.pushStack(b(b.isFunction(e)?e():e),"replaceWith",e):this}},detach:function(e){return this.remove(e,true)},domManip:function(bB,bF,bE){var bx,by,bA,bD,bC=bB[0],bv=[];if(!b.support.checkClone&&arguments.length===3&&typeof bC==="string"&&o.test(bC)){return this.each(function(){b(this).domManip(bB,bF,bE,true)})}if(b.isFunction(bC)){return this.each(function(bH){var bG=b(this);bB[0]=bC.call(this,bH,bF?bG.html():L);bG.domManip(bB,bF,bE)})}if(this[0]){bD=bC&&bC.parentNode;if(b.support.parentNode&&bD&&bD.nodeType===11&&bD.childNodes.length===this.length){bx={fragment:bD}}else{bx=b.buildFragment(bB,this,bv)}bA=bx.fragment;if(bA.childNodes.length===1){by=bA=bA.firstChild}else{by=bA.firstChild}if(by){bF=bF&&b.nodeName(by,"tr");for(var bw=0,e=this.length,bz=e-1;bw<e;bw++){bE.call(bF?bc(this[bw],by):this[bw],bx.cacheable||(e>1&&bw<bz)?b.clone(bA,true,true):bA)}}if(bv.length){b.each(bv,function(bG,bH){if(bH.src){b.ajax({type:"GET",global:false,url:bH.src,async:false,dataType:"script"})}else{b.globalEval((bH.text||bH.textContent||bH.innerHTML||"").replace(aO,"/*$0*/"))}if(bH.parentNode){bH.parentNode.removeChild(bH)}})}}return this}});function bc(e,bv){return b.nodeName(e,"table")?(e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody"))):e}function s(bB,bv){if(bv.nodeType!==1||!b.hasData(bB)){return}var by,bx,e,bA=b._data(bB),bz=b._data(bv,bA),bw=bA.events;if(bw){delete bz.handle;bz.events={};for(by in bw){for(bx=0,e=bw[by].length;bx<e;bx++){b.event.add(bv,by,bw[by][bx])}}}if(bz.data){bz.data=b.extend({},bz.data)}}function aj(bv,e){var bw;if(e.nodeType!==1){return}if(e.clearAttributes){e.clearAttributes()}if(e.mergeAttributes){e.mergeAttributes(bv)}bw=e.nodeName.toLowerCase();if(bw==="object"){e.outerHTML=bv.outerHTML}else{if(bw==="input"&&(bv.type==="checkbox"||bv.type==="radio")){if(bv.checked){e.defaultChecked=e.checked=bv.checked}if(e.value!==bv.value){e.value=bv.value}}else{if(bw==="option"){e.selected=bv.defaultSelected}else{if(bw==="input"||bw==="textarea"){e.defaultValue=bv.defaultValue}else{if(bw==="script"&&e.text!==bv.text){e.text=bv.text}}}}}e.removeAttribute(b.expando);e.removeAttribute("_submit_attached");e.removeAttribute("_change_attached")}b.buildFragment=function(bz,bx,bv){var by,e,bw,bA,bB=bz[0];if(bx&&bx[0]){bA=bx[0].ownerDocument||bx[0]}if(!bA.createDocumentFragment){bA=av}if(bz.length===1&&typeof bB==="string"&&bB.length<512&&bA===av&&bB.charAt(0)==="<"&&!O.test(bB)&&(b.support.checkClone||!o.test(bB))&&(b.support.html5Clone||!ai.test(bB))){e=true;bw=b.fragments[bB];if(bw&&bw!==1){by=bw}}if(!by){by=bA.createDocumentFragment();b.clean(bz,bA,by,bv)}if(e){b.fragments[bB]=bw?by:1}return{fragment:by,cacheable:e}};b.fragments={};b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,bv){b.fn[e]=function(bw){var bz=[],bC=b(bw),bB=this.length===1&&this[0].parentNode;if(bB&&bB.nodeType===11&&bB.childNodes.length===1&&bC.length===1){bC[bv](this[0]);return this}else{for(var bA=0,bx=bC.length;bA<bx;bA++){var by=(bA>0?this.clone(true):this).get();b(bC[bA])[bv](by);bz=bz.concat(by)}return this.pushStack(bz,e,bC.selector)}}});function bh(e){if(typeof e.getElementsByTagName!=="undefined"){return e.getElementsByTagName("*")}else{if(typeof e.querySelectorAll!=="undefined"){return e.querySelectorAll("*")}else{return[]}}}function az(e){if(e.type==="checkbox"||e.type==="radio"){e.defaultChecked=e.checked}}function D(e){var bv=(e.nodeName||"").toLowerCase();if(bv==="input"){az(e)}else{if(bv!=="script"&&typeof e.getElementsByTagName!=="undefined"){b.grep(e.getElementsByTagName("input"),az)}}}function am(e){var bv=av.createElement("div");ac.appendChild(bv);bv.innerHTML=e.outerHTML;return bv.firstChild}b.extend({clone:function(by,bA,bw){var e,bv,bx,bz=b.support.html5Clone||b.isXMLDoc(by)||!ai.test("<"+by.nodeName+">")?by.cloneNode(true):am(by);if((!b.support.noCloneEvent||!b.support.noCloneChecked)&&(by.nodeType===1||by.nodeType===11)&&!b.isXMLDoc(by)){aj(by,bz);e=bh(by);bv=bh(bz);for(bx=0;e[bx];++bx){if(bv[bx]){aj(e[bx],bv[bx])}}}if(bA){s(by,bz);if(bw){e=bh(by);bv=bh(bz);for(bx=0;e[bx];++bx){s(e[bx],bv[bx])}}}e=bv=null;return bz},clean:function(bI,bw,bv,bx){var bA,bH,bD,bJ=[];bw=bw||av;if(typeof bw.createElement==="undefined"){bw=bw.ownerDocument||bw[0]&&bw[0].ownerDocument||av}for(var bE=0,bG;(bG=bI[bE])!=null;bE++){if(typeof bG==="number"){bG+=""}if(!bG){continue}if(typeof bG==="string"){if(!W.test(bG)){bG=bw.createTextNode(bG)}else{bG=bG.replace(R,"<$1></$2>");var bN=(d.exec(bG)||["",""])[1].toLowerCase(),bz=ax[bN]||ax._default,bK=bz[0],bB=bw.createElement("div"),bL=ac.childNodes,bM;if(bw===av){ac.appendChild(bB)}else{a(bw).appendChild(bB)}bB.innerHTML=bz[1]+bG+bz[2];while(bK--){bB=bB.lastChild}if(!b.support.tbody){var by=v.test(bG),e=bN==="table"&&!by?bB.firstChild&&bB.firstChild.childNodes:bz[1]==="<table>"&&!by?bB.childNodes:[];for(bD=e.length-1;bD>=0;--bD){if(b.nodeName(e[bD],"tbody")&&!e[bD].childNodes.length){e[bD].parentNode.removeChild(e[bD])}}}if(!b.support.leadingWhitespace&&ar.test(bG)){bB.insertBefore(bw.createTextNode(ar.exec(bG)[0]),bB.firstChild)}bG=bB.childNodes;if(bB){bB.parentNode.removeChild(bB);if(bL.length>0){bM=bL[bL.length-1];if(bM&&bM.parentNode){bM.parentNode.removeChild(bM)}}}}}var bF;if(!b.support.appendChecked){if(bG[0]&&typeof(bF=bG.length)==="number"){for(bD=0;bD<bF;bD++){D(bG[bD])}}else{D(bG)}}if(bG.nodeType){bJ.push(bG)}else{bJ=b.merge(bJ,bG)}}if(bv){bA=function(bO){return !bO.type||bn.test(bO.type)};for(bE=0;bJ[bE];bE++){bH=bJ[bE];if(bx&&b.nodeName(bH,"script")&&(!bH.type||bn.test(bH.type))){bx.push(bH.parentNode?bH.parentNode.removeChild(bH):bH)}else{if(bH.nodeType===1){var bC=b.grep(bH.getElementsByTagName("script"),bA);bJ.splice.apply(bJ,[bE+1,0].concat(bC))}bv.appendChild(bH)}}}return bJ},cleanData:function(bv){var by,bw,e=b.cache,bB=b.event.special,bA=b.support.deleteExpando;for(var bz=0,bx;(bx=bv[bz])!=null;bz++){if(bx.nodeName&&b.noData[bx.nodeName.toLowerCase()]){continue}bw=bx[b.expando];if(bw){by=e[bw];if(by&&by.events){for(var bC in by.events){if(bB[bC]){b.event.remove(bx,bC)}else{b.removeEvent(bx,bC,by.handle)}}if(by.handle){by.handle.elem=null}}if(bA){delete bx[b.expando]}else{if(bx.removeAttribute){bx.removeAttribute(b.expando)}}delete e[bw]}}}});var al=/alpha\([^)]*\)/i,au=/opacity=([^)]*)/,y=/([A-Z]|^ms)/g,bo=/^[\-+]?(?:\d*\.)?\d+$/i,a1=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,I=/^([\-+])=([\-+.\de]+)/,aE=/^margin/,a9={position:"absolute",visibility:"hidden",display:"block"},G=["Top","Right","Bottom","Left"],Z,aJ,aY;b.fn.css=function(e,bv){return b.access(this,function(bx,bw,by){return by!==L?b.style(bx,bw,by):b.css(bx,bw)},e,bv,arguments.length>1)};b.extend({cssHooks:{opacity:{get:function(bw,bv){if(bv){var e=Z(bw,"opacity");return e===""?"1":e}else{return bw.style.opacity}}}},cssNumber:{fillOpacity:true,fontWeight:true,lineHeight:true,opacity:true,orphans:true,widows:true,zIndex:true,zoom:true},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(bx,bw,bD,by){if(!bx||bx.nodeType===3||bx.nodeType===8||!bx.style){return}var bB,bC,bz=b.camelCase(bw),bv=bx.style,bE=b.cssHooks[bz];bw=b.cssProps[bz]||bz;if(bD!==L){bC=typeof bD;if(bC==="string"&&(bB=I.exec(bD))){bD=(+(bB[1]+1)*+bB[2])+parseFloat(b.css(bx,bw));bC="number"}if(bD==null||bC==="number"&&isNaN(bD)){return}if(bC==="number"&&!b.cssNumber[bz]){bD+="px"}if(!bE||!("set" in bE)||(bD=bE.set(bx,bD))!==L){try{bv[bw]=bD}catch(bA){}}}else{if(bE&&"get" in bE&&(bB=bE.get(bx,false,by))!==L){return bB}return bv[bw]}},css:function(by,bx,bv){var bw,e;bx=b.camelCase(bx);e=b.cssHooks[bx];bx=b.cssProps[bx]||bx;if(bx==="cssFloat"){bx="float"}if(e&&"get" in e&&(bw=e.get(by,true,bv))!==L){return bw}else{if(Z){return Z(by,bx)}}},swap:function(by,bx,bz){var e={},bw,bv;for(bv in bx){e[bv]=by.style[bv];by.style[bv]=bx[bv]}bw=bz.call(by);for(bv in bx){by.style[bv]=e[bv]}return bw}});b.curCSS=b.css;if(av.defaultView&&av.defaultView.getComputedStyle){aJ=function(bA,bw){var bv,bz,e,by,bx=bA.style;bw=bw.replace(y,"-$1").toLowerCase();if((bz=bA.ownerDocument.defaultView)&&(e=bz.getComputedStyle(bA,null))){bv=e.getPropertyValue(bw);if(bv===""&&!b.contains(bA.ownerDocument.documentElement,bA)){bv=b.style(bA,bw)}}if(!b.support.pixelMargin&&e&&aE.test(bw)&&a1.test(bv)){by=bx.width;bx.width=bv;bv=e.width;bx.width=by}return bv}}if(av.documentElement.currentStyle){aY=function(bz,bw){var bA,e,by,bv=bz.currentStyle&&bz.currentStyle[bw],bx=bz.style;if(bv==null&&bx&&(by=bx[bw])){bv=by}if(a1.test(bv)){bA=bx.left;e=bz.runtimeStyle&&bz.runtimeStyle.left;if(e){bz.runtimeStyle.left=bz.currentStyle.left}bx.left=bw==="fontSize"?"1em":bv;bv=bx.pixelLeft+"px";bx.left=bA;if(e){bz.runtimeStyle.left=e}}return bv===""?"auto":bv}}Z=aJ||aY;function af(by,bw,bv){var bz=bw==="width"?by.offsetWidth:by.offsetHeight,bx=bw==="width"?1:0,e=4;if(bz>0){if(bv!=="border"){for(;bx<e;bx+=2){if(!bv){bz-=parseFloat(b.css(by,"padding"+G[bx]))||0}if(bv==="margin"){bz+=parseFloat(b.css(by,bv+G[bx]))||0}else{bz-=parseFloat(b.css(by,"border"+G[bx]+"Width"))||0}}}return bz+"px"}bz=Z(by,bw);if(bz<0||bz==null){bz=by.style[bw]}if(a1.test(bz)){return bz}bz=parseFloat(bz)||0;if(bv){for(;bx<e;bx+=2){bz+=parseFloat(b.css(by,"padding"+G[bx]))||0;if(bv!=="padding"){bz+=parseFloat(b.css(by,"border"+G[bx]+"Width"))||0}if(bv==="margin"){bz+=parseFloat(b.css(by,bv+G[bx]))||0}}}return bz+"px"}b.each(["height","width"],function(bv,e){b.cssHooks[e]={get:function(by,bx,bw){if(bx){if(by.offsetWidth!==0){return af(by,e,bw)}else{return b.swap(by,a9,function(){return af(by,e,bw)})}}},set:function(bw,bx){return bo.test(bx)?bx+"px":bx}}});if(!b.support.opacity){b.cssHooks.opacity={get:function(bv,e){return au.test((e&&bv.currentStyle?bv.currentStyle.filter:bv.style.filter)||"")?(parseFloat(RegExp.$1)/100)+"":e?"1":""},set:function(by,bz){var bx=by.style,bv=by.currentStyle,e=b.isNumeric(bz)?"alpha(opacity="+bz*100+")":"",bw=bv&&bv.filter||bx.filter||"";bx.zoom=1;if(bz>=1&&b.trim(bw.replace(al,""))===""){bx.removeAttribute("filter");if(bv&&!bv.filter){return}}bx.filter=al.test(bw)?bw.replace(al,e):bw+" "+e}}}b(function(){if(!b.support.reliableMarginRight){b.cssHooks.marginRight={get:function(bv,e){return b.swap(bv,{display:"inline-block"},function(){if(e){return Z(bv,"margin-right")}else{return bv.style.marginRight}})}}}});if(b.expr&&b.expr.filters){b.expr.filters.hidden=function(bw){var bv=bw.offsetWidth,e=bw.offsetHeight;return(bv===0&&e===0)||(!b.support.reliableHiddenOffsets&&((bw.style&&bw.style.display)||b.css(bw,"display"))==="none")};b.expr.filters.visible=function(e){return !b.expr.filters.hidden(e)}}b.each({margin:"",padding:"",border:"Width"},function(e,bv){b.cssHooks[e+bv]={expand:function(by){var bx,bz=typeof by==="string"?by.split(" "):[by],bw={};for(bx=0;bx<4;bx++){bw[e+G[bx]+bv]=bz[bx]||bz[bx-2]||bz[0]}return bw}}});var k=/%20/g,ap=/\[\]$/,bs=/\r?\n/g,bq=/#.*$/,aD=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,a0=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,aN=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,aR=/^(?:GET|HEAD)$/,c=/^\/\//,M=/\?/,a7=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,p=/^(?:select|textarea)/i,h=/\s+/,br=/([?&])_=[^&]*/,K=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,z=b.fn.load,aa={},q={},aF,r,aW=["*/"]+["*"];try{aF=bm.href}catch(aw){aF=av.createElement("a");aF.href="";aF=aF.href}r=K.exec(aF.toLowerCase())||[];function f(e){return function(by,bA){if(typeof by!=="string"){bA=by;by="*"}if(b.isFunction(bA)){var bx=by.toLowerCase().split(h),bw=0,bz=bx.length,bv,bB,bC;for(;bw<bz;bw++){bv=bx[bw];bC=/^\+/.test(bv);if(bC){bv=bv.substr(1)||"*"}bB=e[bv]=e[bv]||[];bB[bC?"unshift":"push"](bA)}}}}function aX(bv,bE,bz,bD,bB,bx){bB=bB||bE.dataTypes[0];bx=bx||{};bx[bB]=true;var bA=bv[bB],bw=0,e=bA?bA.length:0,by=(bv===aa),bC;for(;bw<e&&(by||!bC);bw++){bC=bA[bw](bE,bz,bD);if(typeof bC==="string"){if(!by||bx[bC]){bC=L}else{bE.dataTypes.unshift(bC);bC=aX(bv,bE,bz,bD,bC,bx)}}}if((by||!bC)&&!bx["*"]){bC=aX(bv,bE,bz,bD,"*",bx)}return bC}function an(bw,bx){var bv,e,by=b.ajaxSettings.flatOptions||{};for(bv in bx){if(bx[bv]!==L){(by[bv]?bw:(e||(e={})))[bv]=bx[bv]}}if(e){b.extend(true,bw,e)}}b.fn.extend({load:function(bw,bz,bA){if(typeof bw!=="string"&&z){return z.apply(this,arguments)}else{if(!this.length){return this}}var by=bw.indexOf(" ");if(by>=0){var e=bw.slice(by,bw.length);bw=bw.slice(0,by)}var bx="GET";if(bz){if(b.isFunction(bz)){bA=bz;bz=L}else{if(typeof bz==="object"){bz=b.param(bz,b.ajaxSettings.traditional);bx="POST"}}}var bv=this;b.ajax({url:bw,type:bx,dataType:"html",data:bz,complete:function(bC,bB,bD){bD=bC.responseText;if(bC.isResolved()){bC.done(function(bE){bD=bE});bv.html(e?b("<div>").append(bD.replace(a7,"")).find(e):bD)}if(bA){bv.each(bA,[bD,bB,bC])}}});return this},serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?b.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||p.test(this.nodeName)||a0.test(this.type))}).map(function(e,bv){var bw=b(this).val();return bw==null?null:b.isArray(bw)?b.map(bw,function(by,bx){return{name:bv.name,value:by.replace(bs,"\r\n")}}):{name:bv.name,value:bw.replace(bs,"\r\n")}}).get()}});b.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,bv){b.fn[bv]=function(bw){return this.on(bv,bw)}});b.each(["get","post"],function(e,bv){b[bv]=function(bw,by,bz,bx){if(b.isFunction(by)){bx=bx||bz;bz=by;by=L}return b.ajax({type:bv,url:bw,data:by,success:bz,dataType:bx})}});b.extend({getScript:function(e,bv){return b.get(e,L,bv,"script")},getJSON:function(e,bv,bw){return b.get(e,bv,bw,"json")},ajaxSetup:function(bv,e){if(e){an(bv,b.ajaxSettings)}else{e=bv;bv=b.ajaxSettings}an(bv,e);return bv},ajaxSettings:{url:aF,isLocal:aN.test(r[1]),global:true,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:true,async:true,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":aW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":bd.String,"text html":true,"text json":b.parseJSON,"text xml":b.parseXML},flatOptions:{context:true,url:true}},ajaxPrefilter:f(aa),ajaxTransport:f(q),ajax:function(bz,bx){if(typeof bz==="object"){bx=bz;bz=L}bx=bx||{};var bD=b.ajaxSetup({},bx),bS=bD.context||bD,bG=bS!==bD&&(bS.nodeType||bS instanceof b)?b(bS):b.event,bR=b.Deferred(),bN=b.Callbacks("once memory"),bB=bD.statusCode||{},bC,bH={},bO={},bQ,by,bL,bE,bI,bA=0,bw,bK,bJ={readyState:0,setRequestHeader:function(bT,bU){if(!bA){var e=bT.toLowerCase();bT=bO[e]=bO[e]||bT;bH[bT]=bU}return this},getAllResponseHeaders:function(){return bA===2?bQ:null},getResponseHeader:function(bT){var e;if(bA===2){if(!by){by={};while((e=aD.exec(bQ))){by[e[1].toLowerCase()]=e[2]}}e=by[bT.toLowerCase()]}return e===L?null:e},overrideMimeType:function(e){if(!bA){bD.mimeType=e}return this},abort:function(e){e=e||"abort";if(bL){bL.abort(e)}bF(0,e);return this}};function bF(bZ,bU,b0,bW){if(bA===2){return}bA=2;if(bE){clearTimeout(bE)}bL=L;bQ=bW||"";bJ.readyState=bZ>0?4:0;var bT,b4,b3,bX=bU,bY=b0?bk(bD,bJ,b0):L,bV,b2;if(bZ>=200&&bZ<300||bZ===304){if(bD.ifModified){if((bV=bJ.getResponseHeader("Last-Modified"))){b.lastModified[bC]=bV}if((b2=bJ.getResponseHeader("Etag"))){b.etag[bC]=b2}}if(bZ===304){bX="notmodified";bT=true}else{try{b4=F(bD,bY);bX="success";bT=true}catch(b1){bX="parsererror";b3=b1}}}else{b3=bX;if(!bX||bZ){bX="error";if(bZ<0){bZ=0}}}bJ.status=bZ;bJ.statusText=""+(bU||bX);if(bT){bR.resolveWith(bS,[b4,bX,bJ])}else{bR.rejectWith(bS,[bJ,bX,b3])}bJ.statusCode(bB);bB=L;if(bw){bG.trigger("ajax"+(bT?"Success":"Error"),[bJ,bD,bT?b4:b3])}bN.fireWith(bS,[bJ,bX]);if(bw){bG.trigger("ajaxComplete",[bJ,bD]);if(!(--b.active)){b.event.trigger("ajaxStop")}}}bR.promise(bJ);bJ.success=bJ.done;bJ.error=bJ.fail;bJ.complete=bN.add;bJ.statusCode=function(bT){if(bT){var e;if(bA<2){for(e in bT){bB[e]=[bB[e],bT[e]]}}else{e=bT[bJ.status];bJ.then(e,e)}}return this};bD.url=((bz||bD.url)+"").replace(bq,"").replace(c,r[1]+"//");bD.dataTypes=b.trim(bD.dataType||"*").toLowerCase().split(h);if(bD.crossDomain==null){bI=K.exec(bD.url.toLowerCase());bD.crossDomain=!!(bI&&(bI[1]!=r[1]||bI[2]!=r[2]||(bI[3]||(bI[1]==="http:"?80:443))!=(r[3]||(r[1]==="http:"?80:443))))}if(bD.data&&bD.processData&&typeof bD.data!=="string"){bD.data=b.param(bD.data,bD.traditional)}aX(aa,bD,bx,bJ);if(bA===2){return false}bw=bD.global;bD.type=bD.type.toUpperCase();bD.hasContent=!aR.test(bD.type);if(bw&&b.active++===0){b.event.trigger("ajaxStart")}if(!bD.hasContent){if(bD.data){bD.url+=(M.test(bD.url)?"&":"?")+bD.data;delete bD.data}bC=bD.url;if(bD.cache===false){var bv=b.now(),bP=bD.url.replace(br,"$1_="+bv);bD.url=bP+((bP===bD.url)?(M.test(bD.url)?"&":"?")+"_="+bv:"")}}if(bD.data&&bD.hasContent&&bD.contentType!==false||bx.contentType){bJ.setRequestHeader("Content-Type",bD.contentType)}if(bD.ifModified){bC=bC||bD.url;if(b.lastModified[bC]){bJ.setRequestHeader("If-Modified-Since",b.lastModified[bC])}if(b.etag[bC]){bJ.setRequestHeader("If-None-Match",b.etag[bC])}}bJ.setRequestHeader("Accept",bD.dataTypes[0]&&bD.accepts[bD.dataTypes[0]]?bD.accepts[bD.dataTypes[0]]+(bD.dataTypes[0]!=="*"?", "+aW+"; q=0.01":""):bD.accepts["*"]);for(bK in bD.headers){bJ.setRequestHeader(bK,bD.headers[bK])}if(bD.beforeSend&&(bD.beforeSend.call(bS,bJ,bD)===false||bA===2)){bJ.abort();return false}for(bK in {success:1,error:1,complete:1}){bJ[bK](bD[bK])}bL=aX(q,bD,bx,bJ);if(!bL){bF(-1,"No Transport")}else{bJ.readyState=1;if(bw){bG.trigger("ajaxSend",[bJ,bD])}if(bD.async&&bD.timeout>0){bE=setTimeout(function(){bJ.abort("timeout")},bD.timeout)}try{bA=1;bL.send(bH,bF)}catch(bM){if(bA<2){bF(-1,bM)}else{throw bM}}}return bJ},param:function(e,bw){var bv=[],by=function(bz,bA){bA=b.isFunction(bA)?bA():bA;bv[bv.length]=encodeURIComponent(bz)+"="+encodeURIComponent(bA)};if(bw===L){bw=b.ajaxSettings.traditional}if(b.isArray(e)||(e.jquery&&!b.isPlainObject(e))){b.each(e,function(){by(this.name,this.value)})}else{for(var bx in e){u(bx,e[bx],bw,by)}}return bv.join("&").replace(k,"+")}});function u(bw,by,bv,bx){if(b.isArray(by)){b.each(by,function(bA,bz){if(bv||ap.test(bw)){bx(bw,bz)}else{u(bw+"["+(typeof bz==="object"?bA:"")+"]",bz,bv,bx)}})}else{if(!bv&&b.type(by)==="object"){for(var e in by){u(bw+"["+e+"]",by[e],bv,bx)}}else{bx(bw,by)}}}b.extend({active:0,lastModified:{},etag:{}});function bk(bD,bC,bz){var bv=bD.contents,bB=bD.dataTypes,bw=bD.responseFields,by,bA,bx,e;for(bA in bw){if(bA in bz){bC[bw[bA]]=bz[bA]}}while(bB[0]==="*"){bB.shift();if(by===L){by=bD.mimeType||bC.getResponseHeader("content-type")}}if(by){for(bA in bv){if(bv[bA]&&bv[bA].test(by)){bB.unshift(bA);break}}}if(bB[0] in bz){bx=bB[0]}else{for(bA in bz){if(!bB[0]||bD.converters[bA+" "+bB[0]]){bx=bA;break}if(!e){e=bA}}bx=bx||e}if(bx){if(bx!==bB[0]){bB.unshift(bx)}return bz[bx]}}function F(bH,bz){if(bH.dataFilter){bz=bH.dataFilter(bz,bH.dataType)}var bD=bH.dataTypes,bG={},bA,bE,bw=bD.length,bB,bC=bD[0],bx,by,bF,bv,e;for(bA=1;bA<bw;bA++){if(bA===1){for(bE in bH.converters){if(typeof bE==="string"){bG[bE.toLowerCase()]=bH.converters[bE]}}}bx=bC;bC=bD[bA];if(bC==="*"){bC=bx}else{if(bx!=="*"&&bx!==bC){by=bx+" "+bC;bF=bG[by]||bG["* "+bC];if(!bF){e=L;for(bv in bG){bB=bv.split(" ");if(bB[0]===bx||bB[0]==="*"){e=bG[bB[1]+" "+bC];if(e){bv=bG[bv];if(bv===true){bF=e}else{if(e===true){bF=bv}}break}}}}if(!(bF||e)){b.error("No conversion from "+by.replace(" "," to "))}if(bF!==true){bz=bF?bF(bz):e(bv(bz))}}}}return bz}var aC=b.now(),t=/(\=)\?(&|$)|\?\?/i;b.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return b.expando+"_"+(aC++)}});b.ajaxPrefilter("json jsonp",function(bD,bA,bC){var bx=(typeof bD.data==="string")&&/^application\/x\-www\-form\-urlencoded/.test(bD.contentType);if(bD.dataTypes[0]==="jsonp"||bD.jsonp!==false&&(t.test(bD.url)||bx&&t.test(bD.data))){var bB,bw=bD.jsonpCallback=b.isFunction(bD.jsonpCallback)?bD.jsonpCallback():bD.jsonpCallback,bz=bd[bw],e=bD.url,by=bD.data,bv="$1"+bw+"$2";if(bD.jsonp!==false){e=e.replace(t,bv);if(bD.url===e){if(bx){by=by.replace(t,bv)}if(bD.data===by){e+=(/\?/.test(e)?"&":"?")+bD.jsonp+"="+bw}}}bD.url=e;bD.data=by;bd[bw]=function(bE){bB=[bE]};bC.always(function(){bd[bw]=bz;if(bB&&b.isFunction(bz)){bd[bw](bB[0])}});bD.converters["script json"]=function(){if(!bB){b.error(bw+" was not called")}return bB[0]};bD.dataTypes[0]="json";return"script"}});b.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){b.globalEval(e);return e}}});b.ajaxPrefilter("script",function(e){if(e.cache===L){e.cache=false}if(e.crossDomain){e.type="GET";e.global=false}});b.ajaxTransport("script",function(bw){if(bw.crossDomain){var e,bv=av.head||av.getElementsByTagName("head")[0]||av.documentElement;return{send:function(bx,by){e=av.createElement("script");e.async="async";if(bw.scriptCharset){e.charset=bw.scriptCharset}e.src=bw.url;e.onload=e.onreadystatechange=function(bA,bz){if(bz||!e.readyState||/loaded|complete/.test(e.readyState)){e.onload=e.onreadystatechange=null;if(bv&&e.parentNode){bv.removeChild(e)}e=L;if(!bz){by(200,"success")}}};bv.insertBefore(e,bv.firstChild)},abort:function(){if(e){e.onload(0,1)}}}}});var A=bd.ActiveXObject?function(){for(var e in N){N[e](0,1)}}:false,x=0,N;function aM(){try{return new bd.XMLHttpRequest()}catch(bv){}}function ak(){try{return new bd.ActiveXObject("Microsoft.XMLHTTP")}catch(bv){}}b.ajaxSettings.xhr=bd.ActiveXObject?function(){return !this.isLocal&&aM()||ak()}:aM;(function(e){b.extend(b.support,{ajax:!!e,cors:!!e&&("withCredentials" in e)})})(b.ajaxSettings.xhr());if(b.support.ajax){b.ajaxTransport(function(e){if(!e.crossDomain||b.support.cors){var bv;return{send:function(bB,bw){var bA=e.xhr(),bz,by;if(e.username){bA.open(e.type,e.url,e.async,e.username,e.password)}else{bA.open(e.type,e.url,e.async)}if(e.xhrFields){for(by in e.xhrFields){bA[by]=e.xhrFields[by]}}if(e.mimeType&&bA.overrideMimeType){bA.overrideMimeType(e.mimeType)}if(!e.crossDomain&&!bB["X-Requested-With"]){bB["X-Requested-With"]="XMLHttpRequest"}try{for(by in bB){bA.setRequestHeader(by,bB[by])}}catch(bx){}bA.send((e.hasContent&&e.data)||null);bv=function(bK,bE){var bF,bD,bC,bI,bH;try{if(bv&&(bE||bA.readyState===4)){bv=L;if(bz){bA.onreadystatechange=b.noop;if(A){delete N[bz]}}if(bE){if(bA.readyState!==4){bA.abort()}}else{bF=bA.status;bC=bA.getAllResponseHeaders();bI={};bH=bA.responseXML;if(bH&&bH.documentElement){bI.xml=bH}try{bI.text=bA.responseText}catch(bK){}try{bD=bA.statusText}catch(bJ){bD=""}if(!bF&&e.isLocal&&!e.crossDomain){bF=bI.text?200:404}else{if(bF===1223){bF=204}}}}}catch(bG){if(!bE){bw(-1,bG)}}if(bI){bw(bF,bD,bI,bC)}};if(!e.async||bA.readyState===4){bv()}else{bz=++x;if(A){if(!N){N={};b(bd).unload(A)}N[bz]=bv}bA.onreadystatechange=bv}},abort:function(){if(bv){bv(0,1)}}}}})}var Q={},ba,m,aB=/^(?:toggle|show|hide)$/,aU=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,a4,aI=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],a5;b.fn.extend({show:function(bx,bA,bz){var bw,by;if(bx||bx===0){return this.animate(a2("show",3),bx,bA,bz)}else{for(var bv=0,e=this.length;bv<e;bv++){bw=this[bv];if(bw.style){by=bw.style.display;if(!b._data(bw,"olddisplay")&&by==="none"){by=bw.style.display=""}if((by===""&&b.css(bw,"display")==="none")||!b.contains(bw.ownerDocument.documentElement,bw)){b._data(bw,"olddisplay",w(bw.nodeName))}}}for(bv=0;bv<e;bv++){bw=this[bv];if(bw.style){by=bw.style.display;if(by===""||by==="none"){bw.style.display=b._data(bw,"olddisplay")||""}}}return this}},hide:function(bx,bA,bz){if(bx||bx===0){return this.animate(a2("hide",3),bx,bA,bz)}else{var bw,by,bv=0,e=this.length;for(;bv<e;bv++){bw=this[bv];if(bw.style){by=b.css(bw,"display");if(by!=="none"&&!b._data(bw,"olddisplay")){b._data(bw,"olddisplay",by)}}}for(bv=0;bv<e;bv++){if(this[bv].style){this[bv].style.display="none"}}return this}},_toggle:b.fn.toggle,toggle:function(bw,bv,bx){var e=typeof bw==="boolean";if(b.isFunction(bw)&&b.isFunction(bv)){this._toggle.apply(this,arguments)}else{if(bw==null||e){this.each(function(){var by=e?bw:b(this).is(":hidden");b(this)[by?"show":"hide"]()})}else{this.animate(a2("toggle",3),bw,bv,bx)}}return this},fadeTo:function(e,bx,bw,bv){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:bx},e,bw,bv)},animate:function(bz,bw,by,bx){var e=b.speed(bw,by,bx);if(b.isEmptyObject(bz)){return this.each(e.complete,[false])}bz=b.extend({},bz);function bv(){if(e.queue===false){b._mark(this)}var bE=b.extend({},e),bL=this.nodeType===1,bJ=bL&&b(this).is(":hidden"),bB,bG,bD,bK,bN,bF,bI,bC,bH,bM,bA;bE.animatedProperties={};for(bD in bz){bB=b.camelCase(bD);if(bD!==bB){bz[bB]=bz[bD];delete bz[bD]}if((bN=b.cssHooks[bB])&&"expand" in bN){bF=bN.expand(bz[bB]);delete bz[bB];for(bD in bF){if(!(bD in bz)){bz[bD]=bF[bD]}}}}for(bB in bz){bG=bz[bB];if(b.isArray(bG)){bE.animatedProperties[bB]=bG[1];bG=bz[bB]=bG[0]}else{bE.animatedProperties[bB]=bE.specialEasing&&bE.specialEasing[bB]||bE.easing||"swing"}if(bG==="hide"&&bJ||bG==="show"&&!bJ){return bE.complete.call(this)}if(bL&&(bB==="height"||bB==="width")){bE.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY];if(b.css(this,"display")==="inline"&&b.css(this,"float")==="none"){if(!b.support.inlineBlockNeedsLayout||w(this.nodeName)==="inline"){this.style.display="inline-block"}else{this.style.zoom=1}}}}if(bE.overflow!=null){this.style.overflow="hidden"}for(bD in bz){bK=new b.fx(this,bE,bD);bG=bz[bD];if(aB.test(bG)){bA=b._data(this,"toggle"+bD)||(bG==="toggle"?bJ?"show":"hide":0);if(bA){b._data(this,"toggle"+bD,bA==="show"?"hide":"show");bK[bA]()}else{bK[bG]()}}else{bI=aU.exec(bG);bC=bK.cur();if(bI){bH=parseFloat(bI[2]);bM=bI[3]||(b.cssNumber[bD]?"":"px");if(bM!=="px"){b.style(this,bD,(bH||1)+bM);bC=((bH||1)/bK.cur())*bC;b.style(this,bD,bC+bM)}if(bI[1]){bH=((bI[1]==="-="?-1:1)*bH)+bC}bK.custom(bC,bH,bM)}else{bK.custom(bC,bG,"")}}}return true}return e.queue===false?this.each(bv):this.queue(e.queue,bv)},stop:function(bw,bv,e){if(typeof bw!=="string"){e=bv;bv=bw;bw=L}if(bv&&bw!==false){this.queue(bw||"fx",[])}return this.each(function(){var bx,by=false,bA=b.timers,bz=b._data(this);if(!e){b._unmark(true,this)}function bB(bE,bF,bD){var bC=bF[bD];b.removeData(bE,bD,true);bC.stop(e)}if(bw==null){for(bx in bz){if(bz[bx]&&bz[bx].stop&&bx.indexOf(".run")===bx.length-4){bB(this,bz,bx)}}}else{if(bz[bx=bw+".run"]&&bz[bx].stop){bB(this,bz,bx)}}for(bx=bA.length;bx--;){if(bA[bx].elem===this&&(bw==null||bA[bx].queue===bw)){if(e){bA[bx](true)}else{bA[bx].saveState()}by=true;bA.splice(bx,1)}}if(!(e&&by)){b.dequeue(this,bw)}})}});function bi(){setTimeout(at,0);return(a5=b.now())}function at(){a5=L}function a2(bv,e){var bw={};b.each(aI.concat.apply([],aI.slice(0,e)),function(){bw[this]=bv});return bw}b.each({slideDown:a2("show",1),slideUp:a2("hide",1),slideToggle:a2("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,bv){b.fn[e]=function(bw,by,bx){return this.animate(bv,bw,by,bx)}});b.extend({speed:function(bw,bx,bv){var e=bw&&typeof bw==="object"?b.extend({},bw):{complete:bv||!bv&&bx||b.isFunction(bw)&&bw,duration:bw,easing:bv&&bx||bx&&!b.isFunction(bx)&&bx};e.duration=b.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in b.fx.speeds?b.fx.speeds[e.duration]:b.fx.speeds._default;if(e.queue==null||e.queue===true){e.queue="fx"}e.old=e.complete;e.complete=function(by){if(b.isFunction(e.old)){e.old.call(this)}if(e.queue){b.dequeue(this,e.queue)}else{if(by!==false){b._unmark(this)}}};return e},easing:{linear:function(e){return e},swing:function(e){return(-Math.cos(e*Math.PI)/2)+0.5}},timers:[],fx:function(bv,e,bw){this.options=e;this.elem=bv;this.prop=bw;e.orig=e.orig||{}}});b.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(b.fx.step[this.prop]||b.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var e,bv=b.css(this.elem,this.prop);return isNaN(e=parseFloat(bv))?!bv||bv==="auto"?0:bv:e},custom:function(bz,by,bx){var e=this,bw=b.fx;this.startTime=a5||bi();this.end=by;this.now=this.start=bz;this.pos=this.state=0;this.unit=bx||this.unit||(b.cssNumber[this.prop]?"":"px");function bv(bA){return e.step(bA)}bv.queue=this.options.queue;bv.elem=this.elem;bv.saveState=function(){if(b._data(e.elem,"fxshow"+e.prop)===L){if(e.options.hide){b._data(e.elem,"fxshow"+e.prop,e.start)}else{if(e.options.show){b._data(e.elem,"fxshow"+e.prop,e.end)}}}};if(bv()&&b.timers.push(bv)&&!a4){a4=setInterval(bw.tick,bw.interval)}},show:function(){var e=b._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=e||b.style(this.elem,this.prop);this.options.show=true;if(e!==L){this.custom(this.cur(),e)}else{this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur())}b(this.elem).show()},hide:function(){this.options.orig[this.prop]=b._data(this.elem,"fxshow"+this.prop)||b.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(by){var bA,bB,bv,bx=a5||bi(),e=true,bz=this.elem,bw=this.options;if(by||bx>=bw.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();bw.animatedProperties[this.prop]=true;for(bA in bw.animatedProperties){if(bw.animatedProperties[bA]!==true){e=false}}if(e){if(bw.overflow!=null&&!b.support.shrinkWrapBlocks){b.each(["","X","Y"],function(bC,bD){bz.style["overflow"+bD]=bw.overflow[bC]})}if(bw.hide){b(bz).hide()}if(bw.hide||bw.show){for(bA in bw.animatedProperties){b.style(bz,bA,bw.orig[bA]);b.removeData(bz,"fxshow"+bA,true);b.removeData(bz,"toggle"+bA,true)}}bv=bw.complete;if(bv){bw.complete=false;bv.call(bz)}}return false}else{if(bw.duration==Infinity){this.now=bx}else{bB=bx-this.startTime;this.state=bB/bw.duration;this.pos=b.easing[bw.animatedProperties[this.prop]](this.state,bB,0,1,bw.duration);this.now=this.start+((this.end-this.start)*this.pos)}this.update()}return true}};b.extend(b.fx,{tick:function(){var bw,bv=b.timers,e=0;for(;e<bv.length;e++){bw=bv[e];if(!bw()&&bv[e]===bw){bv.splice(e--,1)}}if(!bv.length){b.fx.stop()}},interval:13,stop:function(){clearInterval(a4);a4=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(e){b.style(e.elem,"opacity",e.now)},_default:function(e){if(e.elem.style&&e.elem.style[e.prop]!=null){e.elem.style[e.prop]=e.now+e.unit}else{e.elem[e.prop]=e.now}}}});b.each(aI.concat.apply([],aI),function(e,bv){if(bv.indexOf("margin")){b.fx.step[bv]=function(bw){b.style(bw.elem,bv,Math.max(0,bw.now)+bw.unit)}}});if(b.expr&&b.expr.filters){b.expr.filters.animated=function(e){return b.grep(b.timers,function(bv){return e===bv.elem}).length}}function w(bx){if(!Q[bx]){var e=av.body,bv=b("<"+bx+">").appendTo(e),bw=bv.css("display");bv.remove();if(bw==="none"||bw===""){if(!ba){ba=av.createElement("iframe");ba.frameBorder=ba.width=ba.height=0}e.appendChild(ba);if(!m||!ba.createElement){m=(ba.contentWindow||ba.contentDocument).document;m.write((b.support.boxModel?"<!doctype html>":"")+"<html><body>");m.close()}bv=m.createElement(bx);m.body.appendChild(bv);bw=b.css(bv,"display");e.removeChild(ba)}Q[bx]=bw}return Q[bx]}var a8,V=/^t(?:able|d|h)$/i,ad=/^(?:body|html)$/i;if("getBoundingClientRect" in av.documentElement){a8=function(by,bH,bw,bB){try{bB=by.getBoundingClientRect()}catch(bF){}if(!bB||!b.contains(bw,by)){return bB?{top:bB.top,left:bB.left}:{top:0,left:0}}var bC=bH.body,bD=aL(bH),bA=bw.clientTop||bC.clientTop||0,bE=bw.clientLeft||bC.clientLeft||0,bv=bD.pageYOffset||b.support.boxModel&&bw.scrollTop||bC.scrollTop,bz=bD.pageXOffset||b.support.boxModel&&bw.scrollLeft||bC.scrollLeft,bG=bB.top+bv-bA,bx=bB.left+bz-bE;return{top:bG,left:bx}}}else{a8=function(bz,bE,bx){var bC,bw=bz.offsetParent,bv=bz,bA=bE.body,bB=bE.defaultView,e=bB?bB.getComputedStyle(bz,null):bz.currentStyle,bD=bz.offsetTop,by=bz.offsetLeft;while((bz=bz.parentNode)&&bz!==bA&&bz!==bx){if(b.support.fixedPosition&&e.position==="fixed"){break}bC=bB?bB.getComputedStyle(bz,null):bz.currentStyle;bD-=bz.scrollTop;by-=bz.scrollLeft;if(bz===bw){bD+=bz.offsetTop;by+=bz.offsetLeft;if(b.support.doesNotAddBorder&&!(b.support.doesAddBorderForTableAndCells&&V.test(bz.nodeName))){bD+=parseFloat(bC.borderTopWidth)||0;by+=parseFloat(bC.borderLeftWidth)||0}bv=bw;bw=bz.offsetParent}if(b.support.subtractsBorderForOverflowNotVisible&&bC.overflow!=="visible"){bD+=parseFloat(bC.borderTopWidth)||0;by+=parseFloat(bC.borderLeftWidth)||0}e=bC}if(e.position==="relative"||e.position==="static"){bD+=bA.offsetTop;by+=bA.offsetLeft}if(b.support.fixedPosition&&e.position==="fixed"){bD+=Math.max(bx.scrollTop,bA.scrollTop);by+=Math.max(bx.scrollLeft,bA.scrollLeft)}return{top:bD,left:by}}}b.fn.offset=function(e){if(arguments.length){return e===L?this:this.each(function(bx){b.offset.setOffset(this,e,bx)})}var bv=this[0],bw=bv&&bv.ownerDocument;if(!bw){return null}if(bv===bw.body){return b.offset.bodyOffset(bv)}return a8(bv,bw,bw.documentElement)};b.offset={bodyOffset:function(e){var bw=e.offsetTop,bv=e.offsetLeft;if(b.support.doesNotIncludeMarginInBodyOffset){bw+=parseFloat(b.css(e,"marginTop"))||0;bv+=parseFloat(b.css(e,"marginLeft"))||0}return{top:bw,left:bv}},setOffset:function(bx,bG,bA){var bB=b.css(bx,"position");if(bB==="static"){bx.style.position="relative"}var bz=b(bx),bv=bz.offset(),e=b.css(bx,"top"),bE=b.css(bx,"left"),bF=(bB==="absolute"||bB==="fixed")&&b.inArray("auto",[e,bE])>-1,bD={},bC={},bw,by;if(bF){bC=bz.position();bw=bC.top;by=bC.left}else{bw=parseFloat(e)||0;by=parseFloat(bE)||0}if(b.isFunction(bG)){bG=bG.call(bx,bA,bv)}if(bG.top!=null){bD.top=(bG.top-bv.top)+bw}if(bG.left!=null){bD.left=(bG.left-bv.left)+by}if("using" in bG){bG.using.call(bx,bD)}else{bz.css(bD)}}};b.fn.extend({position:function(){if(!this[0]){return null}var bw=this[0],bv=this.offsetParent(),bx=this.offset(),e=ad.test(bv[0].nodeName)?{top:0,left:0}:bv.offset();bx.top-=parseFloat(b.css(bw,"marginTop"))||0;bx.left-=parseFloat(b.css(bw,"marginLeft"))||0;e.top+=parseFloat(b.css(bv[0],"borderTopWidth"))||0;e.left+=parseFloat(b.css(bv[0],"borderLeftWidth"))||0;return{top:bx.top-e.top,left:bx.left-e.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||av.body;while(e&&(!ad.test(e.nodeName)&&b.css(e,"position")==="static")){e=e.offsetParent}return e})}});b.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(bw,bv){var e=/Y/.test(bv);b.fn[bw]=function(bx){return b.access(this,function(by,bB,bA){var bz=aL(by);if(bA===L){return bz?(bv in bz)?bz[bv]:b.support.boxModel&&bz.document.documentElement[bB]||bz.document.body[bB]:by[bB]}if(bz){bz.scrollTo(!e?bA:b(bz).scrollLeft(),e?bA:b(bz).scrollTop())}else{by[bB]=bA}},bw,bx,arguments.length,null)}});function aL(e){return b.isWindow(e)?e:e.nodeType===9?e.defaultView||e.parentWindow:false}b.each({Height:"height",Width:"width"},function(bw,bx){var bv="client"+bw,e="scroll"+bw,by="offset"+bw;b.fn["inner"+bw]=function(){var bz=this[0];return bz?bz.style?parseFloat(b.css(bz,bx,"padding")):this[bx]():null};b.fn["outer"+bw]=function(bA){var bz=this[0];return bz?bz.style?parseFloat(b.css(bz,bx,bA?"margin":"border")):this[bx]():null};b.fn[bx]=function(bz){return b.access(this,function(bC,bB,bD){var bF,bE,bG,bA;if(b.isWindow(bC)){bF=bC.document;bE=bF.documentElement[bv];return b.support.boxModel&&bE||bF.body&&bF.body[bv]||bE}if(bC.nodeType===9){bF=bC.documentElement;if(bF[bv]>=bF[e]){return bF[bv]}return Math.max(bC.body[e],bF[e],bC.body[by],bF[by])}if(bD===L){bG=b.css(bC,bB);bA=parseFloat(bG);return b.isNumeric(bA)?bA:bG}b(bC).css(bB,bD)},bx,bz,arguments.length,null)}});bd.jQuery=bd.$=b;if(typeof define==="function"&&define.amd&&define.amd.jQuery){define("jquery",[],function(){return b})}})(window);/*!
+ * jQuery UI 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function(a,d){a.ui=a.ui||{};if(a.ui.version){return}a.extend(a.ui,{version:"1.8.18",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(e,f){return typeof e==="number"?this.each(function(){var g=this;setTimeout(function(){a(g).focus();if(f){f.call(g)}},e)}):this._focus.apply(this,arguments)},scrollParent:function(){var e;if((a.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){e=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(a.curCSS(this,"position",1))&&(/(auto|scroll)/).test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0)}else{e=this.parents().filter(function(){return(/(auto|scroll)/).test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!e.length?a(document):e},zIndex:function(h){if(h!==d){return this.css("zIndex",h)}if(this.length){var f=a(this[0]),e,g;while(f.length&&f[0]!==document){e=f.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){g=parseInt(f.css("zIndex"),10);if(!isNaN(g)&&g!==0){return g}}f=f.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});a.each(["Width","Height"],function(g,e){var f=e==="Width"?["Left","Right"]:["Top","Bottom"],h=e.toLowerCase(),k={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};function j(m,l,i,n){a.each(f,function(){l-=parseFloat(a.curCSS(m,"padding"+this,true))||0;if(i){l-=parseFloat(a.curCSS(m,"border"+this+"Width",true))||0}if(n){l-=parseFloat(a.curCSS(m,"margin"+this,true))||0}});return l}a.fn["inner"+e]=function(i){if(i===d){return k["inner"+e].call(this)}return this.each(function(){a(this).css(h,j(this,i)+"px")})};a.fn["outer"+e]=function(i,l){if(typeof i!=="number"){return k["outer"+e].call(this,i)}return this.each(function(){a(this).css(h,j(this,i,true,l)+"px")})}});function c(g,e){var j=g.nodeName.toLowerCase();if("area"===j){var i=g.parentNode,h=i.name,f;if(!g.href||!h||i.nodeName.toLowerCase()!=="map"){return false}f=a("img[usemap=#"+h+"]")[0];return !!f&&b(f)}return(/input|select|textarea|button|object/.test(j)?!g.disabled:"a"==j?g.href||e:e)&&b(g)}function b(e){return !a(e).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}a.extend(a.expr[":"],{data:function(g,f,e){return !!a.data(g,e[3])},focusable:function(e){return c(e,!isNaN(a.attr(e,"tabindex")))},tabbable:function(g){var e=a.attr(g,"tabindex"),f=isNaN(e);return(f||e>=0)&&c(g,!f)}});a(function(){var e=document.body,f=e.appendChild(f=document.createElement("div"));f.offsetHeight;a.extend(f.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});a.support.minHeight=f.offsetHeight===100;a.support.selectstart="onselectstart" in f;e.removeChild(f).style.display="none"});a.extend(a.ui,{plugin:{add:function(f,g,j){var h=a.ui[f].prototype;for(var e in j){h.plugins[e]=h.plugins[e]||[];h.plugins[e].push([g,j[e]])}},call:function(e,g,f){var j=e.plugins[g];if(!j||!e.element[0].parentNode){return}for(var h=0;h<j.length;h++){if(e.options[j[h][0]]){j[h][1].apply(e.element,f)}}}},contains:function(f,e){return document.compareDocumentPosition?f.compareDocumentPosition(e)&16:f!==e&&f.contains(e)},hasScroll:function(h,f){if(a(h).css("overflow")==="hidden"){return false}var e=(f&&f==="left")?"scrollLeft":"scrollTop",g=false;if(h[e]>0){return true}h[e]=1;g=(h[e]>0);h[e]=0;return g},isOverAxis:function(f,e,g){return(f>e)&&(f<(e+g))},isOver:function(j,f,i,h,e,g){return a.ui.isOverAxis(j,i,e)&&a.ui.isOverAxis(f,h,g)}})})(jQuery);/*!
+ * jQuery UI Widget 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function(b,d){if(b.cleanData){var c=b.cleanData;b.cleanData=function(f){for(var g=0,h;(h=f[g])!=null;g++){try{b(h).triggerHandler("remove")}catch(j){}}c(f)}}else{var a=b.fn.remove;b.fn.remove=function(e,f){return this.each(function(){if(!f){if(!e||b.filter(e,[this]).length){b("*",this).add([this]).each(function(){try{b(this).triggerHandler("remove")}catch(g){}})}}return a.call(b(this),e,f)})}}b.widget=function(f,h,e){var g=f.split(".")[0],j;f=f.split(".")[1];j=g+"-"+f;if(!e){e=h;h=b.Widget}b.expr[":"][j]=function(k){return !!b.data(k,f)};b[g]=b[g]||{};b[g][f]=function(k,l){if(arguments.length){this._createWidget(k,l)}};var i=new h();i.options=b.extend(true,{},i.options);b[g][f].prototype=b.extend(true,i,{namespace:g,widgetName:f,widgetEventPrefix:b[g][f].prototype.widgetEventPrefix||f,widgetBaseClass:j},e);b.widget.bridge(f,b[g][f])};b.widget.bridge=function(f,e){b.fn[f]=function(i){var g=typeof i==="string",h=Array.prototype.slice.call(arguments,1),j=this;i=!g&&h.length?b.extend.apply(null,[true,i].concat(h)):i;if(g&&i.charAt(0)==="_"){return j}if(g){this.each(function(){var k=b.data(this,f),l=k&&b.isFunction(k[i])?k[i].apply(k,h):k;if(l!==k&&l!==d){j=l;return false}})}else{this.each(function(){var k=b.data(this,f);if(k){k.option(i||{})._init()}else{b.data(this,f,new e(i,this))}})}return j}};b.Widget=function(e,f){if(arguments.length){this._createWidget(e,f)}};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(f,g){b.data(g,this.widgetName,this);this.element=b(g);this.options=b.extend(true,{},this.options,this._getCreateOptions(),f);var e=this;this.element.bind("remove."+this.widgetName,function(){e.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(f,g){var e=f;if(arguments.length===0){return b.extend({},this.options)}if(typeof f==="string"){if(g===d){return this.options[f]}e={};e[f]=g}this._setOptions(e);return this},_setOptions:function(f){var e=this;b.each(f,function(g,h){e._setOption(g,h)});return this},_setOption:function(e,f){this.options[e]=f;if(e==="disabled"){this.widget()[f?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",f)}return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(e,f,g){var j,i,h=this.options[e];g=g||{};f=b.Event(f);f.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase();f.target=this.element[0];i=f.originalEvent;if(i){for(j in i){if(!(j in f)){f[j]=i[j]}}}this.element.trigger(f,g);return !(b.isFunction(h)&&h.call(this.element[0],f,g)===false||f.isDefaultPrevented())}}})(jQuery);/*!
+ * jQuery UI Mouse 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ * jquery.ui.widget.js
+ */
+(function(b,c){var a=false;b(document).mouseup(function(d){a=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var d=this;this.element.bind("mousedown."+this.widgetName,function(e){return d._mouseDown(e)}).bind("click."+this.widgetName,function(e){if(true===b.data(e.target,d.widgetName+".preventClickEvent")){b.removeData(e.target,d.widgetName+".preventClickEvent");e.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(f){if(a){return}(this._mouseStarted&&this._mouseUp(f));this._mouseDownEvent=f;var e=this,g=(f.which==1),d=(typeof this.options.cancel=="string"&&f.target.nodeName?b(f.target).closest(this.options.cancel).length:false);if(!g||d||!this._mouseCapture(f)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){e.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(f)&&this._mouseDelayMet(f)){this._mouseStarted=(this._mouseStart(f)!==false);if(!this._mouseStarted){f.preventDefault();return true}}if(true===b.data(f.target,this.widgetName+".preventClickEvent")){b.removeData(f.target,this.widgetName+".preventClickEvent")}this._mouseMoveDelegate=function(h){return e._mouseMove(h)};this._mouseUpDelegate=function(h){return e._mouseUp(h)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);f.preventDefault();a=true;return true},_mouseMove:function(d){if(b.browser.msie&&!(document.documentMode>=9)&&!d.button){return this._mouseUp(d)}if(this._mouseStarted){this._mouseDrag(d);return d.preventDefault()}if(this._mouseDistanceMet(d)&&this._mouseDelayMet(d)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,d)!==false);(this._mouseStarted?this._mouseDrag(d):this._mouseUp(d))}return !this._mouseStarted},_mouseUp:function(d){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;if(d.target==this._mouseDownEvent.target){b.data(d.target,this.widgetName+".preventClickEvent",true)}this._mouseStop(d)}return false},_mouseDistanceMet:function(d){return(Math.max(Math.abs(this._mouseDownEvent.pageX-d.pageX),Math.abs(this._mouseDownEvent.pageY-d.pageY))>=this.options.distance)},_mouseDelayMet:function(d){return this.mouseDelayMet},_mouseStart:function(d){},_mouseDrag:function(d){},_mouseStop:function(d){},_mouseCapture:function(d){return true}})})(jQuery);(function(c,d){c.widget("ui.resizable",c.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,containment:false,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1000},_create:function(){var f=this,k=this.options;this.element.addClass("ui-resizable");c.extend(this,{_aspectRatio:!!(k.aspectRatio),aspectRatio:k.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:k.helper||k.ghost||k.animate?k.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)){this.element.wrap(c('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=k.handles||(!c(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all"){this.handles="n,e,s,w,se,sw,ne,nw"}var l=this.handles.split(",");this.handles={};for(var g=0;g<l.length;g++){var j=c.trim(l[g]),e="ui-resizable-"+j;var h=c('<div class="ui-resizable-handle '+e+'"></div>');if(/sw|se|ne|nw/.test(j)){h.css({zIndex:++k.zIndex})}if("se"==j){h.addClass("ui-icon ui-icon-gripsmall-diagonal-se")}this.handles[j]=".ui-resizable-"+j;this.element.append(h)}}this._renderAxis=function(q){q=q||this.element;for(var n in this.handles){if(this.handles[n].constructor==String){this.handles[n]=c(this.handles[n],this.element).show()}if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var o=c(this.handles[n],this.element),p=0;p=/sw|ne|nw|se|n|s/.test(n)?o.outerHeight():o.outerWidth();var m=["padding",/ne|nw|n/.test(n)?"Top":/se|sw|s/.test(n)?"Bottom":/^e$/.test(n)?"Right":"Left"].join("");q.css(m,p);this._proportionallyResize()}if(!c(this.handles[n]).length){continue}}};this._renderAxis(this.element);this._handles=c(".ui-resizable-handle",this.element).disableSelection();this._handles.mouseover(function(){if(!f.resizing){if(this.className){var i=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)}f.axis=i&&i[1]?i[1]:"se"}});if(k.autoHide){this._handles.hide();c(this.element).addClass("ui-resizable-autohide").hover(function(){if(k.disabled){return}c(this).removeClass("ui-resizable-autohide");f._handles.show()},function(){if(k.disabled){return}if(!f.resizing){c(this).addClass("ui-resizable-autohide");f._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();var e=function(g){c(g).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){e(this.element);var f=this.element;f.after(this.originalElement.css({position:f.css("position"),width:f.outerWidth(),height:f.outerHeight(),top:f.css("top"),left:f.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);e(this.originalElement);return this},_mouseCapture:function(f){var g=false;for(var e in this.handles){if(c(this.handles[e])[0]==f.target){g=true}}return !this.options.disabled&&g},_mouseStart:function(g){var j=this.options,f=this.element.position(),e=this.element;this.resizing=true;this.documentScroll={top:c(document).scrollTop(),left:c(document).scrollLeft()};if(e.is(".ui-draggable")||(/absolute/).test(e.css("position"))){e.css({position:"absolute",top:f.top,left:f.left})}this._renderProxy();var k=b(this.helper.css("left")),h=b(this.helper.css("top"));if(j.containment){k+=c(j.containment).scrollLeft()||0;h+=c(j.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:k,top:h};this.size=this._helper?{width:e.outerWidth(),height:e.outerHeight()}:{width:e.width(),height:e.height()};this.originalSize=this._helper?{width:e.outerWidth(),height:e.outerHeight()}:{width:e.width(),height:e.height()};this.originalPosition={left:k,top:h};this.sizeDiff={width:e.outerWidth()-e.width(),height:e.outerHeight()-e.height()};this.originalMousePosition={left:g.pageX,top:g.pageY};this.aspectRatio=(typeof j.aspectRatio=="number")?j.aspectRatio:((this.originalSize.width/this.originalSize.height)||1);var i=c(".ui-resizable-"+this.axis).css("cursor");c("body").css("cursor",i=="auto"?this.axis+"-resize":i);e.addClass("ui-resizable-resizing");this._propagate("start",g);return true},_mouseDrag:function(e){var h=this.helper,g=this.options,m={},q=this,j=this.originalMousePosition,n=this.axis;var r=(e.pageX-j.left)||0,p=(e.pageY-j.top)||0;var i=this._change[n];if(!i){return false}var l=i.apply(this,[e,r,p]),k=c.browser.msie&&c.browser.version<7,f=this.sizeDiff;this._updateVirtualBoundaries(e.shiftKey);if(this._aspectRatio||e.shiftKey){l=this._updateRatio(l,e)}l=this._respectSize(l,e);this._propagate("resize",e);h.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});if(!this._helper&&this._proportionallyResizeElements.length){this._proportionallyResize()}this._updateCache(l);this._trigger("resize",e,this.ui());return false},_mouseStop:function(h){this.resizing=false;var i=this.options,m=this;if(this._helper){var g=this._proportionallyResizeElements,e=g.length&&(/textarea/i).test(g[0].nodeName),f=e&&c.ui.hasScroll(g[0],"left")?0:m.sizeDiff.height,k=e?0:m.sizeDiff.width;var n={width:(m.helper.width()-k),height:(m.helper.height()-f)},j=(parseInt(m.element.css("left"),10)+(m.position.left-m.originalPosition.left))||null,l=(parseInt(m.element.css("top"),10)+(m.position.top-m.originalPosition.top))||null;if(!i.animate){this.element.css(c.extend(n,{top:l,left:j}))}m.helper.height(m.size.height);m.helper.width(m.size.width);if(this._helper&&!i.animate){this._proportionallyResize()}}c("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",h);if(this._helper){this.helper.remove()}return false},_updateVirtualBoundaries:function(g){var j=this.options,i,h,f,k,e;e={minWidth:a(j.minWidth)?j.minWidth:0,maxWidth:a(j.maxWidth)?j.maxWidth:Infinity,minHeight:a(j.minHeight)?j.minHeight:0,maxHeight:a(j.maxHeight)?j.maxHeight:Infinity};if(this._aspectRatio||g){i=e.minHeight*this.aspectRatio;f=e.minWidth/this.aspectRatio;h=e.maxHeight*this.aspectRatio;k=e.maxWidth/this.aspectRatio;if(i>e.minWidth){e.minWidth=i}if(f>e.minHeight){e.minHeight=f}if(h<e.maxWidth){e.maxWidth=h}if(k<e.maxHeight){e.maxHeight=k}}this._vBoundaries=e},_updateCache:function(e){var f=this.options;this.offset=this.helper.offset();if(a(e.left)){this.position.left=e.left}if(a(e.top)){this.position.top=e.top}if(a(e.height)){this.size.height=e.height}if(a(e.width)){this.size.width=e.width}},_updateRatio:function(h,g){var i=this.options,j=this.position,f=this.size,e=this.axis;if(a(h.height)){h.width=(h.height*this.aspectRatio)}else{if(a(h.width)){h.height=(h.width/this.aspectRatio)}}if(e=="sw"){h.left=j.left+(f.width-h.width);h.top=null}if(e=="nw"){h.top=j.top+(f.height-h.height);h.left=j.left+(f.width-h.width)}return h},_respectSize:function(l,g){var j=this.helper,i=this._vBoundaries,r=this._aspectRatio||g.shiftKey,q=this.axis,t=a(l.width)&&i.maxWidth&&(i.maxWidth<l.width),m=a(l.height)&&i.maxHeight&&(i.maxHeight<l.height),h=a(l.width)&&i.minWidth&&(i.minWidth>l.width),s=a(l.height)&&i.minHeight&&(i.minHeight>l.height);if(h){l.width=i.minWidth}if(s){l.height=i.minHeight}if(t){l.width=i.maxWidth}if(m){l.height=i.maxHeight}var f=this.originalPosition.left+this.originalSize.width,p=this.position.top+this.size.height;var k=/sw|nw|w/.test(q),e=/nw|ne|n/.test(q);if(h&&k){l.left=f-i.minWidth}if(t&&k){l.left=f-i.maxWidth}if(s&&e){l.top=p-i.minHeight}if(m&&e){l.top=p-i.maxHeight}var n=!l.width&&!l.height;if(n&&!l.left&&l.top){l.top=null}else{if(n&&!l.top&&l.left){l.left=null}}return l},_proportionallyResize:function(){var k=this.options;if(!this._proportionallyResizeElements.length){return}var g=this.helper||this.element;for(var f=0;f<this._proportionallyResizeElements.length;f++){var h=this._proportionallyResizeElements[f];if(!this.borderDif){var e=[h.css("borderTopWidth"),h.css("borderRightWidth"),h.css("borderBottomWidth"),h.css("borderLeftWidth")],j=[h.css("paddingTop"),h.css("paddingRight"),h.css("paddingBottom"),h.css("paddingLeft")];this.borderDif=c.map(e,function(l,n){var m=parseInt(l,10)||0,o=parseInt(j[n],10)||0;return m+o})}if(c.browser.msie&&!(!(c(g).is(":hidden")||c(g).parents(":hidden").length))){continue}h.css({height:(g.height()-this.borderDif[0]-this.borderDif[2])||0,width:(g.width()-this.borderDif[1]-this.borderDif[3])||0})}},_renderProxy:function(){var f=this.element,i=this.options;this.elementOffset=f.offset();if(this._helper){this.helper=this.helper||c('<div style="overflow:hidden;"></div>');var e=c.browser.msie&&c.browser.version<7,g=(e?1:0),h=(e?2:-1);this.helper.addClass(this._helper).css({width:this.element.outerWidth()+h,height:this.element.outerHeight()+h,position:"absolute",left:this.elementOffset.left-g+"px",top:this.elementOffset.top-g+"px",zIndex:++i.zIndex});this.helper.appendTo("body").disableSelection()}else{this.helper=this.element}},_change:{e:function(g,f,e){return{width:this.originalSize.width+f}},w:function(h,f,e){var j=this.options,g=this.originalSize,i=this.originalPosition;return{left:i.left+f,width:g.width-f}},n:function(h,f,e){var j=this.options,g=this.originalSize,i=this.originalPosition;return{top:i.top+e,height:g.height-e}},s:function(g,f,e){return{height:this.originalSize.height+e}},se:function(g,f,e){return c.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[g,f,e]))},sw:function(g,f,e){return c.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[g,f,e]))},ne:function(g,f,e){return c.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[g,f,e]))},nw:function(g,f,e){return c.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[g,f,e]))}},_propagate:function(f,e){c.ui.plugin.call(this,f,[e,this.ui()]);(f!="resize"&&this._trigger(f,e,this.ui()))},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});c.extend(c.ui.resizable,{version:"1.8.18"});c.ui.plugin.add("resizable","alsoResize",{start:function(f,g){var e=c(this).data("resizable"),i=e.options;var h=function(j){c(j).each(function(){var k=c(this);k.data("resizable-alsoresize",{width:parseInt(k.width(),10),height:parseInt(k.height(),10),left:parseInt(k.css("left"),10),top:parseInt(k.css("top"),10)})})};if(typeof(i.alsoResize)=="object"&&!i.alsoResize.parentNode){if(i.alsoResize.length){i.alsoResize=i.alsoResize[0];h(i.alsoResize)}else{c.each(i.alsoResize,function(j){h(j)})}}else{h(i.alsoResize)}},resize:function(g,i){var f=c(this).data("resizable"),j=f.options,h=f.originalSize,l=f.originalPosition;var k={height:(f.size.height-h.height)||0,width:(f.size.width-h.width)||0,top:(f.position.top-l.top)||0,left:(f.position.left-l.left)||0},e=function(m,n){c(m).each(function(){var q=c(this),r=c(this).data("resizable-alsoresize"),p={},o=n&&n.length?n:q.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];c.each(o,function(s,u){var t=(r[u]||0)+(k[u]||0);if(t&&t>=0){p[u]=t||null}});q.css(p)})};if(typeof(j.alsoResize)=="object"&&!j.alsoResize.nodeType){c.each(j.alsoResize,function(m,n){e(m,n)})}else{e(j.alsoResize)}},stop:function(e,f){c(this).removeData("resizable-alsoresize")}});c.ui.plugin.add("resizable","animate",{stop:function(i,n){var p=c(this).data("resizable"),j=p.options;var h=p._proportionallyResizeElements,e=h.length&&(/textarea/i).test(h[0].nodeName),f=e&&c.ui.hasScroll(h[0],"left")?0:p.sizeDiff.height,l=e?0:p.sizeDiff.width;var g={width:(p.size.width-l),height:(p.size.height-f)},k=(parseInt(p.element.css("left"),10)+(p.position.left-p.originalPosition.left))||null,m=(parseInt(p.element.css("top"),10)+(p.position.top-p.originalPosition.top))||null;p.element.animate(c.extend(g,m&&k?{top:m,left:k}:{}),{duration:j.animateDuration,easing:j.animateEasing,step:function(){var o={width:parseInt(p.element.css("width"),10),height:parseInt(p.element.css("height"),10),top:parseInt(p.element.css("top"),10),left:parseInt(p.element.css("left"),10)};if(h&&h.length){c(h[0]).css({width:o.width,height:o.height})}p._updateCache(o);p._propagate("resize",i)}})}});c.ui.plugin.add("resizable","containment",{start:function(f,r){var t=c(this).data("resizable"),j=t.options,l=t.element;var g=j.containment,k=(g instanceof c)?g.get(0):(/parent/.test(g))?l.parent().get(0):g;if(!k){return}t.containerElement=c(k);if(/document/.test(g)||g==document){t.containerOffset={left:0,top:0};t.containerPosition={left:0,top:0};t.parentData={element:c(document),left:0,top:0,width:c(document).width(),height:c(document).height()||document.body.parentNode.scrollHeight}}else{var n=c(k),i=[];c(["Top","Right","Left","Bottom"]).each(function(p,o){i[p]=b(n.css("padding"+o))});t.containerOffset=n.offset();t.containerPosition=n.position();t.containerSize={height:(n.innerHeight()-i[3]),width:(n.innerWidth()-i[1])};var q=t.containerOffset,e=t.containerSize.height,m=t.containerSize.width,h=(c.ui.hasScroll(k,"left")?k.scrollWidth:m),s=(c.ui.hasScroll(k)?k.scrollHeight:e);t.parentData={element:k,left:q.left,top:q.top,width:h,height:s}}},resize:function(g,q){var t=c(this).data("resizable"),i=t.options,f=t.containerSize,p=t.containerOffset,m=t.size,n=t.position,r=t._aspectRatio||g.shiftKey,e={top:0,left:0},h=t.containerElement;if(h[0]!=document&&(/static/).test(h.css("position"))){e=p}if(n.left<(t._helper?p.left:0)){t.size.width=t.size.width+(t._helper?(t.position.left-p.left):(t.position.left-e.left));if(r){t.size.height=t.size.width/i.aspectRatio}t.position.left=i.helper?p.left:0}if(n.top<(t._helper?p.top:0)){t.size.height=t.size.height+(t._helper?(t.position.top-p.top):t.position.top);if(r){t.size.width=t.size.height*i.aspectRatio}t.position.top=t._helper?p.top:0}t.offset.left=t.parentData.left+t.position.left;t.offset.top=t.parentData.top+t.position.top;var l=Math.abs((t._helper?t.offset.left-e.left:(t.offset.left-e.left))+t.sizeDiff.width),s=Math.abs((t._helper?t.offset.top-e.top:(t.offset.top-p.top))+t.sizeDiff.height);var k=t.containerElement.get(0)==t.element.parent().get(0),j=/relative|absolute/.test(t.containerElement.css("position"));if(k&&j){l-=t.parentData.left}if(l+t.size.width>=t.parentData.width){t.size.width=t.parentData.width-l;if(r){t.size.height=t.size.width/t.aspectRatio}}if(s+t.size.height>=t.parentData.height){t.size.height=t.parentData.height-s;if(r){t.size.width=t.size.height*t.aspectRatio}}},stop:function(f,n){var q=c(this).data("resizable"),g=q.options,l=q.position,m=q.containerOffset,e=q.containerPosition,i=q.containerElement;var j=c(q.helper),r=j.offset(),p=j.outerWidth()-q.sizeDiff.width,k=j.outerHeight()-q.sizeDiff.height;if(q._helper&&!g.animate&&(/relative/).test(i.css("position"))){c(this).css({left:r.left-e.left-m.left,width:p,height:k})}if(q._helper&&!g.animate&&(/static/).test(i.css("position"))){c(this).css({left:r.left-e.left-m.left,width:p,height:k})}}});c.ui.plugin.add("resizable","ghost",{start:function(g,h){var e=c(this).data("resizable"),i=e.options,f=e.size;e.ghost=e.originalElement.clone();e.ghost.css({opacity:0.25,display:"block",position:"relative",height:f.height,width:f.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof i.ghost=="string"?i.ghost:"");e.ghost.appendTo(e.helper)},resize:function(f,g){var e=c(this).data("resizable"),h=e.options;if(e.ghost){e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})}},stop:function(f,g){var e=c(this).data("resizable"),h=e.options;if(e.ghost&&e.helper){e.helper.get(0).removeChild(e.ghost.get(0))}}});c.ui.plugin.add("resizable","grid",{resize:function(e,m){var p=c(this).data("resizable"),h=p.options,k=p.size,i=p.originalSize,j=p.originalPosition,n=p.axis,l=h._aspectRatio||e.shiftKey;h.grid=typeof h.grid=="number"?[h.grid,h.grid]:h.grid;var g=Math.round((k.width-i.width)/(h.grid[0]||1))*(h.grid[0]||1),f=Math.round((k.height-i.height)/(h.grid[1]||1))*(h.grid[1]||1);if(/^(se|s|e)$/.test(n)){p.size.width=i.width+g;p.size.height=i.height+f}else{if(/^(ne)$/.test(n)){p.size.width=i.width+g;p.size.height=i.height+f;p.position.top=j.top-f}else{if(/^(sw)$/.test(n)){p.size.width=i.width+g;p.size.height=i.height+f;p.position.left=j.left-g}else{p.size.width=i.width+g;p.size.height=i.height+f;p.position.top=j.top-f;p.position.left=j.left-g}}}}});var b=function(e){return parseInt(e,10)||0};var a=function(e){return !isNaN(parseInt(e,10))}})(jQuery);/*!
+ * jQuery hashchange event - v1.3 - 7/21/2010
+ * http://benalman.com/projects/jquery-hashchange-plugin/
+ *
+ * Copyright (c) 2010 "Cowboy" Ben Alman
+ * Dual licensed under the MIT and GPL licenses.
+ * http://benalman.com/about/license/
+ */
+(function($,e,b){var c="hashchange",h=document,f,g=$.event.special,i=h.documentMode,d="on"+c in e&&(i===b||i>7);function a(j){j=j||location.href;return"#"+j.replace(/^[^#]*#?(.*)$/,"$1")}$.fn[c]=function(j){return j?this.bind(c,j):this.trigger(c)};$.fn[c].delay=50;g[c]=$.extend(g[c],{setup:function(){if(d){return false}$(f.start)},teardown:function(){if(d){return false}$(f.stop)}});f=(function(){var j={},p,m=a(),k=function(q){return q},l=k,o=k;j.start=function(){p||n()};j.stop=function(){p&&clearTimeout(p);p=b};function n(){var r=a(),q=o(m);if(r!==m){l(m=r,q);$(e).trigger(c)}else{if(q!==m){location.href=location.href.replace(/#.*/,"")+q}}p=setTimeout(n,$.fn[c].delay)}$.browser.msie&&!d&&(function(){var q,r;j.start=function(){if(!q){r=$.fn[c].src;r=r&&r+a();q=$('<iframe tabindex="-1" title="empty"/>').hide().one("load",function(){r||l(a());n()}).attr("src",r||"javascript:0").insertAfter("body")[0].contentWindow;h.onpropertychange=function(){try{if(event.propertyName==="title"){q.document.title=h.title}}catch(s){}}}};j.stop=k;o=function(){return a(q.location.href)};l=function(v,s){var u=q.document,t=$.fn[c].domain;if(v!==s){u.title=h.title;u.open();t&&u.write('<script>document.domain="'+t+'"<\/script>');u.close();q.location.hash=v}}})();return j})()})(jQuery,this);(function(c){var a=c.scrollTo=function(f,e,d){c(window).scrollTo(f,e,d)};a.defaults={axis:"xy",duration:parseFloat(c.fn.jquery)>=1.3?0:1};a.window=function(d){return c(window)._scrollable()};c.fn._scrollable=function(){return this.map(function(){var e=this,d=!e.nodeName||c.inArray(e.nodeName.toLowerCase(),["iframe","#document","html","body"])!=-1;if(!d){return e}var f=(e.contentWindow||e).document||e.ownerDocument||e;return c.browser.safari||f.compatMode=="BackCompat"?f.body:f.documentElement})};c.fn.scrollTo=function(f,e,d){if(typeof e=="object"){d=e;e=0}if(typeof d=="function"){d={onAfter:d}}if(f=="max"){f=9000000000}d=c.extend({},a.defaults,d);e=e||d.speed||d.duration;d.queue=d.queue&&d.axis.length>1;if(d.queue){e/=2}d.offset=b(d.offset);d.over=b(d.over);return this._scrollable().each(function(){var l=this,j=c(l),k=f,i,g={},m=j.is("html,body");switch(typeof k){case"number":case"string":if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(k)){k=b(k);break}k=c(k,this);case"object":if(k.is||k.style){i=(k=c(k)).offset()}}c.each(d.axis.split(""),function(q,r){var s=r=="x"?"Left":"Top",u=s.toLowerCase(),p="scroll"+s,o=l[p],n=a.max(l,r);if(i){g[p]=i[u]+(m?0:o-j.offset()[u]);if(d.margin){g[p]-=parseInt(k.css("margin"+s))||0;g[p]-=parseInt(k.css("border"+s+"Width"))||0}g[p]+=d.offset[u]||0;if(d.over[u]){g[p]+=k[r=="x"?"width":"height"]()*d.over[u]}}else{var t=k[u];g[p]=t.slice&&t.slice(-1)=="%"?parseFloat(t)/100*n:t}if(/^\d+$/.test(g[p])){g[p]=g[p]<=0?0:Math.min(g[p],n)}if(!q&&d.queue){if(o!=g[p]){h(d.onAfterFirst)}delete g[p]}});h(d.onAfter);function h(n){j.animate(g,e,d.easing,n&&function(){n.call(this,f,d)})}}).end()};a.max=function(j,i){var h=i=="x"?"Width":"Height",e="scroll"+h;if(!c(j).is("html,body")){return j[e]-c(j)[h.toLowerCase()]()}var g="client"+h,f=j.ownerDocument.documentElement,d=j.ownerDocument.body;return Math.max(f[e],d[e])-Math.min(f[g],d[g])};function b(d){return typeof d=="object"?d:{top:d,left:d}}})(jQuery);/*!
+ PowerTip - v1.2.0 - 2013-04-03
+ http://stevenbenner.github.com/jquery-powertip/
+ Copyright (c) 2013 Steven Benner (http://stevenbenner.com/).
+ Released under MIT license.
+ https://raw.github.com/stevenbenner/jquery-powertip/master/LICENSE.txt
+*/
+(function(a){if(typeof define==="function"&&define.amd){define(["jquery"],a)}else{a(jQuery)}}(function(k){var A=k(document),s=k(window),w=k("body");var n="displayController",e="hasActiveHover",d="forcedOpen",u="hasMouseMove",f="mouseOnToPopup",g="originalTitle",y="powertip",o="powertipjq",l="powertiptarget",E=180/Math.PI;var c={isTipOpen:false,isFixedTipOpen:false,isClosing:false,tipOpenImminent:false,activeHover:null,currentX:0,currentY:0,previousX:0,previousY:0,desyncTimeout:null,mouseTrackingActive:false,delayInProgress:false,windowWidth:0,windowHeight:0,scrollTop:0,scrollLeft:0};var p={none:0,top:1,bottom:2,left:4,right:8};k.fn.powerTip=function(F,N){if(!this.length){return this}if(k.type(F)==="string"&&k.powerTip[F]){return k.powerTip[F].call(this,this,N)}var O=k.extend({},k.fn.powerTip.defaults,F),G=new x(O);h();this.each(function M(){var R=k(this),Q=R.data(y),P=R.data(o),T=R.data(l),S;if(R.data(n)){k.powerTip.destroy(R)}S=R.attr("title");if(!Q&&!T&&!P&&S){R.data(y,S);R.data(g,S);R.removeAttr("title")}R.data(n,new t(R,O,G))});if(!O.manual){this.on({"mouseenter.powertip":function J(P){k.powerTip.show(this,P)},"mouseleave.powertip":function L(){k.powerTip.hide(this)},"focus.powertip":function K(){k.powerTip.show(this)},"blur.powertip":function H(){k.powerTip.hide(this,true)},"keydown.powertip":function I(P){if(P.keyCode===27){k.powerTip.hide(this,true)}}})}return this};k.fn.powerTip.defaults={fadeInTime:200,fadeOutTime:100,followMouse:false,popupId:"powerTip",intentSensitivity:7,intentPollInterval:100,closeDelay:100,placement:"n",smartPlacement:false,offset:10,mouseOnToPopup:false,manual:false};k.fn.powerTip.smartPlacementLists={n:["n","ne","nw","s"],e:["e","ne","se","w","nw","sw","n","s","e"],s:["s","se","sw","n"],w:["w","nw","sw","e","ne","se","n","s","w"],nw:["nw","w","sw","n","s","se","nw"],ne:["ne","e","se","n","s","sw","ne"],sw:["sw","w","nw","s","n","ne","sw"],se:["se","e","ne","s","n","nw","se"],"nw-alt":["nw-alt","n","ne-alt","sw-alt","s","se-alt","w","e"],"ne-alt":["ne-alt","n","nw-alt","se-alt","s","sw-alt","e","w"],"sw-alt":["sw-alt","s","se-alt","nw-alt","n","ne-alt","w","e"],"se-alt":["se-alt","s","sw-alt","ne-alt","n","nw-alt","e","w"]};k.powerTip={show:function z(F,G){if(G){i(G);c.previousX=G.pageX;c.previousY=G.pageY;k(F).data(n).show()}else{k(F).first().data(n).show(true,true)}return F},reposition:function r(F){k(F).first().data(n).resetPosition();return F},hide:function D(G,F){if(G){k(G).first().data(n).hide(F)}else{if(c.activeHover){c.activeHover.data(n).hide(true)}}return G},destroy:function C(G){k(G).off(".powertip").each(function F(){var I=k(this),H=[g,n,e,d];if(I.data(g)){I.attr("title",I.data(g));H.push(y)}I.removeData(H)});return G}};k.powerTip.showTip=k.powerTip.show;k.powerTip.closeTip=k.powerTip.hide;function b(){var F=this;F.top="auto";F.left="auto";F.right="auto";F.bottom="auto";F.set=function(H,G){if(k.isNumeric(G)){F[H]=Math.round(G)}}}function t(K,N,F){var J=null;function L(P,Q){M();if(!K.data(e)){if(!P){c.tipOpenImminent=true;J=setTimeout(function O(){J=null;I()},N.intentPollInterval)}else{if(Q){K.data(d,true)}F.showTip(K)}}}function G(P){M();c.tipOpenImminent=false;if(K.data(e)){K.data(d,false);if(!P){c.delayInProgress=true;J=setTimeout(function O(){J=null;F.hideTip(K);c.delayInProgress=false},N.closeDelay)}else{F.hideTip(K)}}}function I(){var Q=Math.abs(c.previousX-c.currentX),O=Math.abs(c.previousY-c.currentY),P=Q+O;if(P<N.intentSensitivity){F.showTip(K)}else{c.previousX=c.currentX;c.previousY=c.currentY;L()}}function M(){J=clearTimeout(J);c.delayInProgress=false}function H(){F.resetPosition(K)}this.show=L;this.hide=G;this.cancel=M;this.resetPosition=H}function j(){function G(M,L,J,O,P){var K=L.split("-")[0],N=new b(),I;if(q(M)){I=H(M,K)}else{I=F(M,K)}switch(L){case"n":N.set("left",I.left-(J/2));N.set("bottom",c.windowHeight-I.top+P);break;case"e":N.set("left",I.left+P);N.set("top",I.top-(O/2));break;case"s":N.set("left",I.left-(J/2));N.set("top",I.top+P);break;case"w":N.set("top",I.top-(O/2));N.set("right",c.windowWidth-I.left+P);break;case"nw":N.set("bottom",c.windowHeight-I.top+P);N.set("right",c.windowWidth-I.left-20);break;case"nw-alt":N.set("left",I.left);N.set("bottom",c.windowHeight-I.top+P);break;case"ne":N.set("left",I.left-20);N.set("bottom",c.windowHeight-I.top+P);break;case"ne-alt":N.set("bottom",c.windowHeight-I.top+P);N.set("right",c.windowWidth-I.left);break;case"sw":N.set("top",I.top+P);N.set("right",c.windowWidth-I.left-20);break;case"sw-alt":N.set("left",I.left);N.set("top",I.top+P);break;case"se":N.set("left",I.left-20);N.set("top",I.top+P);break;case"se-alt":N.set("top",I.top+P);N.set("right",c.windowWidth-I.left);break}return N}function F(K,J){var O=K.offset(),N=K.outerWidth(),I=K.outerHeight(),M,L;switch(J){case"n":M=O.left+N/2;L=O.top;break;case"e":M=O.left+N;L=O.top+I/2;break;case"s":M=O.left+N/2;L=O.top+I;break;case"w":M=O.left;L=O.top+I/2;break;case"nw":M=O.left;L=O.top;break;case"ne":M=O.left+N;L=O.top;break;case"sw":M=O.left;L=O.top+I;break;case"se":M=O.left+N;L=O.top+I;break}return{top:L,left:M}}function H(O,K){var S=O.closest("svg")[0],N=O[0],W=S.createSVGPoint(),L=N.getBBox(),V=N.getScreenCTM(),M=L.width/2,Q=L.height/2,P=[],I=["nw","n","ne","e","se","s","sw","w"],U,X,R,T;function J(){P.push(W.matrixTransform(V))}W.x=L.x;W.y=L.y;J();W.x+=M;J();W.x+=M;J();W.y+=Q;J();W.y+=Q;J();W.x-=M;J();W.x-=M;J();W.y-=Q;J();if(P[0].y!==P[1].y||P[0].x!==P[7].x){X=Math.atan2(V.b,V.a)*E;R=Math.ceil(((X%360)-22.5)/45);if(R<1){R+=8}while(R--){I.push(I.shift())}}for(T=0;T<P.length;T++){if(I[T]===K){U=P[T];break}}return{top:U.y+c.scrollTop,left:U.x+c.scrollLeft}}this.compute=G}function x(Q){var P=new j(),O=k("#"+Q.popupId);if(O.length===0){O=k("<div/>",{id:Q.popupId});if(w.length===0){w=k("body")}w.append(O)}if(Q.followMouse){if(!O.data(u)){A.on("mousemove",M);s.on("scroll",M);O.data(u,true)}}if(Q.mouseOnToPopup){O.on({mouseenter:function L(){if(O.data(f)){if(c.activeHover){c.activeHover.data(n).cancel()}}},mouseleave:function N(){if(c.activeHover){c.activeHover.data(n).hide()}}})}function I(S){S.data(e,true);O.queue(function R(T){H(S);T()})}function H(S){var U;if(!S.data(e)){return}if(c.isTipOpen){if(!c.isClosing){K(c.activeHover)}O.delay(100).queue(function R(V){H(S);V()});return}S.trigger("powerTipPreRender");U=B(S);if(U){O.empty().append(U)}else{return}S.trigger("powerTipRender");c.activeHover=S;c.isTipOpen=true;O.data(f,Q.mouseOnToPopup);if(!Q.followMouse){G(S);c.isFixedTipOpen=true}else{M()}O.fadeIn(Q.fadeInTime,function T(){if(!c.desyncTimeout){c.desyncTimeout=setInterval(J,500)}S.trigger("powerTipOpen")})}function K(R){c.isClosing=true;c.activeHover=null;c.isTipOpen=false;c.desyncTimeout=clearInterval(c.desyncTimeout);R.data(e,false);R.data(d,false);O.fadeOut(Q.fadeOutTime,function S(){var T=new b();c.isClosing=false;c.isFixedTipOpen=false;O.removeClass();T.set("top",c.currentY+Q.offset);T.set("left",c.currentX+Q.offset);O.css(T);R.trigger("powerTipClose")})}function M(){if(!c.isFixedTipOpen&&(c.isTipOpen||(c.tipOpenImminent&&O.data(u)))){var R=O.outerWidth(),V=O.outerHeight(),U=new b(),S,T;U.set("top",c.currentY+Q.offset);U.set("left",c.currentX+Q.offset);S=m(U,R,V);if(S!==p.none){T=a(S);if(T===1){if(S===p.right){U.set("left",c.windowWidth-R)}else{if(S===p.bottom){U.set("top",c.scrollTop+c.windowHeight-V)}}}else{U.set("left",c.currentX-R-Q.offset);U.set("top",c.currentY-V-Q.offset)}}O.css(U)}}function G(S){var R,T;if(Q.smartPlacement){R=k.fn.powerTip.smartPlacementLists[Q.placement];k.each(R,function(U,W){var V=m(F(S,W),O.outerWidth(),O.outerHeight());T=W;if(V===p.none){return false}})}else{F(S,Q.placement);T=Q.placement}O.addClass(T)}function F(U,T){var R=0,S,W,V=new b();V.set("top",0);V.set("left",0);O.css(V);do{S=O.outerWidth();W=O.outerHeight();V=P.compute(U,T,S,W,Q.offset);O.css(V)}while(++R<=5&&(S!==O.outerWidth()||W!==O.outerHeight()));return V}function J(){var R=false;if(c.isTipOpen&&!c.isClosing&&!c.delayInProgress){if(c.activeHover.data(e)===false||c.activeHover.is(":disabled")){R=true}else{if(!v(c.activeHover)&&!c.activeHover.is(":focus")&&!c.activeHover.data(d)){if(O.data(f)){if(!v(O)){R=true}}else{R=true}}}if(R){K(c.activeHover)}}}this.showTip=I;this.hideTip=K;this.resetPosition=G}function q(F){return window.SVGElement&&F[0] instanceof SVGElement}function h(){if(!c.mouseTrackingActive){c.mouseTrackingActive=true;k(function H(){c.scrollLeft=s.scrollLeft();c.scrollTop=s.scrollTop();c.windowWidth=s.width();c.windowHeight=s.height()});A.on("mousemove",i);s.on({resize:function G(){c.windowWidth=s.width();c.windowHeight=s.height()},scroll:function F(){var I=s.scrollLeft(),J=s.scrollTop();if(I!==c.scrollLeft){c.currentX+=I-c.scrollLeft;c.scrollLeft=I}if(J!==c.scrollTop){c.currentY+=J-c.scrollTop;c.scrollTop=J}}})}}function i(F){c.currentX=F.pageX;c.currentY=F.pageY}function v(F){var H=F.offset(),J=F[0].getBoundingClientRect(),I=J.right-J.left,G=J.bottom-J.top;return c.currentX>=H.left&&c.currentX<=H.left+I&&c.currentY>=H.top&&c.currentY<=H.top+G}function B(I){var G=I.data(y),F=I.data(o),K=I.data(l),H,J;if(G){if(k.isFunction(G)){G=G.call(I[0])}J=G}else{if(F){if(k.isFunction(F)){F=F.call(I[0])}if(F.length>0){J=F.clone(true,true)}}else{if(K){H=k("#"+K);if(H.length>0){J=H.html()}}}}return J}function m(M,L,K){var G=c.scrollTop,J=c.scrollLeft,I=G+c.windowHeight,F=J+c.windowWidth,H=p.none;if(M.top<G||Math.abs(M.bottom-c.windowHeight)-K<G){H|=p.top}if(M.top+K>I||Math.abs(M.bottom-c.windowHeight)>I){H|=p.bottom}if(M.left<J||M.right+L>F){H|=p.left}if(M.left+L>F||M.right<J){H|=p.right}return H}function a(G){var F=0;while(G){G&=G-1;F++}return F}}));/*!
+ * jQuery UI Touch Punch 0.2.3
+ *
+ * Copyright 2011–2014, Dave Furfero
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ *
+ * Depends:
+ * jquery.ui.widget.js
+ * jquery.ui.mouse.js
+ */
+(function(b){b.support.touch="ontouchend" in document;if(!b.support.touch){return}var d=b.ui.mouse.prototype,f=d._mouseInit,c=d._mouseDestroy,a;function e(h,i){if(h.originalEvent.touches.length>1){return}h.preventDefault();var j=h.originalEvent.changedTouches[0],g=document.createEvent("MouseEvents");g.initMouseEvent(i,true,true,window,1,j.screenX,j.screenY,j.clientX,j.clientY,false,false,false,false,0,null);h.target.dispatchEvent(g)}d._touchStart=function(h){var g=this;if(a||!g._mouseCapture(h.originalEvent.changedTouches[0])){return}a=true;g._touchMoved=false;e(h,"mouseover");e(h,"mousemove");e(h,"mousedown")};d._touchMove=function(g){if(!a){return}this._touchMoved=true;e(g,"mousemove")};d._touchEnd=function(g){if(!a){return}e(g,"mouseup");e(g,"mouseout");if(!this._touchMoved){e(g,"click")}a=false};d._mouseInit=function(){var g=this;g.element.bind({touchstart:b.proxy(g,"_touchStart"),touchmove:b.proxy(g,"_touchMove"),touchend:b.proxy(g,"_touchEnd")});f.call(g)};d._mouseDestroy=function(){var g=this;g.element.unbind({touchstart:b.proxy(g,"_touchStart"),touchmove:b.proxy(g,"_touchMove"),touchend:b.proxy(g,"_touchEnd")});c.call(g)}})(jQuery);/*!
+ * SmartMenus jQuery Plugin - v1.0.0 - January 27, 2016
+ * http://www.smartmenus.org/
+ *
+ * Copyright Vasil Dinkov, Vadikom Web Ltd.
+ * http://vadikom.com
+ *
+ * Licensed MIT
+ */
+(function(a){if(typeof define==="function"&&define.amd){define(["jquery"],a)}else{if(typeof module==="object"&&typeof module.exports==="object"){module.exports=a(require("jquery"))}else{a(jQuery)}}}(function(a){var b=[],e=!!window.createPopup,f=false,d="ontouchstart" in window,h=false,g=window.requestAnimationFrame||function(l){return setTimeout(l,1000/60)},c=window.cancelAnimationFrame||function(l){clearTimeout(l)};function k(m){var n=".smartmenus_mouse";if(!h&&!m){var o=true,l=null;a(document).bind(i([["mousemove",function(s){var t={x:s.pageX,y:s.pageY,timeStamp:new Date().getTime()};if(l){var q=Math.abs(l.x-t.x),p=Math.abs(l.y-t.y);if((q>0||p>0)&&q<=2&&p<=2&&t.timeStamp-l.timeStamp<=300){f=true;if(o){var r=a(s.target).closest("a");if(r.is("a")){a.each(b,function(){if(a.contains(this.$root[0],r[0])){this.itemEnter({currentTarget:r[0]});return false}})}o=false}}}l=t}],[d?"touchstart":"pointerover pointermove pointerout MSPointerOver MSPointerMove MSPointerOut",function(p){if(j(p.originalEvent)){f=false}}]],n));h=true}else{if(h&&m){a(document).unbind(n);h=false}}}function j(l){return !/^(4|mouse)$/.test(l.pointerType)}function i(l,n){if(!n){n=""}var m={};a.each(l,function(o,p){m[p[0].split(" ").join(n+" ")+n]=p[1]});return m}a.SmartMenus=function(m,l){this.$root=a(m);this.opts=l;this.rootId="";this.accessIdPrefix="";this.$subArrow=null;this.activatedItems=[];this.visibleSubMenus=[];this.showTimeout=0;this.hideTimeout=0;this.scrollTimeout=0;this.clickActivated=false;this.focusActivated=false;this.zIndexInc=0;this.idInc=0;this.$firstLink=null;this.$firstSub=null;this.disabled=false;this.$disableOverlay=null;this.$touchScrollingSub=null;this.cssTransforms3d="perspective" in m.style||"webkitPerspective" in m.style;this.wasCollapsible=false;this.init()};a.extend(a.SmartMenus,{hideAll:function(){a.each(b,function(){this.menuHideAll()})},destroy:function(){while(b.length){b[0].destroy()}k(true)},prototype:{init:function(n){var l=this;if(!n){b.push(this);this.rootId=(new Date().getTime()+Math.random()+"").replace(/\D/g,"");this.accessIdPrefix="sm-"+this.rootId+"-";if(this.$root.hasClass("sm-rtl")){this.opts.rightToLeftSubMenus=true}var r=".smartmenus";this.$root.data("smartmenus",this).attr("data-smartmenus-id",this.rootId).dataSM("level",1).bind(i([["mouseover focusin",a.proxy(this.rootOver,this)],["mouseout focusout",a.proxy(this.rootOut,this)],["keydown",a.proxy(this.rootKeyDown,this)]],r)).delegate("a",i([["mouseenter",a.proxy(this.itemEnter,this)],["mouseleave",a.proxy(this.itemLeave,this)],["mousedown",a.proxy(this.itemDown,this)],["focus",a.proxy(this.itemFocus,this)],["blur",a.proxy(this.itemBlur,this)],["click",a.proxy(this.itemClick,this)]],r));r+=this.rootId;if(this.opts.hideOnClick){a(document).bind(i([["touchstart",a.proxy(this.docTouchStart,this)],["touchmove",a.proxy(this.docTouchMove,this)],["touchend",a.proxy(this.docTouchEnd,this)],["click",a.proxy(this.docClick,this)]],r))}a(window).bind(i([["resize orientationchange",a.proxy(this.winResize,this)]],r));if(this.opts.subIndicators){this.$subArrow=a("<span/>").addClass("sub-arrow");if(this.opts.subIndicatorsText){this.$subArrow.html(this.opts.subIndicatorsText)}}k()}this.$firstSub=this.$root.find("ul").each(function(){l.menuInit(a(this))}).eq(0);this.$firstLink=this.$root.find("a").eq(0);if(this.opts.markCurrentItem){var p=/(index|default)\.[^#\?\/]*/i,m=/#.*/,q=window.location.href.replace(p,""),o=q.replace(m,"");this.$root.find("a").each(function(){var s=this.href.replace(p,""),t=a(this);if(s==q||s==o){t.addClass("current");if(l.opts.markCurrentTree){t.parentsUntil("[data-smartmenus-id]","ul").each(function(){a(this).dataSM("parent-a").addClass("current")})}}})}this.wasCollapsible=this.isCollapsible()},destroy:function(m){if(!m){var n=".smartmenus";this.$root.removeData("smartmenus").removeAttr("data-smartmenus-id").removeDataSM("level").unbind(n).undelegate(n);n+=this.rootId;a(document).unbind(n);a(window).unbind(n);if(this.opts.subIndicators){this.$subArrow=null}}this.menuHideAll();var l=this;this.$root.find("ul").each(function(){var o=a(this);if(o.dataSM("scroll-arrows")){o.dataSM("scroll-arrows").remove()}if(o.dataSM("shown-before")){if(l.opts.subMenusMinWidth||l.opts.subMenusMaxWidth){o.css({width:"",minWidth:"",maxWidth:""}).removeClass("sm-nowrap")}if(o.dataSM("scroll-arrows")){o.dataSM("scroll-arrows").remove()}o.css({zIndex:"",top:"",left:"",marginLeft:"",marginTop:"",display:""})}if((o.attr("id")||"").indexOf(l.accessIdPrefix)==0){o.removeAttr("id")}}).removeDataSM("in-mega").removeDataSM("shown-before").removeDataSM("ie-shim").removeDataSM("scroll-arrows").removeDataSM("parent-a").removeDataSM("level").removeDataSM("beforefirstshowfired").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeAttr("aria-expanded");this.$root.find("a.has-submenu").each(function(){var o=a(this);if(o.attr("id").indexOf(l.accessIdPrefix)==0){o.removeAttr("id")}}).removeClass("has-submenu").removeDataSM("sub").removeAttr("aria-haspopup").removeAttr("aria-controls").removeAttr("aria-expanded").closest("li").removeDataSM("sub");if(this.opts.subIndicators){this.$root.find("span.sub-arrow").remove()}if(this.opts.markCurrentItem){this.$root.find("a.current").removeClass("current")}if(!m){this.$root=null;this.$firstLink=null;this.$firstSub=null;if(this.$disableOverlay){this.$disableOverlay.remove();this.$disableOverlay=null}b.splice(a.inArray(this,b),1)}},disable:function(l){if(!this.disabled){this.menuHideAll();if(!l&&!this.opts.isPopup&&this.$root.is(":visible")){var m=this.$root.offset();this.$disableOverlay=a('<div class="sm-jquery-disable-overlay"/>').css({position:"absolute",top:m.top,left:m.left,width:this.$root.outerWidth(),height:this.$root.outerHeight(),zIndex:this.getStartZIndex(true),opacity:0}).appendTo(document.body)}this.disabled=true}},docClick:function(l){if(this.$touchScrollingSub){this.$touchScrollingSub=null;return}if(this.visibleSubMenus.length&&!a.contains(this.$root[0],l.target)||a(l.target).is("a")){this.menuHideAll()}},docTouchEnd:function(m){if(!this.lastTouch){return}if(this.visibleSubMenus.length&&(this.lastTouch.x2===undefined||this.lastTouch.x1==this.lastTouch.x2)&&(this.lastTouch.y2===undefined||this.lastTouch.y1==this.lastTouch.y2)&&(!this.lastTouch.target||!a.contains(this.$root[0],this.lastTouch.target))){if(this.hideTimeout){clearTimeout(this.hideTimeout);this.hideTimeout=0}var l=this;this.hideTimeout=setTimeout(function(){l.menuHideAll()},350)}this.lastTouch=null},docTouchMove:function(m){if(!this.lastTouch){return}var l=m.originalEvent.touches[0];this.lastTouch.x2=l.pageX;this.lastTouch.y2=l.pageY},docTouchStart:function(m){var l=m.originalEvent.touches[0];this.lastTouch={x1:l.pageX,y1:l.pageY,target:l.target}},enable:function(){if(this.disabled){if(this.$disableOverlay){this.$disableOverlay.remove();this.$disableOverlay=null}this.disabled=false}},getClosestMenu:function(m){var l=a(m).closest("ul");while(l.dataSM("in-mega")){l=l.parent().closest("ul")}return l[0]||null},getHeight:function(l){return this.getOffset(l,true)},getOffset:function(n,l){var m;if(n.css("display")=="none"){m={position:n[0].style.position,visibility:n[0].style.visibility};n.css({position:"absolute",visibility:"hidden"}).show()}var o=n[0].getBoundingClientRect&&n[0].getBoundingClientRect(),p=o&&(l?o.height||o.bottom-o.top:o.width||o.right-o.left);if(!p&&p!==0){p=l?n[0].offsetHeight:n[0].offsetWidth}if(m){n.hide().css(m)}return p},getStartZIndex:function(l){var m=parseInt(this[l?"$root":"$firstSub"].css("z-index"));if(!l&&isNaN(m)){m=parseInt(this.$root.css("z-index"))}return !isNaN(m)?m:1},getTouchPoint:function(l){return l.touches&&l.touches[0]||l.changedTouches&&l.changedTouches[0]||l},getViewport:function(l){var m=l?"Height":"Width",o=document.documentElement["client"+m],n=window["inner"+m];if(n){o=Math.min(o,n)}return o},getViewportHeight:function(){return this.getViewport(true)},getViewportWidth:function(){return this.getViewport()},getWidth:function(l){return this.getOffset(l)},handleEvents:function(){return !this.disabled&&this.isCSSOn()},handleItemEvents:function(l){return this.handleEvents()&&!this.isLinkInMegaMenu(l)},isCollapsible:function(){return this.$firstSub.css("position")=="static"},isCSSOn:function(){return this.$firstLink.css("display")=="block"},isFixed:function(){var l=this.$root.css("position")=="fixed";if(!l){this.$root.parentsUntil("body").each(function(){if(a(this).css("position")=="fixed"){l=true;return false}})}return l},isLinkInMegaMenu:function(l){return a(this.getClosestMenu(l[0])).hasClass("mega-menu")},isTouchMode:function(){return !f||this.opts.noMouseOver||this.isCollapsible()},itemActivate:function(p,l){var n=p.closest("ul"),q=n.dataSM("level");if(q>1&&(!this.activatedItems[q-2]||this.activatedItems[q-2][0]!=n.dataSM("parent-a")[0])){var m=this;a(n.parentsUntil("[data-smartmenus-id]","ul").get().reverse()).add(n).each(function(){m.itemActivate(a(this).dataSM("parent-a"))})}if(!this.isCollapsible()||l){this.menuHideSubMenus(!this.activatedItems[q-1]||this.activatedItems[q-1][0]!=p[0]?q-1:q)}this.activatedItems[q-1]=p;if(this.$root.triggerHandler("activate.smapi",p[0])===false){return}var o=p.dataSM("sub");if(o&&(this.isTouchMode()||(!this.opts.showOnClick||this.clickActivated))){this.menuShow(o)}},itemBlur:function(m){var l=a(m.currentTarget);if(!this.handleItemEvents(l)){return}this.$root.triggerHandler("blur.smapi",l[0])},itemClick:function(o){var n=a(o.currentTarget);if(!this.handleItemEvents(n)){return}if(this.$touchScrollingSub&&this.$touchScrollingSub[0]==n.closest("ul")[0]){this.$touchScrollingSub=null;o.stopPropagation();return false}if(this.$root.triggerHandler("click.smapi",n[0])===false){return false}var p=a(o.target).is("span.sub-arrow"),m=n.dataSM("sub"),l=m?m.dataSM("level")==2:false;if(m&&!m.is(":visible")){if(this.opts.showOnClick&&l){this.clickActivated=true}this.itemActivate(n);if(m.is(":visible")){this.focusActivated=true;return false}}else{if(this.isCollapsible()&&p){this.itemActivate(n);this.menuHide(m);return false}}if(this.opts.showOnClick&&l||n.hasClass("disabled")||this.$root.triggerHandler("select.smapi",n[0])===false){return false}},itemDown:function(m){var l=a(m.currentTarget);if(!this.handleItemEvents(l)){return}l.dataSM("mousedown",true)},itemEnter:function(n){var m=a(n.currentTarget);if(!this.handleItemEvents(m)){return}if(!this.isTouchMode()){if(this.showTimeout){clearTimeout(this.showTimeout);this.showTimeout=0}var l=this;this.showTimeout=setTimeout(function(){l.itemActivate(m)},this.opts.showOnClick&&m.closest("ul").dataSM("level")==1?1:this.opts.showTimeout)}this.$root.triggerHandler("mouseenter.smapi",m[0])},itemFocus:function(m){var l=a(m.currentTarget);if(!this.handleItemEvents(l)){return}if(this.focusActivated&&(!this.isTouchMode()||!l.dataSM("mousedown"))&&(!this.activatedItems.length||this.activatedItems[this.activatedItems.length-1][0]!=l[0])){this.itemActivate(l,true)}this.$root.triggerHandler("focus.smapi",l[0])},itemLeave:function(m){var l=a(m.currentTarget);if(!this.handleItemEvents(l)){return}if(!this.isTouchMode()){l[0].blur();if(this.showTimeout){clearTimeout(this.showTimeout);this.showTimeout=0}}l.removeDataSM("mousedown");this.$root.triggerHandler("mouseleave.smapi",l[0])},menuHide:function(m){if(this.$root.triggerHandler("beforehide.smapi",m[0])===false){return}m.stop(true,true);if(m.css("display")!="none"){var l=function(){m.css("z-index","")};if(this.isCollapsible()){if(this.opts.collapsibleHideFunction){this.opts.collapsibleHideFunction.call(this,m,l)}else{m.hide(this.opts.collapsibleHideDuration,l)}}else{if(this.opts.hideFunction){this.opts.hideFunction.call(this,m,l)}else{m.hide(this.opts.hideDuration,l)}}if(m.dataSM("ie-shim")){m.dataSM("ie-shim").remove().css({"-webkit-transform":"",transform:""})}if(m.dataSM("scroll")){this.menuScrollStop(m);m.css({"touch-action":"","-ms-touch-action":"","-webkit-transform":"",transform:""}).unbind(".smartmenus_scroll").removeDataSM("scroll").dataSM("scroll-arrows").hide()}m.dataSM("parent-a").removeClass("highlighted").attr("aria-expanded","false");m.attr({"aria-expanded":"false","aria-hidden":"true"});var n=m.dataSM("level");this.activatedItems.splice(n-1,1);this.visibleSubMenus.splice(a.inArray(m,this.visibleSubMenus),1);this.$root.triggerHandler("hide.smapi",m[0])}},menuHideAll:function(){if(this.showTimeout){clearTimeout(this.showTimeout);this.showTimeout=0}var m=this.opts.isPopup?1:0;for(var l=this.visibleSubMenus.length-1;l>=m;l--){this.menuHide(this.visibleSubMenus[l])}if(this.opts.isPopup){this.$root.stop(true,true);if(this.$root.is(":visible")){if(this.opts.hideFunction){this.opts.hideFunction.call(this,this.$root)}else{this.$root.hide(this.opts.hideDuration)}if(this.$root.dataSM("ie-shim")){this.$root.dataSM("ie-shim").remove()}}}this.activatedItems=[];this.visibleSubMenus=[];this.clickActivated=false;this.focusActivated=false;this.zIndexInc=0;this.$root.triggerHandler("hideAll.smapi")},menuHideSubMenus:function(n){for(var l=this.activatedItems.length-1;l>=n;l--){var m=this.activatedItems[l].dataSM("sub");if(m){this.menuHide(m)}}},menuIframeShim:function(l){if(e&&this.opts.overlapControlsInIE&&!l.dataSM("ie-shim")){l.dataSM("ie-shim",a("<iframe/>").attr({src:"javascript:0",tabindex:-9}).css({position:"absolute",top:"auto",left:"0",opacity:0,border:"0"}))}},menuInit:function(l){if(!l.dataSM("in-mega")){if(l.hasClass("mega-menu")){l.find("ul").dataSM("in-mega",true)}var q=2,m=l[0];while((m=m.parentNode.parentNode)!=this.$root[0]){q++}var n=l.prevAll("a").eq(-1);if(!n.length){n=l.prevAll().find("a").eq(-1)}n.addClass("has-submenu").dataSM("sub",l);l.dataSM("parent-a",n).dataSM("level",q).parent().dataSM("sub",l);var o=n.attr("id")||this.accessIdPrefix+(++this.idInc),p=l.attr("id")||this.accessIdPrefix+(++this.idInc);n.attr({id:o,"aria-haspopup":"true","aria-controls":p,"aria-expanded":"false"});l.attr({id:p,role:"group","aria-hidden":"true","aria-labelledby":o,"aria-expanded":"false"});if(this.opts.subIndicators){n[this.opts.subIndicatorsPos](this.$subArrow.clone())}}},menuPosition:function(K){var r=K.dataSM("parent-a"),D=r.closest("li"),E=D.parent(),l=K.dataSM("level"),t=this.getWidth(K),J=this.getHeight(K),u=r.offset(),o=u.left,m=u.top,q=this.getWidth(r),F=this.getHeight(r),H=a(window),v=H.scrollLeft(),s=H.scrollTop(),z=this.getViewportWidth(),L=this.getViewportHeight(),w=E.parent().is("[data-sm-horizontal-sub]")||l==2&&!E.hasClass("sm-vertical"),B=this.opts.rightToLeftSubMenus&&!D.is("[data-sm-reverse]")||!this.opts.rightToLeftSubMenus&&D.is("[data-sm-reverse]"),p=l==2?this.opts.mainMenuSubOffsetX:this.opts.subMenusSubOffsetX,n=l==2?this.opts.mainMenuSubOffsetY:this.opts.subMenusSubOffsetY,C,A;if(w){C=B?q-t-p:p;A=this.opts.bottomToTopSubMenus?-J-n:F+n}else{C=B?p-t:q-p;A=this.opts.bottomToTopSubMenus?F-n-J:n}if(this.opts.keepInViewport){var N=o+C,M=m+A;if(B&&N<v){C=w?v-N+C:q-p}else{if(!B&&N+t>v+z){C=w?v+z-t-N+C:p-t}}if(!w){if(J<L&&M+J>s+L){A+=s+L-J-M}else{if(J>=L||M<s){A+=s-M}}}if(w&&(M+J>s+L+0.49||M<s)||!w&&J>L+0.49){var G=this;if(!K.dataSM("scroll-arrows")){K.dataSM("scroll-arrows",a([a('<span class="scroll-up"><span class="scroll-up-arrow"></span></span>')[0],a('<span class="scroll-down"><span class="scroll-down-arrow"></span></span>')[0]]).bind({mouseenter:function(){K.dataSM("scroll").up=a(this).hasClass("scroll-up");G.menuScroll(K)},mouseleave:function(x){G.menuScrollStop(K);G.menuScrollOut(K,x)},"mousewheel DOMMouseScroll":function(x){x.preventDefault()}}).insertAfter(K))}var I=".smartmenus_scroll";K.dataSM("scroll",{y:this.cssTransforms3d?0:A-F,step:1,itemH:F,subH:J,arrowDownH:this.getHeight(K.dataSM("scroll-arrows").eq(1))}).bind(i([["mouseover",function(x){G.menuScrollOver(K,x)}],["mouseout",function(x){G.menuScrollOut(K,x)}],["mousewheel DOMMouseScroll",function(x){G.menuScrollMousewheel(K,x)}]],I)).dataSM("scroll-arrows").css({top:"auto",left:"0",marginLeft:C+(parseInt(K.css("border-left-width"))||0),width:t-(parseInt(K.css("border-left-width"))||0)-(parseInt(K.css("border-right-width"))||0),zIndex:K.css("z-index")}).eq(w&&this.opts.bottomToTopSubMenus?0:1).show();if(this.isFixed()){K.css({"touch-action":"none","-ms-touch-action":"none"}).bind(i([[d?"touchstart touchmove touchend":"pointerdown pointermove pointerup MSPointerDown MSPointerMove MSPointerUp",function(x){G.menuScrollTouch(K,x)}]],I))}}}K.css({top:"auto",left:"0",marginLeft:C,marginTop:A-F});this.menuIframeShim(K);if(K.dataSM("ie-shim")){K.dataSM("ie-shim").css({zIndex:K.css("z-index"),width:t,height:J,marginLeft:C,marginTop:A-F})}},menuScroll:function(r,m,n){var p=r.dataSM("scroll"),q=r.dataSM("scroll-arrows"),o=p.up?p.upEnd:p.downEnd,s;if(!m&&p.momentum){p.momentum*=0.92;s=p.momentum;if(s<0.5){this.menuScrollStop(r);return}}else{s=n||(m||!this.opts.scrollAccelerate?this.opts.scrollStep:Math.floor(p.step))}var l=r.dataSM("level");if(this.activatedItems[l-1]&&this.activatedItems[l-1].dataSM("sub")&&this.activatedItems[l-1].dataSM("sub").is(":visible")){this.menuHideSubMenus(l-1)}p.y=p.up&&o<=p.y||!p.up&&o>=p.y?p.y:(Math.abs(o-p.y)>s?p.y+(p.up?s:-s):o);r.add(r.dataSM("ie-shim")).css(this.cssTransforms3d?{"-webkit-transform":"translate3d(0, "+p.y+"px, 0)",transform:"translate3d(0, "+p.y+"px, 0)"}:{marginTop:p.y});if(f&&(p.up&&p.y>p.downEnd||!p.up&&p.y<p.upEnd)){q.eq(p.up?1:0).show()}if(p.y==o){if(f){q.eq(p.up?0:1).hide()}this.menuScrollStop(r)}else{if(!m){if(this.opts.scrollAccelerate&&p.step<this.opts.scrollStep){p.step+=0.2}var t=this;this.scrollTimeout=g(function(){t.menuScroll(r)})}}},menuScrollMousewheel:function(m,n){if(this.getClosestMenu(n.target)==m[0]){n=n.originalEvent;var l=(n.wheelDelta||-n.detail)>0;if(m.dataSM("scroll-arrows").eq(l?0:1).is(":visible")){m.dataSM("scroll").up=l;this.menuScroll(m,true)}}n.preventDefault()},menuScrollOut:function(l,m){if(f){if(!/^scroll-(up|down)/.test((m.relatedTarget||"").className)&&(l[0]!=m.relatedTarget&&!a.contains(l[0],m.relatedTarget)||this.getClosestMenu(m.relatedTarget)!=l[0])){l.dataSM("scroll-arrows").css("visibility","hidden")}}},menuScrollOver:function(n,o){if(f){if(!/^scroll-(up|down)/.test(o.target.className)&&this.getClosestMenu(o.target)==n[0]){this.menuScrollRefreshData(n);var m=n.dataSM("scroll"),l=a(window).scrollTop()-n.dataSM("parent-a").offset().top-m.itemH;n.dataSM("scroll-arrows").eq(0).css("margin-top",l).end().eq(1).css("margin-top",l+this.getViewportHeight()-m.arrowDownH).end().css("visibility","visible")}}},menuScrollRefreshData:function(n){var m=n.dataSM("scroll"),l=a(window).scrollTop()-n.dataSM("parent-a").offset().top-m.itemH;if(this.cssTransforms3d){l=-(parseFloat(n.css("margin-top"))-l)}a.extend(m,{upEnd:l,downEnd:l+this.getViewportHeight()-m.subH})},menuScrollStop:function(l){if(this.scrollTimeout){c(this.scrollTimeout);this.scrollTimeout=0;l.dataSM("scroll").step=1;return true}},menuScrollTouch:function(p,q){q=q.originalEvent;if(j(q)){var m=this.getTouchPoint(q);if(this.getClosestMenu(m.target)==p[0]){var o=p.dataSM("scroll");if(/(start|down)$/i.test(q.type)){if(this.menuScrollStop(p)){q.preventDefault();this.$touchScrollingSub=p}else{this.$touchScrollingSub=null}this.menuScrollRefreshData(p);a.extend(o,{touchStartY:m.pageY,touchStartTime:q.timeStamp})}else{if(/move$/i.test(q.type)){var n=o.touchY!==undefined?o.touchY:o.touchStartY;if(n!==undefined&&n!=m.pageY){this.$touchScrollingSub=p;var l=n<m.pageY;if(o.up!==undefined&&o.up!=l){a.extend(o,{touchStartY:m.pageY,touchStartTime:q.timeStamp})}a.extend(o,{up:l,touchY:m.pageY});this.menuScroll(p,true,Math.abs(m.pageY-n))}q.preventDefault()}else{if(o.touchY!==undefined){if(o.momentum=Math.pow(Math.abs(m.pageY-o.touchStartY)/(q.timeStamp-o.touchStartTime),2)*15){this.menuScrollStop(p);this.menuScroll(p);q.preventDefault()}delete o.touchY}}}}}},menuShow:function(n){if(!n.dataSM("beforefirstshowfired")){n.dataSM("beforefirstshowfired",true);if(this.$root.triggerHandler("beforefirstshow.smapi",n[0])===false){return}}if(this.$root.triggerHandler("beforeshow.smapi",n[0])===false){return}n.dataSM("shown-before",true).stop(true,true);if(!n.is(":visible")){var m=n.dataSM("parent-a");if(this.opts.keepHighlighted||this.isCollapsible()){m.addClass("highlighted")}if(this.isCollapsible()){n.removeClass("sm-nowrap").css({zIndex:"",width:"auto",minWidth:"",maxWidth:"",top:"",left:"",marginLeft:"",marginTop:""})}else{n.css("z-index",this.zIndexInc=(this.zIndexInc||this.getStartZIndex())+1);if(this.opts.subMenusMinWidth||this.opts.subMenusMaxWidth){n.css({width:"auto",minWidth:"",maxWidth:""}).addClass("sm-nowrap");if(this.opts.subMenusMinWidth){n.css("min-width",this.opts.subMenusMinWidth)}if(this.opts.subMenusMaxWidth){var o=this.getWidth(n);n.css("max-width",this.opts.subMenusMaxWidth);if(o>this.getWidth(n)){n.removeClass("sm-nowrap").css("width",this.opts.subMenusMaxWidth)}}}this.menuPosition(n);if(n.dataSM("ie-shim")){n.dataSM("ie-shim").insertBefore(n)}}var l=function(){n.css("overflow","")};if(this.isCollapsible()){if(this.opts.collapsibleShowFunction){this.opts.collapsibleShowFunction.call(this,n,l)}else{n.show(this.opts.collapsibleShowDuration,l)}}else{if(this.opts.showFunction){this.opts.showFunction.call(this,n,l)}else{n.show(this.opts.showDuration,l)}}m.attr("aria-expanded","true");n.attr({"aria-expanded":"true","aria-hidden":"false"});this.visibleSubMenus.push(n);this.$root.triggerHandler("show.smapi",n[0])}},popupHide:function(l){if(this.hideTimeout){clearTimeout(this.hideTimeout);this.hideTimeout=0}var m=this;this.hideTimeout=setTimeout(function(){m.menuHideAll()},l?1:this.opts.hideTimeout)},popupShow:function(o,n){if(!this.opts.isPopup){alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.');return}if(this.hideTimeout){clearTimeout(this.hideTimeout);this.hideTimeout=0}this.$root.dataSM("shown-before",true).stop(true,true);if(!this.$root.is(":visible")){this.$root.css({left:o,top:n});this.menuIframeShim(this.$root);if(this.$root.dataSM("ie-shim")){this.$root.dataSM("ie-shim").css({zIndex:this.$root.css("z-index"),width:this.getWidth(this.$root),height:this.getHeight(this.$root),left:o,top:n}).insertBefore(this.$root)}var m=this,l=function(){m.$root.css("overflow","")};if(this.opts.showFunction){this.opts.showFunction.call(this,this.$root,l)}else{this.$root.show(this.opts.showDuration,l)}this.visibleSubMenus[0]=this.$root}},refresh:function(){this.destroy(true);this.init(true)},rootKeyDown:function(o){if(!this.handleEvents()){return}switch(o.keyCode){case 27:var m=this.activatedItems[0];if(m){this.menuHideAll();m[0].focus();var n=m.dataSM("sub");if(n){this.menuHide(n)}}break;case 32:var l=a(o.target);if(l.is("a")&&this.handleItemEvents(l)){var n=l.dataSM("sub");if(n&&!n.is(":visible")){this.itemClick({currentTarget:o.target});o.preventDefault()}}break}},rootOut:function(m){if(!this.handleEvents()||this.isTouchMode()||m.target==this.$root[0]){return}if(this.hideTimeout){clearTimeout(this.hideTimeout);this.hideTimeout=0}if(!this.opts.showOnClick||!this.opts.hideOnClick){var l=this;this.hideTimeout=setTimeout(function(){l.menuHideAll()},this.opts.hideTimeout)}},rootOver:function(l){if(!this.handleEvents()||this.isTouchMode()||l.target==this.$root[0]){return}if(this.hideTimeout){clearTimeout(this.hideTimeout);this.hideTimeout=0}},winResize:function(m){if(!this.handleEvents()){if(this.$disableOverlay){var n=this.$root.offset();this.$disableOverlay.css({top:n.top,left:n.left,width:this.$root.outerWidth(),height:this.$root.outerHeight()})}return}if(!("onorientationchange" in window)||m.type=="orientationchange"){var l=this.isCollapsible();if(!(this.wasCollapsible&&l)){if(this.activatedItems.length){this.activatedItems[this.activatedItems.length-1][0].blur()}this.menuHideAll()}this.wasCollapsible=l}}}});a.fn.dataSM=function(l,m){if(m){return this.data(l+"_smartmenus",m)}return this.data(l+"_smartmenus")};a.fn.removeDataSM=function(l){return this.removeData(l+"_smartmenus")};a.fn.smartmenus=function(m){if(typeof m=="string"){var l=arguments,o=m;Array.prototype.shift.call(l);return this.each(function(){var p=a(this).data("smartmenus");if(p&&p[o]){p[o].apply(p,l)}})}var n=a.extend({},a.fn.smartmenus.defaults,m);return this.each(function(){new a.SmartMenus(this,n)})};a.fn.smartmenus.defaults={isPopup:false,mainMenuSubOffsetX:0,mainMenuSubOffsetY:0,subMenusSubOffsetX:0,subMenusSubOffsetY:0,subMenusMinWidth:"10em",subMenusMaxWidth:"20em",subIndicators:true,subIndicatorsPos:"prepend",subIndicatorsText:"+",scrollStep:30,scrollAccelerate:true,showTimeout:250,hideTimeout:500,showDuration:0,showFunction:null,hideDuration:0,hideFunction:function(m,l){m.fadeOut(200,l)},collapsibleShowDuration:0,collapsibleShowFunction:function(m,l){m.slideDown(200,l)},collapsibleHideDuration:0,collapsibleHideFunction:function(m,l){m.slideUp(200,l)},showOnClick:false,hideOnClick:true,noMouseOver:false,keepInViewport:true,keepHighlighted:true,markCurrentItem:false,markCurrentTree:true,rightToLeftSubMenus:false,bottomToTopSubMenus:false,overlapControlsInIE:true};return a}));
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: mimalloc-doc.h Source File</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('mimalloc-doc_8h_source.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="headertitle">
+<div class="title">mimalloc-doc.h</div> </div>
+</div><!--header-->
+<div class="contents">
+<div class="fragment"><div class="line"><a name="l00001"></a><span class="lineno"> 1</span> <span class="comment">/* ----------------------------------------------------------------------------</span></div><div class="line"><a name="l00002"></a><span class="lineno"> 2</span> <span class="comment">Copyright (c) 2018, Microsoft Research, Daan Leijen</span></div><div class="line"><a name="l00003"></a><span class="lineno"> 3</span> <span class="comment">This is free software; you can redistribute it and/or modify it under the</span></div><div class="line"><a name="l00004"></a><span class="lineno"> 4</span> <span class="comment">terms of the MIT license. A copy of the license can be found in the file</span></div><div class="line"><a name="l00005"></a><span class="lineno"> 5</span> <span class="comment">"LICENSE" at the root of this distribution.</span></div><div class="line"><a name="l00006"></a><span class="lineno"> 6</span> <span class="comment">-----------------------------------------------------------------------------*/</span></div><div class="line"><a name="l00007"></a><span class="lineno"> 7</span> </div><div class="line"><a name="l00008"></a><span class="lineno"> 8</span> <span class="preprocessor">#error "documentation file only!"</span></div><div class="line"><a name="l00009"></a><span class="lineno"> 9</span> </div><div class="line"><a name="l00010"></a><span class="lineno"> 10</span> </div><div class="line"><a name="l00083"></a><span class="lineno"> 83</span> </div><div class="line"><a name="l00087"></a><span class="lineno"> 87</span> </div><div class="line"><a name="l00091"></a><span class="lineno"> 91</span> <span class="keywordtype">void</span> <a class="code" href="group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95">mi_free</a>(<span class="keywordtype">void</span>* p);</div><div class="line"><a name="l00092"></a><span class="lineno"> 92</span> </div><div class="line"><a name="l00097"></a><span class="lineno"> 97</span> <span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a">mi_malloc</a>(<span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00098"></a><span class="lineno"> 98</span> </div><div class="line"><a name="l00103"></a><span class="lineno"> 103</span> <span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#gafdd9d8bb2986e668ba9884f28af38000">mi_zalloc</a>(<span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00104"></a><span class="lineno"> 104</span> </div><div class="line"><a name="l00114"></a><span class="lineno"> 114</span> <span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d">mi_calloc</a>(<span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00115"></a><span class="lineno"> 115</span> </div><div class="line"><a name="l00128"></a><span class="lineno"> 128</span> <span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6">mi_realloc</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><a name="l00129"></a><span class="lineno"> 129</span> </div><div class="line"><a name="l00140"></a><span class="lineno"> 140</span> <span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#ga23a0fbb452b5dce8e31fab1a1958cacc">mi_recalloc</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00141"></a><span class="lineno"> 141</span> </div><div class="line"><a name="l00155"></a><span class="lineno"> 155</span> <span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#gaaee66a1d483c3e28f585525fb96707e4">mi_expand</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><a name="l00156"></a><span class="lineno"> 156</span> </div><div class="line"><a name="l00166"></a><span class="lineno"> 166</span> <span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6">mi_mallocn</a>(<span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00167"></a><span class="lineno"> 167</span> </div><div class="line"><a name="l00177"></a><span class="lineno"> 177</span> <span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853">mi_reallocn</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00178"></a><span class="lineno"> 178</span> </div><div class="line"><a name="l00195"></a><span class="lineno"> 195</span> <span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#gafe68ac7c5e24a65cd55c9d6b152211a0">mi_reallocf</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><a name="l00196"></a><span class="lineno"> 196</span> </div><div class="line"><a name="l00197"></a><span class="lineno"> 197</span> </div><div class="line"><a name="l00206"></a><span class="lineno"> 206</span> <span class="keywordtype">char</span>* <a class="code" href="group__malloc.html#gac7cffe13f1f458ed16789488bf92b9b2">mi_strdup</a>(<span class="keyword">const</span> <span class="keywordtype">char</span>* s);</div><div class="line"><a name="l00207"></a><span class="lineno"> 207</span> </div><div class="line"><a name="l00217"></a><span class="lineno"> 217</span> <span class="keywordtype">char</span>* <a class="code" href="group__malloc.html#gaaabf971c2571891433477e2d21a35266">mi_strndup</a>(<span class="keyword">const</span> <span class="keywordtype">char</span>* s, <span class="keywordtype">size_t</span> n);</div><div class="line"><a name="l00218"></a><span class="lineno"> 218</span> </div><div class="line"><a name="l00231"></a><span class="lineno"> 231</span> <span class="keywordtype">char</span>* <a class="code" href="group__malloc.html#ga08cec32dd5bbe7da91c78d19f1b5bebe">mi_realpath</a>(<span class="keyword">const</span> <span class="keywordtype">char</span>* fname, <span class="keywordtype">char</span>* resolved_name);</div><div class="line"><a name="l00232"></a><span class="lineno"> 232</span> </div><div class="line"><a name="l00234"></a><span class="lineno"> 234</span> </div><div class="line"><a name="l00235"></a><span class="lineno"> 235</span> <span class="comment">// ------------------------------------------------------</span></div><div class="line"><a name="l00236"></a><span class="lineno"> 236</span> <span class="comment">// Extended functionality</span></div><div class="line"><a name="l00237"></a><span class="lineno"> 237</span> <span class="comment">// ------------------------------------------------------</span></div><div class="line"><a name="l00238"></a><span class="lineno"> 238</span> </div><div class="line"><a name="l00242"></a><span class="lineno"> 242</span> </div><div class="line"><a name="l00245"></a><span class="lineno"><a class="line" href="group__extended.html#ga1ea64283508718d9d645c38efc2f4305"> 245</a></span> <span class="preprocessor">#define MI_SMALL_SIZE_MAX (128*sizeof(void*))</span></div><div class="line"><a name="l00246"></a><span class="lineno"> 246</span> </div><div class="line"><a name="l00254"></a><span class="lineno"> 254</span> <span class="keywordtype">void</span>* <a class="code" href="group__extended.html#ga7136c2e55cb22c98ecf95d08d6debb99">mi_malloc_small</a>(<span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00255"></a><span class="lineno"> 255</span> </div><div class="line"><a name="l00263"></a><span class="lineno"> 263</span> <span class="keywordtype">void</span>* <a class="code" href="group__extended.html#ga220f29f40a44404b0061c15bc1c31152">mi_zalloc_small</a>(<span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00264"></a><span class="lineno"> 264</span> </div><div class="line"><a name="l00279"></a><span class="lineno"> 279</span> <span class="keywordtype">size_t</span> <a class="code" href="group__extended.html#ga089c859d9eddc5f9b4bd946cd53cebee">mi_usable_size</a>(<span class="keywordtype">void</span>* p);</div><div class="line"><a name="l00280"></a><span class="lineno"> 280</span> </div><div class="line"><a name="l00290"></a><span class="lineno"> 290</span> <span class="keywordtype">size_t</span> <a class="code" href="group__extended.html#gac057927cd06c854b45fe7847e921bd47">mi_good_size</a>(<span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00291"></a><span class="lineno"> 291</span> </div><div class="line"><a name="l00299"></a><span class="lineno"> 299</span> <span class="keywordtype">void</span> <a class="code" href="group__extended.html#ga421430e2226d7d468529cec457396756">mi_collect</a>(<span class="keywordtype">bool</span> force);</div><div class="line"><a name="l00300"></a><span class="lineno"> 300</span> </div><div class="line"><a name="l00305"></a><span class="lineno"> 305</span> <span class="keywordtype">void</span> <a class="code" href="group__extended.html#ga2d126e5c62d3badc35445e5d84166df2">mi_stats_print</a>(<span class="keywordtype">void</span>* out);</div><div class="line"><a name="l00306"></a><span class="lineno"> 306</span> </div><div class="line"><a name="l00312"></a><span class="lineno"> 312</span> <span class="keywordtype">void</span> <a class="code" href="group__extended.html#ga537f13b299ddf801e49a5a94fde02c79">mi_stats_print_out</a>(<a class="code" href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c">mi_output_fun</a>* out, <span class="keywordtype">void</span>* arg);</div><div class="line"><a name="l00313"></a><span class="lineno"> 313</span> </div><div class="line"><a name="l00315"></a><span class="lineno"> 315</span> <span class="keywordtype">void</span> <a class="code" href="group__extended.html#ga3bb8468b8cfcc6e2a61d98aee85c5f99">mi_stats_reset</a>(<span class="keywordtype">void</span>);</div><div class="line"><a name="l00316"></a><span class="lineno"> 316</span> </div><div class="line"><a name="l00318"></a><span class="lineno"> 318</span> <span class="keywordtype">void</span> <a class="code" href="group__extended.html#ga854b1de8cb067c7316286c28b2fcd3d1">mi_stats_merge</a>(<span class="keywordtype">void</span>);</div><div class="line"><a name="l00319"></a><span class="lineno"> 319</span> </div><div class="line"><a name="l00323"></a><span class="lineno"> 323</span> <span class="keywordtype">void</span> <a class="code" href="group__extended.html#gaf8e73efc2cbca9ebfdfb166983a04c17">mi_thread_init</a>(<span class="keywordtype">void</span>);</div><div class="line"><a name="l00324"></a><span class="lineno"> 324</span> </div><div class="line"><a name="l00329"></a><span class="lineno"> 329</span> <span class="keywordtype">void</span> <a class="code" href="group__extended.html#ga0ae4581e85453456a0d658b2b98bf7bf">mi_thread_done</a>(<span class="keywordtype">void</span>);</div><div class="line"><a name="l00330"></a><span class="lineno"> 330</span> </div><div class="line"><a name="l00336"></a><span class="lineno"> 336</span> <span class="keywordtype">void</span> <a class="code" href="group__extended.html#gab1dac8476c46cb9eecab767eb40c1525">mi_thread_stats_print_out</a>(<a class="code" href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c">mi_output_fun</a>* out, <span class="keywordtype">void</span>* arg);</div><div class="line"><a name="l00337"></a><span class="lineno"> 337</span> </div><div class="line"><a name="l00344"></a><span class="lineno"><a class="line" href="group__extended.html#ga299dae78d25ce112e384a98b7309c5be"> 344</a></span> <span class="keyword">typedef</span> void (<a class="code" href="group__extended.html#ga299dae78d25ce112e384a98b7309c5be">mi_deferred_free_fun</a>)(<span class="keywordtype">bool</span> force, <span class="keywordtype">unsigned</span> <span class="keywordtype">long</span> <span class="keywordtype">long</span> heartbeat, <span class="keywordtype">void</span>* arg);</div><div class="line"><a name="l00345"></a><span class="lineno"> 345</span> </div><div class="line"><a name="l00361"></a><span class="lineno"> 361</span> <span class="keywordtype">void</span> <a class="code" href="group__extended.html#ga3460a6ca91af97be4058f523d3cb8ece">mi_register_deferred_free</a>(<a class="code" href="group__extended.html#ga299dae78d25ce112e384a98b7309c5be">mi_deferred_free_fun</a>* deferred_free, <span class="keywordtype">void</span>* arg);</div><div class="line"><a name="l00362"></a><span class="lineno"> 362</span> </div><div class="line"><a name="l00368"></a><span class="lineno"><a class="line" href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c"> 368</a></span> <span class="keyword">typedef</span> void (<a class="code" href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c">mi_output_fun</a>)(<span class="keyword">const</span> <span class="keywordtype">char</span>* msg, <span class="keywordtype">void</span>* arg);</div><div class="line"><a name="l00369"></a><span class="lineno"> 369</span> </div><div class="line"><a name="l00376"></a><span class="lineno"> 376</span> <span class="keywordtype">void</span> <a class="code" href="group__extended.html#gae5b17ff027cd2150b43a33040250cf3f">mi_register_output</a>(<a class="code" href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c">mi_output_fun</a>* out, <span class="keywordtype">void</span>* arg);</div><div class="line"><a name="l00377"></a><span class="lineno"> 377</span> </div><div class="line"><a name="l00383"></a><span class="lineno"><a class="line" href="group__extended.html#ga251d369cda3f1c2a955c555486ed90e5"> 383</a></span> <span class="keyword">typedef</span> void (<a class="code" href="group__extended.html#ga251d369cda3f1c2a955c555486ed90e5">mi_error_fun</a>)(<span class="keywordtype">int</span> err, <span class="keywordtype">void</span>* arg);</div><div class="line"><a name="l00384"></a><span class="lineno"> 384</span> </div><div class="line"><a name="l00400"></a><span class="lineno"> 400</span> <span class="keywordtype">void</span> <a class="code" href="group__extended.html#gaa1d55e0e894be240827e5d87ec3a1f45">mi_register_error</a>(<a class="code" href="group__extended.html#ga251d369cda3f1c2a955c555486ed90e5">mi_error_fun</a>* errfun, <span class="keywordtype">void</span>* arg);</div><div class="line"><a name="l00401"></a><span class="lineno"> 401</span> </div><div class="line"><a name="l00406"></a><span class="lineno"> 406</span> <span class="keywordtype">bool</span> <a class="code" href="group__extended.html#ga5f071b10d4df1c3658e04e7fd67a94e6">mi_is_in_heap_region</a>(<span class="keyword">const</span> <span class="keywordtype">void</span>* p);</div><div class="line"><a name="l00407"></a><span class="lineno"> 407</span> </div><div class="line"><a name="l00408"></a><span class="lineno"> 408</span> </div><div class="line"><a name="l00421"></a><span class="lineno"> 421</span> <span class="keywordtype">int</span> <a class="code" href="group__extended.html#ga3132f521fb756fc0e8ec0b74fb58df50">mi_reserve_huge_os_pages_interleave</a>(<span class="keywordtype">size_t</span> pages, <span class="keywordtype">size_t</span> numa_nodes, <span class="keywordtype">size_t</span> timeout_msecs);</div><div class="line"><a name="l00422"></a><span class="lineno"> 422</span> </div><div class="line"><a name="l00435"></a><span class="lineno"> 435</span> <span class="keywordtype">int</span> <a class="code" href="group__extended.html#ga7795a13d20087447281858d2c771cca1">mi_reserve_huge_os_pages_at</a>(<span class="keywordtype">size_t</span> pages, <span class="keywordtype">int</span> numa_node, <span class="keywordtype">size_t</span> timeout_msecs);</div><div class="line"><a name="l00436"></a><span class="lineno"> 436</span> </div><div class="line"><a name="l00437"></a><span class="lineno"> 437</span> </div><div class="line"><a name="l00442"></a><span class="lineno"> 442</span> <span class="keywordtype">bool</span> <a class="code" href="group__extended.html#gaad25050b19f30cd79397b227e0157a3f">mi_is_redirected</a>();</div><div class="line"><a name="l00443"></a><span class="lineno"> 443</span> </div><div class="line"><a name="l00444"></a><span class="lineno"> 444</span> </div><div class="line"><a name="l00446"></a><span class="lineno"> 446</span> </div><div class="line"><a name="l00447"></a><span class="lineno"> 447</span> <span class="comment">// ------------------------------------------------------</span></div><div class="line"><a name="l00448"></a><span class="lineno"> 448</span> <span class="comment">// Aligned allocation</span></div><div class="line"><a name="l00449"></a><span class="lineno"> 449</span> <span class="comment">// ------------------------------------------------------</span></div><div class="line"><a name="l00450"></a><span class="lineno"> 450</span> </div><div class="line"><a name="l00456"></a><span class="lineno"> 456</span> </div><div class="line"><a name="l00469"></a><span class="lineno"> 469</span> <span class="keywordtype">void</span>* <a class="code" href="group__aligned.html#ga68930196751fa2cca9e1fd0d71bade56">mi_malloc_aligned</a>(<span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00470"></a><span class="lineno"> 470</span> <span class="keywordtype">void</span>* <a class="code" href="group__aligned.html#ga0cadbcf5b89a7b6fb171bc8df8734819">mi_zalloc_aligned</a>(<span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00471"></a><span class="lineno"> 471</span> <span class="keywordtype">void</span>* <a class="code" href="group__aligned.html#ga53dddb4724042a90315b94bc268fb4c9">mi_calloc_aligned</a>(<span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00472"></a><span class="lineno"> 472</span> <span class="keywordtype">void</span>* <a class="code" href="group__aligned.html#ga4028d1cf4aa4c87c880747044a8322ae">mi_realloc_aligned</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00473"></a><span class="lineno"> 473</span> </div><div class="line"><a name="l00484"></a><span class="lineno"> 484</span> <span class="keywordtype">void</span>* <a class="code" href="group__aligned.html#ga5850da130c936bd77db039dcfbc8295d">mi_malloc_aligned_at</a>(<span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> offset);</div><div class="line"><a name="l00485"></a><span class="lineno"> 485</span> <span class="keywordtype">void</span>* <a class="code" href="group__aligned.html#ga5f8c2353766db522565e642fafd8a3f8">mi_zalloc_aligned_at</a>(<span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> offset);</div><div class="line"><a name="l00486"></a><span class="lineno"> 486</span> <span class="keywordtype">void</span>* <a class="code" href="group__aligned.html#ga08647c4593f3b2eef24a919a73eba3a3">mi_calloc_aligned_at</a>(<span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> offset);</div><div class="line"><a name="l00487"></a><span class="lineno"> 487</span> <span class="keywordtype">void</span>* <a class="code" href="group__aligned.html#gaf66a9ae6c6f08bd6be6fb6ea771faffb">mi_realloc_aligned_at</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> offset);</div><div class="line"><a name="l00488"></a><span class="lineno"> 488</span> </div><div class="line"><a name="l00490"></a><span class="lineno"> 490</span> </div><div class="line"><a name="l00496"></a><span class="lineno"> 496</span> </div><div class="line"><a name="l00501"></a><span class="lineno"> 501</span> <span class="keyword">struct </span>mi_heap_s;</div><div class="line"><a name="l00502"></a><span class="lineno"> 502</span> </div><div class="line"><a name="l00507"></a><span class="lineno"><a class="line" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2"> 507</a></span> <span class="keyword">typedef</span> <span class="keyword">struct </span>mi_heap_s <a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>;</div><div class="line"><a name="l00508"></a><span class="lineno"> 508</span> </div><div class="line"><a name="l00510"></a><span class="lineno"> 510</span> <a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* <a class="code" href="group__heap.html#ga766f672ba56f2fbfeb9d9dbb0b7f6b11">mi_heap_new</a>();</div><div class="line"><a name="l00511"></a><span class="lineno"> 511</span> </div><div class="line"><a name="l00519"></a><span class="lineno"> 519</span> <span class="keywordtype">void</span> <a class="code" href="group__heap.html#ga2ab1af8d438819b55319c7ef51d1e409">mi_heap_delete</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap);</div><div class="line"><a name="l00520"></a><span class="lineno"> 520</span> </div><div class="line"><a name="l00528"></a><span class="lineno"> 528</span> <span class="keywordtype">void</span> <a class="code" href="group__heap.html#ga9f9c0844edb9717f4feacd79116b8e0d">mi_heap_destroy</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap);</div><div class="line"><a name="l00529"></a><span class="lineno"> 529</span> </div><div class="line"><a name="l00533"></a><span class="lineno"> 533</span> <a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* <a class="code" href="group__heap.html#gab8631ec88c8d26641b68b5d25dcd4422">mi_heap_set_default</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap);</div><div class="line"><a name="l00534"></a><span class="lineno"> 534</span> </div><div class="line"><a name="l00537"></a><span class="lineno"> 537</span> <a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* <a class="code" href="group__heap.html#ga8db4cbb87314a989a9a187464d6b5e05">mi_heap_get_default</a>();</div><div class="line"><a name="l00538"></a><span class="lineno"> 538</span> </div><div class="line"><a name="l00544"></a><span class="lineno"> 544</span> <a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* <a class="code" href="group__heap.html#ga5d03fbe062ffcf38f0f417fd968357fc">mi_heap_get_backing</a>();</div><div class="line"><a name="l00545"></a><span class="lineno"> 545</span> </div><div class="line"><a name="l00547"></a><span class="lineno"> 547</span> <span class="keywordtype">void</span> <a class="code" href="group__heap.html#ga7922f7495cde30b1984d0e6072419298">mi_heap_collect</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">bool</span> force);</div><div class="line"><a name="l00548"></a><span class="lineno"> 548</span> </div><div class="line"><a name="l00551"></a><span class="lineno"> 551</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#ga9cbed01e42c0647907295de92c3fa296">mi_heap_malloc</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00552"></a><span class="lineno"> 552</span> </div><div class="line"><a name="l00556"></a><span class="lineno"> 556</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#gaa1a1c7a1f4da6826b5a25b70ef878368">mi_heap_malloc_small</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00557"></a><span class="lineno"> 557</span> </div><div class="line"><a name="l00560"></a><span class="lineno"> 560</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#ga903104592c8ed53417a3762da6241133">mi_heap_zalloc</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00561"></a><span class="lineno"> 561</span> </div><div class="line"><a name="l00564"></a><span class="lineno"> 564</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#gaa6702b3c48e9e53e50e81b36f5011d55">mi_heap_calloc</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00565"></a><span class="lineno"> 565</span> </div><div class="line"><a name="l00568"></a><span class="lineno"> 568</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#ga851da6c43fe0b71c1376cee8aef90db0">mi_heap_mallocn</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00569"></a><span class="lineno"> 569</span> </div><div class="line"><a name="l00572"></a><span class="lineno"> 572</span> <span class="keywordtype">char</span>* <a class="code" href="group__heap.html#ga139d6b09dbf50c3c2523d0f4d1cfdeb5">mi_heap_strdup</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keyword">const</span> <span class="keywordtype">char</span>* s);</div><div class="line"><a name="l00573"></a><span class="lineno"> 573</span> </div><div class="line"><a name="l00576"></a><span class="lineno"> 576</span> <span class="keywordtype">char</span>* <a class="code" href="group__heap.html#ga8e3dbd46650dd26573cf307a2c8f1f5a">mi_heap_strndup</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keyword">const</span> <span class="keywordtype">char</span>* s, <span class="keywordtype">size_t</span> n);</div><div class="line"><a name="l00577"></a><span class="lineno"> 577</span> </div><div class="line"><a name="l00580"></a><span class="lineno"> 580</span> <span class="keywordtype">char</span>* <a class="code" href="group__heap.html#ga00e95ba1e01acac3cfd95bb7a357a6f0">mi_heap_realpath</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keyword">const</span> <span class="keywordtype">char</span>* fname, <span class="keywordtype">char</span>* resolved_name);</div><div class="line"><a name="l00581"></a><span class="lineno"> 581</span> </div><div class="line"><a name="l00582"></a><span class="lineno"> 582</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#gaaef3395f66be48f37bdc8322509c5d81">mi_heap_realloc</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><a name="l00583"></a><span class="lineno"> 583</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#gac74e94ad9b0c9b57c1c4d88b8825b7a8">mi_heap_reallocn</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00584"></a><span class="lineno"> 584</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#ga4a21070eb4e7cce018133c8d5f4b0527">mi_heap_reallocf</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><a name="l00585"></a><span class="lineno"> 585</span> </div><div class="line"><a name="l00586"></a><span class="lineno"> 586</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#gab5b87e1805306f70df38789fcfcf6653">mi_heap_malloc_aligned</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00587"></a><span class="lineno"> 587</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#ga23acd7680fb0976dde3783254c6c874b">mi_heap_malloc_aligned_at</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> offset);</div><div class="line"><a name="l00588"></a><span class="lineno"> 588</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#gaa450a59c6c7ae5fdbd1c2b80a8329ef0">mi_heap_zalloc_aligned</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00589"></a><span class="lineno"> 589</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#ga45fb43a62776fbebbdf1edd99b527954">mi_heap_zalloc_aligned_at</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> offset);</div><div class="line"><a name="l00590"></a><span class="lineno"> 590</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#ga4af03a6e2b93fae77424d93f889705c3">mi_heap_calloc_aligned</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00591"></a><span class="lineno"> 591</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#ga08ca6419a5c057a4d965868998eef487">mi_heap_calloc_aligned_at</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> offset);</div><div class="line"><a name="l00592"></a><span class="lineno"> 592</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#gafc603b696bd14cae6da28658f950d98c">mi_heap_realloc_aligned</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00593"></a><span class="lineno"> 593</span> <span class="keywordtype">void</span>* <a class="code" href="group__heap.html#gaf96c788a1bf553fe2d371de9365e047c">mi_heap_realloc_aligned_at</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> offset);</div><div class="line"><a name="l00594"></a><span class="lineno"> 594</span> </div><div class="line"><a name="l00596"></a><span class="lineno"> 596</span> </div><div class="line"><a name="l00597"></a><span class="lineno"> 597</span> </div><div class="line"><a name="l00606"></a><span class="lineno"> 606</span> </div><div class="line"><a name="l00607"></a><span class="lineno"> 607</span> <span class="keywordtype">void</span>* <a class="code" href="group__zeroinit.html#ga8c292e142110229a2980b37ab036dbc6">mi_rezalloc</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><a name="l00608"></a><span class="lineno"> 608</span> <span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#ga23a0fbb452b5dce8e31fab1a1958cacc">mi_recalloc</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newcount, <span class="keywordtype">size_t</span> size) ;</div><div class="line"><a name="l00609"></a><span class="lineno"> 609</span> </div><div class="line"><a name="l00610"></a><span class="lineno"> 610</span> <span class="keywordtype">void</span>* <a class="code" href="group__zeroinit.html#gacd71a7bce96aab38ae6de17af2eb2cf0">mi_rezalloc_aligned</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00611"></a><span class="lineno"> 611</span> <span class="keywordtype">void</span>* <a class="code" href="group__zeroinit.html#gae8b358c417e61d5307da002702b0a8e1">mi_rezalloc_aligned_at</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> offset);</div><div class="line"><a name="l00612"></a><span class="lineno"> 612</span> <span class="keywordtype">void</span>* <a class="code" href="group__zeroinit.html#ga3e7e5c291acf1c7fd7ffd9914a9f945f">mi_recalloc_aligned</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newcount, <span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00613"></a><span class="lineno"> 613</span> <span class="keywordtype">void</span>* <a class="code" href="group__zeroinit.html#ga4ff5e92ad73585418a072c9d059e5cf9">mi_recalloc_aligned_at</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newcount, <span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> offset);</div><div class="line"><a name="l00614"></a><span class="lineno"> 614</span> </div><div class="line"><a name="l00615"></a><span class="lineno"> 615</span> <span class="keywordtype">void</span>* <a class="code" href="group__zeroinit.html#gacfad83f14eb5d6a42a497a898e19fc76">mi_heap_rezalloc</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><a name="l00616"></a><span class="lineno"> 616</span> <span class="keywordtype">void</span>* <a class="code" href="group__zeroinit.html#ga8648c5fbb22a80f0262859099f06dfbd">mi_heap_recalloc</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newcount, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00617"></a><span class="lineno"> 617</span> </div><div class="line"><a name="l00618"></a><span class="lineno"> 618</span> <span class="keywordtype">void</span>* <a class="code" href="group__zeroinit.html#ga375fa8a611c51905e592d5d467c49664">mi_heap_rezalloc_aligned</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00619"></a><span class="lineno"> 619</span> <span class="keywordtype">void</span>* <a class="code" href="group__zeroinit.html#gac90da54fa7e5d10bdc97ce0b51dce2eb">mi_heap_rezalloc_aligned_at</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> offset);</div><div class="line"><a name="l00620"></a><span class="lineno"> 620</span> <span class="keywordtype">void</span>* <a class="code" href="group__zeroinit.html#ga9f3f999396c8f77ca5e80e7b40ac29e3">mi_heap_recalloc_aligned</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newcount, <span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00621"></a><span class="lineno"> 621</span> <span class="keywordtype">void</span>* <a class="code" href="group__zeroinit.html#ga496452c96f1de8c500be9fddf52edaf7">mi_heap_recalloc_aligned_at</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newcount, <span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> offset);</div><div class="line"><a name="l00622"></a><span class="lineno"> 622</span> </div><div class="line"><a name="l00624"></a><span class="lineno"> 624</span> </div><div class="line"><a name="l00633"></a><span class="lineno"> 633</span> </div><div class="line"><a name="l00645"></a><span class="lineno"><a class="line" href="group__typed.html#ga0619a62c5fd886f1016030abe91f0557"> 645</a></span> <span class="preprocessor">#define mi_malloc_tp(tp) ((tp*)mi_malloc(sizeof(tp)))</span></div><div class="line"><a name="l00646"></a><span class="lineno"> 646</span> </div><div class="line"><a name="l00648"></a><span class="lineno"><a class="line" href="group__typed.html#gac77a61bdaf680a803785fe307820b48c"> 648</a></span> <span class="preprocessor">#define mi_zalloc_tp(tp) ((tp*)mi_zalloc(sizeof(tp)))</span></div><div class="line"><a name="l00649"></a><span class="lineno"> 649</span> </div><div class="line"><a name="l00651"></a><span class="lineno"><a class="line" href="group__typed.html#gae80c47c9d4cab10961fff1a8ac98fc07"> 651</a></span> <span class="preprocessor">#define mi_calloc_tp(tp,count) ((tp*)mi_calloc(count,sizeof(tp)))</span></div><div class="line"><a name="l00652"></a><span class="lineno"> 652</span> </div><div class="line"><a name="l00654"></a><span class="lineno"><a class="line" href="group__typed.html#gae5cb6e0fafc9f23169c5622e077afe8b"> 654</a></span> <span class="preprocessor">#define mi_mallocn_tp(tp,count) ((tp*)mi_mallocn(count,sizeof(tp)))</span></div><div class="line"><a name="l00655"></a><span class="lineno"> 655</span> </div><div class="line"><a name="l00657"></a><span class="lineno"><a class="line" href="group__typed.html#ga1158b49a55dfa81f58a4426a7578f523"> 657</a></span> <span class="preprocessor">#define mi_reallocn_tp(p,tp,count) ((tp*)mi_reallocn(p,count,sizeof(tp)))</span></div><div class="line"><a name="l00658"></a><span class="lineno"> 658</span> </div><div class="line"><a name="l00660"></a><span class="lineno"><a class="line" href="group__typed.html#ga653bcb24ac495bc19940ecd6898f9cd7"> 660</a></span> <span class="preprocessor">#define mi_heap_malloc_tp(hp,tp) ((tp*)mi_heap_malloc(hp,sizeof(tp)))</span></div><div class="line"><a name="l00661"></a><span class="lineno"> 661</span> </div><div class="line"><a name="l00663"></a><span class="lineno"><a class="line" href="group__typed.html#gad6e87e86e994aa14416ae9b5d4c188fe"> 663</a></span> <span class="preprocessor">#define mi_heap_zalloc_tp(hp,tp) ((tp*)mi_heap_zalloc(hp,sizeof(tp)))</span></div><div class="line"><a name="l00664"></a><span class="lineno"> 664</span> </div><div class="line"><a name="l00666"></a><span class="lineno"><a class="line" href="group__typed.html#ga4e5d1f1707c90e5f55e023ac5f45fe74"> 666</a></span> <span class="preprocessor">#define mi_heap_calloc_tp(hp,tp,count) ((tp*)mi_heap_calloc(hp,count,sizeof(tp)))</span></div><div class="line"><a name="l00667"></a><span class="lineno"> 667</span> </div><div class="line"><a name="l00669"></a><span class="lineno"><a class="line" href="group__typed.html#ga6b75cb9c4b9c647661d0924552dc6e83"> 669</a></span> <span class="preprocessor">#define mi_heap_mallocn_tp(hp,tp,count) ((tp*)mi_heap_mallocn(hp,count,sizeof(tp)))</span></div><div class="line"><a name="l00670"></a><span class="lineno"> 670</span> </div><div class="line"><a name="l00672"></a><span class="lineno"><a class="line" href="group__typed.html#gaf213d5422ec35e7f6caad827c79bc948"> 672</a></span> <span class="preprocessor">#define mi_heap_reallocn_tp(hp,p,tp,count) ((tp*)mi_heap_reallocn(p,count,sizeof(tp)))</span></div><div class="line"><a name="l00673"></a><span class="lineno"> 673</span> </div><div class="line"><a name="l00675"></a><span class="lineno"><a class="line" href="group__typed.html#ga3e50a1600958fcaf1a7f3560c9174f9e"> 675</a></span> <span class="preprocessor">#define mi_heap_recalloc_tp(hp,p,tp,count) ((tp*)mi_heap_recalloc(p,count,sizeof(tp)))</span></div><div class="line"><a name="l00676"></a><span class="lineno"> 676</span> </div><div class="line"><a name="l00678"></a><span class="lineno"> 678</span> </div><div class="line"><a name="l00684"></a><span class="lineno"> 684</span> </div><div class="line"><a name="l00691"></a><span class="lineno"> 691</span> <span class="keywordtype">bool</span> <a class="code" href="group__analysis.html#gaa862aa8ed8d57d84cae41fc1022d71af">mi_heap_contains_block</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keyword">const</span> <span class="keywordtype">void</span>* p);</div><div class="line"><a name="l00692"></a><span class="lineno"> 692</span> </div><div class="line"><a name="l00701"></a><span class="lineno"> 701</span> <span class="keywordtype">bool</span> <a class="code" href="group__analysis.html#ga0d67c1789faaa15ff366c024fcaf6377">mi_heap_check_owned</a>(<a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keyword">const</span> <span class="keywordtype">void</span>* p);</div><div class="line"><a name="l00702"></a><span class="lineno"> 702</span> </div><div class="line"><a name="l00710"></a><span class="lineno"> 710</span> <span class="keywordtype">bool</span> <a class="code" href="group__analysis.html#ga628c237489c2679af84a4d0d143b3dd5">mi_check_owned</a>(<span class="keyword">const</span> <span class="keywordtype">void</span>* p);</div><div class="line"><a name="l00711"></a><span class="lineno"> 711</span> </div><div class="line"><a name="l00714"></a><span class="lineno"><a class="line" href="group__analysis.html"> 714</a></span> <span class="keyword">typedef</span> <span class="keyword">struct </span>mi_heap_area_s {</div><div class="line"><a name="l00715"></a><span class="lineno"><a class="line" href="group__analysis.html#ae0085e6e1cf059a4eb7767e30e9991b8"> 715</a></span>  <span class="keywordtype">void</span>* <a class="code" href="group__analysis.html#ae0085e6e1cf059a4eb7767e30e9991b8">blocks</a>; </div><div class="line"><a name="l00716"></a><span class="lineno"><a class="line" href="group__analysis.html#ae848a3e6840414891035423948ca0383"> 716</a></span>  <span class="keywordtype">size_t</span> <a class="code" href="group__analysis.html#ae848a3e6840414891035423948ca0383">reserved</a>; </div><div class="line"><a name="l00717"></a><span class="lineno"><a class="line" href="group__analysis.html#ab47526df656d8837ec3e97f11b83f835"> 717</a></span>  <span class="keywordtype">size_t</span> <a class="code" href="group__analysis.html#ab47526df656d8837ec3e97f11b83f835">committed</a>; </div><div class="line"><a name="l00718"></a><span class="lineno"><a class="line" href="group__analysis.html#ab820302c5cd0df133eb8e51650a008b4"> 718</a></span>  <span class="keywordtype">size_t</span> <a class="code" href="group__analysis.html#ab820302c5cd0df133eb8e51650a008b4">used</a>; </div><div class="line"><a name="l00719"></a><span class="lineno"><a class="line" href="group__analysis.html#a332a6c14d736a99699d5453a1cb04b41"> 719</a></span>  <span class="keywordtype">size_t</span> <a class="code" href="group__analysis.html#a332a6c14d736a99699d5453a1cb04b41">block_size</a>; </div><div class="line"><a name="l00720"></a><span class="lineno"> 720</span> } <a class="code" href="group__analysis.html#structmi__heap__area__t">mi_heap_area_t</a>;</div><div class="line"><a name="l00721"></a><span class="lineno"> 721</span> </div><div class="line"><a name="l00729"></a><span class="lineno"><a class="line" href="group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65"> 729</a></span> <span class="keyword">typedef</span> bool (<a class="code" href="group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65">mi_block_visit_fun</a>)(<span class="keyword">const</span> <a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keyword">const</span> <a class="code" href="group__analysis.html#structmi__heap__area__t">mi_heap_area_t</a>* area, <span class="keywordtype">void</span>* block, <span class="keywordtype">size_t</span> block_size, <span class="keywordtype">void</span>* arg);</div><div class="line"><a name="l00730"></a><span class="lineno"> 730</span> </div><div class="line"><a name="l00742"></a><span class="lineno"> 742</span> <span class="keywordtype">bool</span> <a class="code" href="group__analysis.html#ga70c46687dc6e9dc98b232b02646f8bed">mi_heap_visit_blocks</a>(<span class="keyword">const</span> <a class="code" href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a>* heap, <span class="keywordtype">bool</span> visit_all_blocks, <a class="code" href="group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65">mi_block_visit_fun</a>* visitor, <span class="keywordtype">void</span>* arg);</div><div class="line"><a name="l00743"></a><span class="lineno"> 743</span> </div><div class="line"><a name="l00745"></a><span class="lineno"> 745</span> </div><div class="line"><a name="l00751"></a><span class="lineno"> 751</span> </div><div class="line"><a name="l00753"></a><span class="lineno"><a class="line" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c"> 753</a></span> <span class="keyword">typedef</span> <span class="keyword">enum</span> mi_option_e {</div><div class="line"><a name="l00754"></a><span class="lineno"> 754</span>  <span class="comment">// stable options</span></div><div class="line"><a name="l00755"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0"> 755</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0">mi_option_show_errors</a>, </div><div class="line"><a name="l00756"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda"> 756</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda">mi_option_show_stats</a>, </div><div class="line"><a name="l00757"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777"> 757</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777">mi_option_verbose</a>, </div><div class="line"><a name="l00758"></a><span class="lineno"> 758</span>  <span class="comment">// the following options are experimental</span></div><div class="line"><a name="l00759"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b"> 759</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b">mi_option_eager_commit</a>, </div><div class="line"><a name="l00760"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad"> 760</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad">mi_option_eager_region_commit</a>, </div><div class="line"><a name="l00761"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e"> 761</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e">mi_option_large_os_pages</a>, </div><div class="line"><a name="l00762"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2"> 762</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2">mi_option_reserve_huge_os_pages</a>, </div><div class="line"><a name="l00763"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1"> 763</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1">mi_option_segment_cache</a>, </div><div class="line"><a name="l00764"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968"> 764</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968">mi_option_page_reset</a>, </div><div class="line"><a name="l00765"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d"> 765</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d">mi_option_segment_reset</a>, </div><div class="line"><a name="l00766"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5"> 766</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5">mi_option_reset_delay</a>, </div><div class="line"><a name="l00767"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74"> 767</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74">mi_option_use_numa_nodes</a>, </div><div class="line"><a name="l00768"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536"> 768</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536">mi_option_reset_decommits</a>, </div><div class="line"><a name="l00769"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c"> 769</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c">mi_option_eager_commit_delay</a>, </div><div class="line"><a name="l00770"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf"> 770</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf">mi_option_os_tag</a>, </div><div class="line"><a name="l00771"></a><span class="lineno"><a class="line" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a"> 771</a></span>  <a class="code" href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a">_mi_option_last</a></div><div class="line"><a name="l00772"></a><span class="lineno"> 772</span> } <a class="code" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a>;</div><div class="line"><a name="l00773"></a><span class="lineno"> 773</span> </div><div class="line"><a name="l00774"></a><span class="lineno"> 774</span> </div><div class="line"><a name="l00775"></a><span class="lineno"> 775</span> <span class="keywordtype">bool</span> <a class="code" href="group__options.html#ga459ad98f18b3fc9275474807fe0ca188">mi_option_is_enabled</a>(<a class="code" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option);</div><div class="line"><a name="l00776"></a><span class="lineno"> 776</span> <span class="keywordtype">void</span> <a class="code" href="group__options.html#ga04180ae41b0d601421dd62ced40ca050">mi_option_enable</a>(<a class="code" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option);</div><div class="line"><a name="l00777"></a><span class="lineno"> 777</span> <span class="keywordtype">void</span> <a class="code" href="group__options.html#gaebf6ff707a2e688ebb1a2296ca564054">mi_option_disable</a>(<a class="code" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option);</div><div class="line"><a name="l00778"></a><span class="lineno"> 778</span> <span class="keywordtype">void</span> <a class="code" href="group__options.html#ga9a13d05fcb77489cb06d4d017ebd8bed">mi_option_set_enabled</a>(<a class="code" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option, <span class="keywordtype">bool</span> enable);</div><div class="line"><a name="l00779"></a><span class="lineno"> 779</span> <span class="keywordtype">void</span> <a class="code" href="group__options.html#ga65518b69ec5d32336b50e07f74b3f629">mi_option_set_enabled_default</a>(<a class="code" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option, <span class="keywordtype">bool</span> enable);</div><div class="line"><a name="l00780"></a><span class="lineno"> 780</span> </div><div class="line"><a name="l00781"></a><span class="lineno"> 781</span> <span class="keywordtype">long</span> <a class="code" href="group__options.html#ga7e8af195cc81d3fa64ccf2662caa565a">mi_option_get</a>(<a class="code" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option);</div><div class="line"><a name="l00782"></a><span class="lineno"> 782</span> <span class="keywordtype">void</span> <a class="code" href="group__options.html#gaf84921c32375e25754dc2ee6a911fa60">mi_option_set</a>(<a class="code" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option, <span class="keywordtype">long</span> value);</div><div class="line"><a name="l00783"></a><span class="lineno"> 783</span> <span class="keywordtype">void</span> <a class="code" href="group__options.html#ga7ef623e440e6e5545cb08c94e71e4b90">mi_option_set_default</a>(<a class="code" href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a> option, <span class="keywordtype">long</span> value);</div><div class="line"><a name="l00784"></a><span class="lineno"> 784</span> </div><div class="line"><a name="l00785"></a><span class="lineno"> 785</span> </div><div class="line"><a name="l00787"></a><span class="lineno"> 787</span> </div><div class="line"><a name="l00794"></a><span class="lineno"> 794</span> </div><div class="line"><a name="l00795"></a><span class="lineno"> 795</span> <span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#ga23a0fbb452b5dce8e31fab1a1958cacc">mi_recalloc</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00796"></a><span class="lineno"> 796</span> <span class="keywordtype">size_t</span> <a class="code" href="group__posix.html#ga4531c9e775bb3ae12db57c1ba8a5d7de">mi_malloc_size</a>(<span class="keyword">const</span> <span class="keywordtype">void</span>* p);</div><div class="line"><a name="l00797"></a><span class="lineno"> 797</span> <span class="keywordtype">size_t</span> <a class="code" href="group__posix.html#ga06d07cf357bbac5c73ba5d0c0c421e17">mi_malloc_usable_size</a>(<span class="keyword">const</span> <span class="keywordtype">void</span> *p);</div><div class="line"><a name="l00798"></a><span class="lineno"> 798</span> </div><div class="line"><a name="l00800"></a><span class="lineno"> 800</span> <span class="keywordtype">void</span> <a class="code" href="group__posix.html#ga705dc7a64bffacfeeb0141501a5c35d7">mi_cfree</a>(<span class="keywordtype">void</span>* p);</div><div class="line"><a name="l00801"></a><span class="lineno"> 801</span> </div><div class="line"><a name="l00802"></a><span class="lineno"> 802</span> <span class="keywordtype">int</span> <a class="code" href="group__posix.html#gacff84f226ba9feb2031b8992e5579447">mi_posix_memalign</a>(<span class="keywordtype">void</span>** p, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00803"></a><span class="lineno"> 803</span> <span class="keywordtype">int</span> <a class="code" href="group__posix.html#gad5a69c8fea96aa2b7a7c818c2130090a">mi__posix_memalign</a>(<span class="keywordtype">void</span>** p, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00804"></a><span class="lineno"> 804</span> <span class="keywordtype">void</span>* <a class="code" href="group__posix.html#gaab7fa71ea93b96873f5d9883db57d40e">mi_memalign</a>(<span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00805"></a><span class="lineno"> 805</span> <span class="keywordtype">void</span>* <a class="code" href="group__posix.html#ga73baaf5951f5165ba0763d0c06b6a93b">mi_valloc</a>(<span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00806"></a><span class="lineno"> 806</span> </div><div class="line"><a name="l00807"></a><span class="lineno"> 807</span> <span class="keywordtype">void</span>* <a class="code" href="group__posix.html#gaeb325c39b887d3b90d85d1eb1712fb1e">mi_pvalloc</a>(<span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00808"></a><span class="lineno"> 808</span> <span class="keywordtype">void</span>* <a class="code" href="group__posix.html#ga1326d2e4388630b5f81ca7206318b8e5">mi_aligned_alloc</a>(<span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00809"></a><span class="lineno"> 809</span> <span class="keywordtype">void</span>* <a class="code" href="group__posix.html#ga48fad8648a2f1dab9c87ea9448a52088">mi_reallocarray</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00810"></a><span class="lineno"> 810</span> </div><div class="line"><a name="l00811"></a><span class="lineno"> 811</span> <span class="keywordtype">void</span> <a class="code" href="group__posix.html#gae01389eedab8d67341ff52e2aad80ebb">mi_free_size</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00812"></a><span class="lineno"> 812</span> <span class="keywordtype">void</span> <a class="code" href="group__posix.html#ga72e9d7ffb5fe94d69bc722c8506e27bc">mi_free_size_aligned</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00813"></a><span class="lineno"> 813</span> <span class="keywordtype">void</span> <a class="code" href="group__posix.html#ga0d28d5cf61e6bfbb18c63092939fe5c9">mi_free_aligned</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00814"></a><span class="lineno"> 814</span> </div><div class="line"><a name="l00816"></a><span class="lineno"> 816</span> </div><div class="line"><a name="l00829"></a><span class="lineno"> 829</span> </div><div class="line"><a name="l00831"></a><span class="lineno"> 831</span> <span class="keywordtype">void</span>* <a class="code" href="group__cpp.html#gaad048a9fce3d02c5909cd05c6ec24545">mi_new</a>(std::size_t n) noexcept(<span class="keyword">false</span>);</div><div class="line"><a name="l00832"></a><span class="lineno"> 832</span> </div><div class="line"><a name="l00834"></a><span class="lineno"> 834</span> <span class="keywordtype">void</span>* <a class="code" href="group__cpp.html#gae7bc4f56cd57ed3359060ff4f38bda81">mi_new_n</a>(<span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size) noexcept(<span class="keyword">false</span>);</div><div class="line"><a name="l00835"></a><span class="lineno"> 835</span> </div><div class="line"><a name="l00837"></a><span class="lineno"> 837</span> <span class="keywordtype">void</span>* <a class="code" href="group__cpp.html#gaef2c2bdb4f70857902d3c8903ac095f3">mi_new_aligned</a>(std::size_t n, std::align_val_t alignment) noexcept(<span class="keyword">false</span>);</div><div class="line"><a name="l00838"></a><span class="lineno"> 838</span> </div><div class="line"><a name="l00840"></a><span class="lineno"> 840</span> <span class="keywordtype">void</span>* <a class="code" href="group__cpp.html#gaeaded64eda71ed6b1d569d3e723abc4a">mi_new_nothrow</a>(<span class="keywordtype">size_t</span> n);</div><div class="line"><a name="l00841"></a><span class="lineno"> 841</span> </div><div class="line"><a name="l00843"></a><span class="lineno"> 843</span> <span class="keywordtype">void</span>* <a class="code" href="group__cpp.html#gab5e29558926d934c3f1cae8c815f942c">mi_new_aligned_nothrow</a>(<span class="keywordtype">size_t</span> n, <span class="keywordtype">size_t</span> alignment);</div><div class="line"><a name="l00844"></a><span class="lineno"> 844</span> </div><div class="line"><a name="l00846"></a><span class="lineno"> 846</span> <span class="keywordtype">void</span>* <a class="code" href="group__cpp.html#gaab78a32f55149e9fbf432d5288e38e1e">mi_new_realloc</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><a name="l00847"></a><span class="lineno"> 847</span> </div><div class="line"><a name="l00849"></a><span class="lineno"> 849</span> <span class="keywordtype">void</span>* <a class="code" href="group__cpp.html#ga756f4b2bc6a7ecd0a90baea8e90c7907">mi_new_reallocn</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newcount, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00850"></a><span class="lineno"> 850</span> </div><div class="line"><a name="l00858"></a><span class="lineno"><a class="line" href="group__cpp.html"> 858</a></span> <span class="keyword">template</span><<span class="keyword">class</span> T> <span class="keyword">struct </span><a class="code" href="group__cpp.html#structmi__stl__allocator">mi_stl_allocator</a> { }</div><div class="line"><a name="l00859"></a><span class="lineno"> 859</span> </div><div class="line"><a name="l00861"></a><span class="lineno"> 861</span> </div><div class="ttc" id="group__extended_html_ga089c859d9eddc5f9b4bd946cd53cebee"><div class="ttname"><a href="group__extended.html#ga089c859d9eddc5f9b4bd946cd53cebee">mi_usable_size</a></div><div class="ttdeci">size_t mi_usable_size(void *p)</div><div class="ttdoc">Return the available bytes in a memory block.</div></div>
+<div class="ttc" id="group__cpp_html_gaeaded64eda71ed6b1d569d3e723abc4a"><div class="ttname"><a href="group__cpp.html#gaeaded64eda71ed6b1d569d3e723abc4a">mi_new_nothrow</a></div><div class="ttdeci">void * mi_new_nothrow(size_t n)</div><div class="ttdoc">like mi_malloc, but when out of memory, use std::get_new_handler but return NULL on failure.</div></div>
+<div class="ttc" id="group__malloc_html_ga61d57b4144ba24fba5c1e9b956d13853"><div class="ttname"><a href="group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853">mi_reallocn</a></div><div class="ttdeci">void * mi_reallocn(void *p, size_t count, size_t size)</div><div class="ttdoc">Re-allocate memory to count elements of size bytes.</div></div>
+<div class="ttc" id="group__aligned_html_ga68930196751fa2cca9e1fd0d71bade56"><div class="ttname"><a href="group__aligned.html#ga68930196751fa2cca9e1fd0d71bade56">mi_malloc_aligned</a></div><div class="ttdeci">void * mi_malloc_aligned(size_t size, size_t alignment)</div><div class="ttdoc">Allocate size bytes aligned by alignment.</div></div>
+<div class="ttc" id="group__zeroinit_html_ga4ff5e92ad73585418a072c9d059e5cf9"><div class="ttname"><a href="group__zeroinit.html#ga4ff5e92ad73585418a072c9d059e5cf9">mi_recalloc_aligned_at</a></div><div class="ttdeci">void * mi_recalloc_aligned_at(void *p, size_t newcount, size_t size, size_t alignment, size_t offset)</div></div>
+<div class="ttc" id="group__extended_html_ga3bb8468b8cfcc6e2a61d98aee85c5f99"><div class="ttname"><a href="group__extended.html#ga3bb8468b8cfcc6e2a61d98aee85c5f99">mi_stats_reset</a></div><div class="ttdeci">void mi_stats_reset(void)</div><div class="ttdoc">Reset statistics.</div></div>
+<div class="ttc" id="group__heap_html_gafc603b696bd14cae6da28658f950d98c"><div class="ttname"><a href="group__heap.html#gafc603b696bd14cae6da28658f950d98c">mi_heap_realloc_aligned</a></div><div class="ttdeci">void * mi_heap_realloc_aligned(mi_heap_t *heap, void *p, size_t newsize, size_t alignment)</div></div>
+<div class="ttc" id="group__options_html_ga459ad98f18b3fc9275474807fe0ca188"><div class="ttname"><a href="group__options.html#ga459ad98f18b3fc9275474807fe0ca188">mi_option_is_enabled</a></div><div class="ttdeci">bool mi_option_is_enabled(mi_option_t option)</div></div>
+<div class="ttc" id="group__cpp_html_gaab78a32f55149e9fbf432d5288e38e1e"><div class="ttname"><a href="group__cpp.html#gaab78a32f55149e9fbf432d5288e38e1e">mi_new_realloc</a></div><div class="ttdeci">void * mi_new_realloc(void *p, size_t newsize)</div><div class="ttdoc">like mi_realloc(), but when out of memory, use std::get_new_handler and raise std::bad_alloc exceptio...</div></div>
+<div class="ttc" id="group__malloc_html_ga23a0fbb452b5dce8e31fab1a1958cacc"><div class="ttname"><a href="group__malloc.html#ga23a0fbb452b5dce8e31fab1a1958cacc">mi_recalloc</a></div><div class="ttdeci">void * mi_recalloc(void *p, size_t count, size_t size)</div><div class="ttdoc">Re-allocate memory to count elements of size bytes, with extra memory initialized to zero.</div></div>
+<div class="ttc" id="group__malloc_html_ga0b05e2bf0f73e7401ae08597ff782ac6"><div class="ttname"><a href="group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6">mi_mallocn</a></div><div class="ttdeci">void * mi_mallocn(size_t count, size_t size)</div><div class="ttdoc">Allocate count elements of size bytes.</div></div>
+<div class="ttc" id="group__posix_html_ga4531c9e775bb3ae12db57c1ba8a5d7de"><div class="ttname"><a href="group__posix.html#ga4531c9e775bb3ae12db57c1ba8a5d7de">mi_malloc_size</a></div><div class="ttdeci">size_t mi_malloc_size(const void *p)</div></div>
+<div class="ttc" id="group__options_html_ga9a13d05fcb77489cb06d4d017ebd8bed"><div class="ttname"><a href="group__options.html#ga9a13d05fcb77489cb06d4d017ebd8bed">mi_option_set_enabled</a></div><div class="ttdeci">void mi_option_set_enabled(mi_option_t option, bool enable)</div></div>
+<div class="ttc" id="group__posix_html_gacff84f226ba9feb2031b8992e5579447"><div class="ttname"><a href="group__posix.html#gacff84f226ba9feb2031b8992e5579447">mi_posix_memalign</a></div><div class="ttdeci">int mi_posix_memalign(void **p, size_t alignment, size_t size)</div></div>
+<div class="ttc" id="group__extended_html_ga854b1de8cb067c7316286c28b2fcd3d1"><div class="ttname"><a href="group__extended.html#ga854b1de8cb067c7316286c28b2fcd3d1">mi_stats_merge</a></div><div class="ttdeci">void mi_stats_merge(void)</div><div class="ttdoc">Merge thread local statistics with the main statistics and reset.</div></div>
+<div class="ttc" id="group__cpp_html_gae7bc4f56cd57ed3359060ff4f38bda81"><div class="ttname"><a href="group__cpp.html#gae7bc4f56cd57ed3359060ff4f38bda81">mi_new_n</a></div><div class="ttdeci">void * mi_new_n(size_t count, size_t size) noexcept(false)</div><div class="ttdoc">like mi_mallocn(), but when out of memory, use std::get_new_handler and raise std::bad_alloc exceptio...</div></div>
+<div class="ttc" id="group__options_html_ga7ef623e440e6e5545cb08c94e71e4b90"><div class="ttname"><a href="group__options.html#ga7ef623e440e6e5545cb08c94e71e4b90">mi_option_set_default</a></div><div class="ttdeci">void mi_option_set_default(mi_option_t option, long value)</div></div>
+<div class="ttc" id="group__extended_html_ga537f13b299ddf801e49a5a94fde02c79"><div class="ttname"><a href="group__extended.html#ga537f13b299ddf801e49a5a94fde02c79">mi_stats_print_out</a></div><div class="ttdeci">void mi_stats_print_out(mi_output_fun *out, void *arg)</div><div class="ttdoc">Print the main statistics.</div></div>
+<div class="ttc" id="group__extended_html_ga251d369cda3f1c2a955c555486ed90e5"><div class="ttname"><a href="group__extended.html#ga251d369cda3f1c2a955c555486ed90e5">mi_error_fun</a></div><div class="ttdeci">void() mi_error_fun(int err, void *arg)</div><div class="ttdoc">Type of error callback functions.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:383</div></div>
+<div class="ttc" id="group__zeroinit_html_ga8c292e142110229a2980b37ab036dbc6"><div class="ttname"><a href="group__zeroinit.html#ga8c292e142110229a2980b37ab036dbc6">mi_rezalloc</a></div><div class="ttdeci">void * mi_rezalloc(void *p, size_t newsize)</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b">mi_option_eager_commit</a></div><div class="ttdoc">Eagerly commit segments (4MiB) (enabled by default).</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:759</div></div>
+<div class="ttc" id="group__heap_html_ga903104592c8ed53417a3762da6241133"><div class="ttname"><a href="group__heap.html#ga903104592c8ed53417a3762da6241133">mi_heap_zalloc</a></div><div class="ttdeci">void * mi_heap_zalloc(mi_heap_t *heap, size_t size)</div><div class="ttdoc">Allocate zero-initialized in a specific heap.</div></div>
+<div class="ttc" id="group__options_html_gaf84921c32375e25754dc2ee6a911fa60"><div class="ttname"><a href="group__options.html#gaf84921c32375e25754dc2ee6a911fa60">mi_option_set</a></div><div class="ttdeci">void mi_option_set(mi_option_t option, long value)</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad">mi_option_eager_region_commit</a></div><div class="ttdoc">Eagerly commit large (256MiB) memory regions (enabled by default, except on Windows)</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:760</div></div>
+<div class="ttc" id="group__posix_html_ga705dc7a64bffacfeeb0141501a5c35d7"><div class="ttname"><a href="group__posix.html#ga705dc7a64bffacfeeb0141501a5c35d7">mi_cfree</a></div><div class="ttdeci">void mi_cfree(void *p)</div><div class="ttdoc">Just as free but also checks if the pointer p belongs to our heap.</div></div>
+<div class="ttc" id="group__zeroinit_html_ga3e7e5c291acf1c7fd7ffd9914a9f945f"><div class="ttname"><a href="group__zeroinit.html#ga3e7e5c291acf1c7fd7ffd9914a9f945f">mi_recalloc_aligned</a></div><div class="ttdeci">void * mi_recalloc_aligned(void *p, size_t newcount, size_t size, size_t alignment)</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a">_mi_option_last</a></div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:771</div></div>
+<div class="ttc" id="group__aligned_html_gaf66a9ae6c6f08bd6be6fb6ea771faffb"><div class="ttname"><a href="group__aligned.html#gaf66a9ae6c6f08bd6be6fb6ea771faffb">mi_realloc_aligned_at</a></div><div class="ttdeci">void * mi_realloc_aligned_at(void *p, size_t newsize, size_t alignment, size_t offset)</div></div>
+<div class="ttc" id="group__analysis_html_ae0085e6e1cf059a4eb7767e30e9991b8"><div class="ttname"><a href="group__analysis.html#ae0085e6e1cf059a4eb7767e30e9991b8">mi_heap_area_t::blocks</a></div><div class="ttdeci">void * blocks</div><div class="ttdoc">start of the area containing heap blocks</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:715</div></div>
+<div class="ttc" id="group__aligned_html_ga4028d1cf4aa4c87c880747044a8322ae"><div class="ttname"><a href="group__aligned.html#ga4028d1cf4aa4c87c880747044a8322ae">mi_realloc_aligned</a></div><div class="ttdeci">void * mi_realloc_aligned(void *p, size_t newsize, size_t alignment)</div></div>
+<div class="ttc" id="group__options_html_ga04180ae41b0d601421dd62ced40ca050"><div class="ttname"><a href="group__options.html#ga04180ae41b0d601421dd62ced40ca050">mi_option_enable</a></div><div class="ttdeci">void mi_option_enable(mi_option_t option)</div></div>
+<div class="ttc" id="group__posix_html_gad5a69c8fea96aa2b7a7c818c2130090a"><div class="ttname"><a href="group__posix.html#gad5a69c8fea96aa2b7a7c818c2130090a">mi__posix_memalign</a></div><div class="ttdeci">int mi__posix_memalign(void **p, size_t alignment, size_t size)</div></div>
+<div class="ttc" id="group__malloc_html_gaf2c7b89c327d1f60f59e68b9ea644d95"><div class="ttname"><a href="group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95">mi_free</a></div><div class="ttdeci">void mi_free(void *p)</div><div class="ttdoc">Free previously allocated memory.</div></div>
+<div class="ttc" id="group__heap_html_ga139d6b09dbf50c3c2523d0f4d1cfdeb5"><div class="ttname"><a href="group__heap.html#ga139d6b09dbf50c3c2523d0f4d1cfdeb5">mi_heap_strdup</a></div><div class="ttdeci">char * mi_heap_strdup(mi_heap_t *heap, const char *s)</div><div class="ttdoc">Duplicate a string in a specific heap.</div></div>
+<div class="ttc" id="group__heap_html_ga00e95ba1e01acac3cfd95bb7a357a6f0"><div class="ttname"><a href="group__heap.html#ga00e95ba1e01acac3cfd95bb7a357a6f0">mi_heap_realpath</a></div><div class="ttdeci">char * mi_heap_realpath(mi_heap_t *heap, const char *fname, char *resolved_name)</div><div class="ttdoc">Resolve a file path name using a specific heap to allocate the result.</div></div>
+<div class="ttc" id="group__heap_html_ga08ca6419a5c057a4d965868998eef487"><div class="ttname"><a href="group__heap.html#ga08ca6419a5c057a4d965868998eef487">mi_heap_calloc_aligned_at</a></div><div class="ttdeci">void * mi_heap_calloc_aligned_at(mi_heap_t *heap, size_t count, size_t size, size_t alignment, size_t offset)</div></div>
+<div class="ttc" id="group__aligned_html_ga53dddb4724042a90315b94bc268fb4c9"><div class="ttname"><a href="group__aligned.html#ga53dddb4724042a90315b94bc268fb4c9">mi_calloc_aligned</a></div><div class="ttdeci">void * mi_calloc_aligned(size_t count, size_t size, size_t alignment)</div></div>
+<div class="ttc" id="group__heap_html_gaa450a59c6c7ae5fdbd1c2b80a8329ef0"><div class="ttname"><a href="group__heap.html#gaa450a59c6c7ae5fdbd1c2b80a8329ef0">mi_heap_zalloc_aligned</a></div><div class="ttdeci">void * mi_heap_zalloc_aligned(mi_heap_t *heap, size_t size, size_t alignment)</div></div>
+<div class="ttc" id="group__extended_html_ga220f29f40a44404b0061c15bc1c31152"><div class="ttname"><a href="group__extended.html#ga220f29f40a44404b0061c15bc1c31152">mi_zalloc_small</a></div><div class="ttdeci">void * mi_zalloc_small(size_t size)</div><div class="ttdoc">Allocate a zero initialized small object.</div></div>
+<div class="ttc" id="group__malloc_html_gaaabf971c2571891433477e2d21a35266"><div class="ttname"><a href="group__malloc.html#gaaabf971c2571891433477e2d21a35266">mi_strndup</a></div><div class="ttdeci">char * mi_strndup(const char *s, size_t n)</div><div class="ttdoc">Allocate and duplicate a string up to n bytes.</div></div>
+<div class="ttc" id="group__malloc_html_gaaee66a1d483c3e28f585525fb96707e4"><div class="ttname"><a href="group__malloc.html#gaaee66a1d483c3e28f585525fb96707e4">mi_expand</a></div><div class="ttdeci">void * mi_expand(void *p, size_t newsize)</div><div class="ttdoc">Try to re-allocate memory to newsize bytes in place.</div></div>
+<div class="ttc" id="group__posix_html_gaeb325c39b887d3b90d85d1eb1712fb1e"><div class="ttname"><a href="group__posix.html#gaeb325c39b887d3b90d85d1eb1712fb1e">mi_pvalloc</a></div><div class="ttdeci">void * mi_pvalloc(size_t size)</div></div>
+<div class="ttc" id="group__options_html_ga65518b69ec5d32336b50e07f74b3f629"><div class="ttname"><a href="group__options.html#ga65518b69ec5d32336b50e07f74b3f629">mi_option_set_enabled_default</a></div><div class="ttdeci">void mi_option_set_enabled_default(mi_option_t option, bool enable)</div></div>
+<div class="ttc" id="group__zeroinit_html_gac90da54fa7e5d10bdc97ce0b51dce2eb"><div class="ttname"><a href="group__zeroinit.html#gac90da54fa7e5d10bdc97ce0b51dce2eb">mi_heap_rezalloc_aligned_at</a></div><div class="ttdeci">void * mi_heap_rezalloc_aligned_at(mi_heap_t *heap, void *p, size_t newsize, size_t alignment, size_t offset)</div></div>
+<div class="ttc" id="group__malloc_html_gafdd9d8bb2986e668ba9884f28af38000"><div class="ttname"><a href="group__malloc.html#gafdd9d8bb2986e668ba9884f28af38000">mi_zalloc</a></div><div class="ttdeci">void * mi_zalloc(size_t size)</div><div class="ttdoc">Allocate zero-initialized size bytes.</div></div>
+<div class="ttc" id="group__zeroinit_html_gacfad83f14eb5d6a42a497a898e19fc76"><div class="ttname"><a href="group__zeroinit.html#gacfad83f14eb5d6a42a497a898e19fc76">mi_heap_rezalloc</a></div><div class="ttdeci">void * mi_heap_rezalloc(mi_heap_t *heap, void *p, size_t newsize)</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1">mi_option_segment_cache</a></div><div class="ttdoc">The number of segments per thread to keep cached.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:763</div></div>
+<div class="ttc" id="group__heap_html_gaa6702b3c48e9e53e50e81b36f5011d55"><div class="ttname"><a href="group__heap.html#gaa6702b3c48e9e53e50e81b36f5011d55">mi_heap_calloc</a></div><div class="ttdeci">void * mi_heap_calloc(mi_heap_t *heap, size_t count, size_t size)</div><div class="ttdoc">Allocate count zero-initialized elements in a specific heap.</div></div>
+<div class="ttc" id="group__heap_html_ga4af03a6e2b93fae77424d93f889705c3"><div class="ttname"><a href="group__heap.html#ga4af03a6e2b93fae77424d93f889705c3">mi_heap_calloc_aligned</a></div><div class="ttdeci">void * mi_heap_calloc_aligned(mi_heap_t *heap, size_t count, size_t size, size_t alignment)</div></div>
+<div class="ttc" id="group__extended_html_gaad25050b19f30cd79397b227e0157a3f"><div class="ttname"><a href="group__extended.html#gaad25050b19f30cd79397b227e0157a3f">mi_is_redirected</a></div><div class="ttdeci">bool mi_is_redirected()</div><div class="ttdoc">Is the C runtime malloc API redirected?</div></div>
+<div class="ttc" id="group__analysis_html_a332a6c14d736a99699d5453a1cb04b41"><div class="ttname"><a href="group__analysis.html#a332a6c14d736a99699d5453a1cb04b41">mi_heap_area_t::block_size</a></div><div class="ttdeci">size_t block_size</div><div class="ttdoc">size in bytes of one block</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:719</div></div>
+<div class="ttc" id="group__posix_html_ga48fad8648a2f1dab9c87ea9448a52088"><div class="ttname"><a href="group__posix.html#ga48fad8648a2f1dab9c87ea9448a52088">mi_reallocarray</a></div><div class="ttdeci">void * mi_reallocarray(void *p, size_t count, size_t size)</div></div>
+<div class="ttc" id="group__extended_html_ga3132f521fb756fc0e8ec0b74fb58df50"><div class="ttname"><a href="group__extended.html#ga3132f521fb756fc0e8ec0b74fb58df50">mi_reserve_huge_os_pages_interleave</a></div><div class="ttdeci">int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs)</div><div class="ttdoc">Reserve pages of huge OS pages (1GiB) evenly divided over numa_nodes nodes, but stops after at most t...</div></div>
+<div class="ttc" id="group__extended_html_ga299dae78d25ce112e384a98b7309c5be"><div class="ttname"><a href="group__extended.html#ga299dae78d25ce112e384a98b7309c5be">mi_deferred_free_fun</a></div><div class="ttdeci">void() mi_deferred_free_fun(bool force, unsigned long long heartbeat, void *arg)</div><div class="ttdoc">Type of deferred free functions.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:344</div></div>
+<div class="ttc" id="group__extended_html_ga5f071b10d4df1c3658e04e7fd67a94e6"><div class="ttname"><a href="group__extended.html#ga5f071b10d4df1c3658e04e7fd67a94e6">mi_is_in_heap_region</a></div><div class="ttdeci">bool mi_is_in_heap_region(const void *p)</div><div class="ttdoc">Is a pointer part of our heap?</div></div>
+<div class="ttc" id="group__cpp_html_gaef2c2bdb4f70857902d3c8903ac095f3"><div class="ttname"><a href="group__cpp.html#gaef2c2bdb4f70857902d3c8903ac095f3">mi_new_aligned</a></div><div class="ttdeci">void * mi_new_aligned(std::size_t n, std::align_val_t alignment) noexcept(false)</div><div class="ttdoc">like mi_malloc_aligned(), but when out of memory, use std::get_new_handler and raise std::bad_alloc e...</div></div>
+<div class="ttc" id="group__malloc_html_gaf11eb497da57bdfb2de65eb191c69db6"><div class="ttname"><a href="group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6">mi_realloc</a></div><div class="ttdeci">void * mi_realloc(void *p, size_t newsize)</div><div class="ttdoc">Re-allocate memory to newsize bytes.</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2">mi_option_reserve_huge_os_pages</a></div><div class="ttdoc">The number of huge OS pages (1GiB in size) to reserve at the start of the program.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:762</div></div>
+<div class="ttc" id="group__heap_html_ga4a21070eb4e7cce018133c8d5f4b0527"><div class="ttname"><a href="group__heap.html#ga4a21070eb4e7cce018133c8d5f4b0527">mi_heap_reallocf</a></div><div class="ttdeci">void * mi_heap_reallocf(mi_heap_t *heap, void *p, size_t newsize)</div></div>
+<div class="ttc" id="group__posix_html_ga72e9d7ffb5fe94d69bc722c8506e27bc"><div class="ttname"><a href="group__posix.html#ga72e9d7ffb5fe94d69bc722c8506e27bc">mi_free_size_aligned</a></div><div class="ttdeci">void mi_free_size_aligned(void *p, size_t size, size_t alignment)</div></div>
+<div class="ttc" id="group__zeroinit_html_gae8b358c417e61d5307da002702b0a8e1"><div class="ttname"><a href="group__zeroinit.html#gae8b358c417e61d5307da002702b0a8e1">mi_rezalloc_aligned_at</a></div><div class="ttdeci">void * mi_rezalloc_aligned_at(void *p, size_t newsize, size_t alignment, size_t offset)</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968">mi_option_page_reset</a></div><div class="ttdoc">Reset page memory after mi_option_reset_delay milliseconds when it becomes free.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:764</div></div>
+<div class="ttc" id="group__extended_html_ga0ae4581e85453456a0d658b2b98bf7bf"><div class="ttname"><a href="group__extended.html#ga0ae4581e85453456a0d658b2b98bf7bf">mi_thread_done</a></div><div class="ttdeci">void mi_thread_done(void)</div><div class="ttdoc">Uninitialize mimalloc on a thread.</div></div>
+<div class="ttc" id="group__analysis_html_ga70c46687dc6e9dc98b232b02646f8bed"><div class="ttname"><a href="group__analysis.html#ga70c46687dc6e9dc98b232b02646f8bed">mi_heap_visit_blocks</a></div><div class="ttdeci">bool mi_heap_visit_blocks(const mi_heap_t *heap, bool visit_all_blocks, mi_block_visit_fun *visitor, void *arg)</div><div class="ttdoc">Visit all areas and blocks in a heap.</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74">mi_option_use_numa_nodes</a></div><div class="ttdoc">Pretend there are at most N NUMA nodes.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:767</div></div>
+<div class="ttc" id="group__malloc_html_ga3406e8b168bc74c8637b11571a6da83a"><div class="ttname"><a href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a">mi_malloc</a></div><div class="ttdeci">void * mi_malloc(size_t size)</div><div class="ttdoc">Allocate size bytes.</div></div>
+<div class="ttc" id="group__extended_html_gaa1d55e0e894be240827e5d87ec3a1f45"><div class="ttname"><a href="group__extended.html#gaa1d55e0e894be240827e5d87ec3a1f45">mi_register_error</a></div><div class="ttdeci">void mi_register_error(mi_error_fun *errfun, void *arg)</div><div class="ttdoc">Register an error callback function.</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536">mi_option_reset_decommits</a></div><div class="ttdoc">Experimental.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:768</div></div>
+<div class="ttc" id="group__heap_html_ga8e3dbd46650dd26573cf307a2c8f1f5a"><div class="ttname"><a href="group__heap.html#ga8e3dbd46650dd26573cf307a2c8f1f5a">mi_heap_strndup</a></div><div class="ttdeci">char * mi_heap_strndup(mi_heap_t *heap, const char *s, size_t n)</div><div class="ttdoc">Duplicate a string of at most length n in a specific heap.</div></div>
+<div class="ttc" id="group__analysis_html_gadfa01e2900f0e5d515ad5506b26f6d65"><div class="ttname"><a href="group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65">mi_block_visit_fun</a></div><div class="ttdeci">bool() mi_block_visit_fun(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *arg)</div><div class="ttdoc">Visitor function passed to mi_heap_visit_blocks()</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:729</div></div>
+<div class="ttc" id="group__zeroinit_html_ga8648c5fbb22a80f0262859099f06dfbd"><div class="ttname"><a href="group__zeroinit.html#ga8648c5fbb22a80f0262859099f06dfbd">mi_heap_recalloc</a></div><div class="ttdeci">void * mi_heap_recalloc(mi_heap_t *heap, void *p, size_t newcount, size_t size)</div></div>
+<div class="ttc" id="group__heap_html_ga23acd7680fb0976dde3783254c6c874b"><div class="ttname"><a href="group__heap.html#ga23acd7680fb0976dde3783254c6c874b">mi_heap_malloc_aligned_at</a></div><div class="ttdeci">void * mi_heap_malloc_aligned_at(mi_heap_t *heap, size_t size, size_t alignment, size_t offset)</div></div>
+<div class="ttc" id="group__malloc_html_ga08cec32dd5bbe7da91c78d19f1b5bebe"><div class="ttname"><a href="group__malloc.html#ga08cec32dd5bbe7da91c78d19f1b5bebe">mi_realpath</a></div><div class="ttdeci">char * mi_realpath(const char *fname, char *resolved_name)</div><div class="ttdoc">Resolve a file path name.</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0">mi_option_show_errors</a></div><div class="ttdoc">Print error messages to stderr.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:755</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d">mi_option_segment_reset</a></div><div class="ttdoc">Experimental.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:765</div></div>
+<div class="ttc" id="group__zeroinit_html_ga375fa8a611c51905e592d5d467c49664"><div class="ttname"><a href="group__zeroinit.html#ga375fa8a611c51905e592d5d467c49664">mi_heap_rezalloc_aligned</a></div><div class="ttdeci">void * mi_heap_rezalloc_aligned(mi_heap_t *heap, void *p, size_t newsize, size_t alignment)</div></div>
+<div class="ttc" id="group__cpp_html_gab5e29558926d934c3f1cae8c815f942c"><div class="ttname"><a href="group__cpp.html#gab5e29558926d934c3f1cae8c815f942c">mi_new_aligned_nothrow</a></div><div class="ttdeci">void * mi_new_aligned_nothrow(size_t n, size_t alignment)</div><div class="ttdoc">like mi_malloc_aligned, but when out of memory, use std::get_new_handler but return NULL on failure.</div></div>
+<div class="ttc" id="group__posix_html_gaab7fa71ea93b96873f5d9883db57d40e"><div class="ttname"><a href="group__posix.html#gaab7fa71ea93b96873f5d9883db57d40e">mi_memalign</a></div><div class="ttdeci">void * mi_memalign(size_t alignment, size_t size)</div></div>
+<div class="ttc" id="group__zeroinit_html_gacd71a7bce96aab38ae6de17af2eb2cf0"><div class="ttname"><a href="group__zeroinit.html#gacd71a7bce96aab38ae6de17af2eb2cf0">mi_rezalloc_aligned</a></div><div class="ttdeci">void * mi_rezalloc_aligned(void *p, size_t newsize, size_t alignment)</div></div>
+<div class="ttc" id="group__analysis_html_gaa862aa8ed8d57d84cae41fc1022d71af"><div class="ttname"><a href="group__analysis.html#gaa862aa8ed8d57d84cae41fc1022d71af">mi_heap_contains_block</a></div><div class="ttdeci">bool mi_heap_contains_block(mi_heap_t *heap, const void *p)</div><div class="ttdoc">Does a heap contain a pointer to a previously allocated block?</div></div>
+<div class="ttc" id="group__heap_html_ga7922f7495cde30b1984d0e6072419298"><div class="ttname"><a href="group__heap.html#ga7922f7495cde30b1984d0e6072419298">mi_heap_collect</a></div><div class="ttdeci">void mi_heap_collect(mi_heap_t *heap, bool force)</div><div class="ttdoc">Release outstanding resources in a specific heap.</div></div>
+<div class="ttc" id="group__zeroinit_html_ga496452c96f1de8c500be9fddf52edaf7"><div class="ttname"><a href="group__zeroinit.html#ga496452c96f1de8c500be9fddf52edaf7">mi_heap_recalloc_aligned_at</a></div><div class="ttdeci">void * mi_heap_recalloc_aligned_at(mi_heap_t *heap, void *p, size_t newcount, size_t size, size_t alignment, size_t offset)</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777">mi_option_verbose</a></div><div class="ttdoc">Print verbose messages to stderr.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:757</div></div>
+<div class="ttc" id="group__aligned_html_ga5f8c2353766db522565e642fafd8a3f8"><div class="ttname"><a href="group__aligned.html#ga5f8c2353766db522565e642fafd8a3f8">mi_zalloc_aligned_at</a></div><div class="ttdeci">void * mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset)</div></div>
+<div class="ttc" id="group__aligned_html_ga5850da130c936bd77db039dcfbc8295d"><div class="ttname"><a href="group__aligned.html#ga5850da130c936bd77db039dcfbc8295d">mi_malloc_aligned_at</a></div><div class="ttdeci">void * mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset)</div><div class="ttdoc">Allocate size bytes aligned by alignment at a specified offset.</div></div>
+<div class="ttc" id="group__heap_html_ga2ab1af8d438819b55319c7ef51d1e409"><div class="ttname"><a href="group__heap.html#ga2ab1af8d438819b55319c7ef51d1e409">mi_heap_delete</a></div><div class="ttdeci">void mi_heap_delete(mi_heap_t *heap)</div><div class="ttdoc">Delete a previously allocated heap.</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf">mi_option_os_tag</a></div><div class="ttdoc">OS tag to assign to mimalloc'd memory.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:770</div></div>
+<div class="ttc" id="group__heap_html_ga8db4cbb87314a989a9a187464d6b5e05"><div class="ttname"><a href="group__heap.html#ga8db4cbb87314a989a9a187464d6b5e05">mi_heap_get_default</a></div><div class="ttdeci">mi_heap_t * mi_heap_get_default()</div><div class="ttdoc">Get the default heap that is used for mi_malloc() et al.</div></div>
+<div class="ttc" id="group__extended_html_ga7795a13d20087447281858d2c771cca1"><div class="ttname"><a href="group__extended.html#ga7795a13d20087447281858d2c771cca1">mi_reserve_huge_os_pages_at</a></div><div class="ttdeci">int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs)</div><div class="ttdoc">Reserve pages of huge OS pages (1GiB) at a specific numa_node, but stops after at most timeout_msecs ...</div></div>
+<div class="ttc" id="group__options_html_gaebf6ff707a2e688ebb1a2296ca564054"><div class="ttname"><a href="group__options.html#gaebf6ff707a2e688ebb1a2296ca564054">mi_option_disable</a></div><div class="ttdeci">void mi_option_disable(mi_option_t option)</div></div>
+<div class="ttc" id="group__posix_html_ga1326d2e4388630b5f81ca7206318b8e5"><div class="ttname"><a href="group__posix.html#ga1326d2e4388630b5f81ca7206318b8e5">mi_aligned_alloc</a></div><div class="ttdeci">void * mi_aligned_alloc(size_t alignment, size_t size)</div></div>
+<div class="ttc" id="group__posix_html_ga73baaf5951f5165ba0763d0c06b6a93b"><div class="ttname"><a href="group__posix.html#ga73baaf5951f5165ba0763d0c06b6a93b">mi_valloc</a></div><div class="ttdeci">void * mi_valloc(size_t size)</div></div>
+<div class="ttc" id="group__extended_html_gaf8e73efc2cbca9ebfdfb166983a04c17"><div class="ttname"><a href="group__extended.html#gaf8e73efc2cbca9ebfdfb166983a04c17">mi_thread_init</a></div><div class="ttdeci">void mi_thread_init(void)</div><div class="ttdoc">Initialize mimalloc on a thread.</div></div>
+<div class="ttc" id="group__extended_html_gac057927cd06c854b45fe7847e921bd47"><div class="ttname"><a href="group__extended.html#gac057927cd06c854b45fe7847e921bd47">mi_good_size</a></div><div class="ttdeci">size_t mi_good_size(size_t size)</div><div class="ttdoc">Return the used allocation size.</div></div>
+<div class="ttc" id="group__extended_html_ga2d126e5c62d3badc35445e5d84166df2"><div class="ttname"><a href="group__extended.html#ga2d126e5c62d3badc35445e5d84166df2">mi_stats_print</a></div><div class="ttdeci">void mi_stats_print(void *out)</div><div class="ttdoc">Deprecated.</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c">mi_option_eager_commit_delay</a></div><div class="ttdoc">Experimental.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:769</div></div>
+<div class="ttc" id="group__zeroinit_html_ga9f3f999396c8f77ca5e80e7b40ac29e3"><div class="ttname"><a href="group__zeroinit.html#ga9f3f999396c8f77ca5e80e7b40ac29e3">mi_heap_recalloc_aligned</a></div><div class="ttdeci">void * mi_heap_recalloc_aligned(mi_heap_t *heap, void *p, size_t newcount, size_t size, size_t alignment)</div></div>
+<div class="ttc" id="group__heap_html_ga851da6c43fe0b71c1376cee8aef90db0"><div class="ttname"><a href="group__heap.html#ga851da6c43fe0b71c1376cee8aef90db0">mi_heap_mallocn</a></div><div class="ttdeci">void * mi_heap_mallocn(mi_heap_t *heap, size_t count, size_t size)</div><div class="ttdoc">Allocate count elements in a specific heap.</div></div>
+<div class="ttc" id="group__analysis_html_structmi__heap__area__t"><div class="ttname"><a href="group__analysis.html#structmi__heap__area__t">mi_heap_area_t</a></div><div class="ttdoc">An area of heap space contains blocks of a single size.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:714</div></div>
+<div class="ttc" id="group__extended_html_gab1dac8476c46cb9eecab767eb40c1525"><div class="ttname"><a href="group__extended.html#gab1dac8476c46cb9eecab767eb40c1525">mi_thread_stats_print_out</a></div><div class="ttdeci">void mi_thread_stats_print_out(mi_output_fun *out, void *arg)</div><div class="ttdoc">Print out heap statistics for this thread.</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda">mi_option_show_stats</a></div><div class="ttdoc">Print statistics to stderr when the program is done.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:756</div></div>
+<div class="ttc" id="group__aligned_html_ga0cadbcf5b89a7b6fb171bc8df8734819"><div class="ttname"><a href="group__aligned.html#ga0cadbcf5b89a7b6fb171bc8df8734819">mi_zalloc_aligned</a></div><div class="ttdeci">void * mi_zalloc_aligned(size_t size, size_t alignment)</div></div>
+<div class="ttc" id="group__analysis_html_ae848a3e6840414891035423948ca0383"><div class="ttname"><a href="group__analysis.html#ae848a3e6840414891035423948ca0383">mi_heap_area_t::reserved</a></div><div class="ttdeci">size_t reserved</div><div class="ttdoc">bytes reserved for this area</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:716</div></div>
+<div class="ttc" id="group__heap_html_ga34a47cde5a5b38c29f1aa3c5e76943c2"><div class="ttname"><a href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a></div><div class="ttdeci">struct mi_heap_s mi_heap_t</div><div class="ttdoc">Type of first-class heaps.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:507</div></div>
+<div class="ttc" id="group__analysis_html_ab820302c5cd0df133eb8e51650a008b4"><div class="ttname"><a href="group__analysis.html#ab820302c5cd0df133eb8e51650a008b4">mi_heap_area_t::used</a></div><div class="ttdeci">size_t used</div><div class="ttdoc">bytes in use by allocated blocks</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:718</div></div>
+<div class="ttc" id="group__extended_html_ga3460a6ca91af97be4058f523d3cb8ece"><div class="ttname"><a href="group__extended.html#ga3460a6ca91af97be4058f523d3cb8ece">mi_register_deferred_free</a></div><div class="ttdeci">void mi_register_deferred_free(mi_deferred_free_fun *deferred_free, void *arg)</div><div class="ttdoc">Register a deferred free function.</div></div>
+<div class="ttc" id="group__posix_html_gae01389eedab8d67341ff52e2aad80ebb"><div class="ttname"><a href="group__posix.html#gae01389eedab8d67341ff52e2aad80ebb">mi_free_size</a></div><div class="ttdeci">void mi_free_size(void *p, size_t size)</div></div>
+<div class="ttc" id="group__extended_html_ga421430e2226d7d468529cec457396756"><div class="ttname"><a href="group__extended.html#ga421430e2226d7d468529cec457396756">mi_collect</a></div><div class="ttdeci">void mi_collect(bool force)</div><div class="ttdoc">Eagerly free memory.</div></div>
+<div class="ttc" id="group__cpp_html_ga756f4b2bc6a7ecd0a90baea8e90c7907"><div class="ttname"><a href="group__cpp.html#ga756f4b2bc6a7ecd0a90baea8e90c7907">mi_new_reallocn</a></div><div class="ttdeci">void * mi_new_reallocn(void *p, size_t newcount, size_t size)</div><div class="ttdoc">like mi_reallocn(), but when out of memory, use std::get_new_handler and raise std::bad_alloc excepti...</div></div>
+<div class="ttc" id="group__heap_html_ga9f9c0844edb9717f4feacd79116b8e0d"><div class="ttname"><a href="group__heap.html#ga9f9c0844edb9717f4feacd79116b8e0d">mi_heap_destroy</a></div><div class="ttdeci">void mi_heap_destroy(mi_heap_t *heap)</div><div class="ttdoc">Destroy a heap, freeing all its still allocated blocks.</div></div>
+<div class="ttc" id="group__aligned_html_ga08647c4593f3b2eef24a919a73eba3a3"><div class="ttname"><a href="group__aligned.html#ga08647c4593f3b2eef24a919a73eba3a3">mi_calloc_aligned_at</a></div><div class="ttdeci">void * mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset)</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e">mi_option_large_os_pages</a></div><div class="ttdoc">Use large OS pages (2MiB in size) if possible.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:761</div></div>
+<div class="ttc" id="group__heap_html_gac74e94ad9b0c9b57c1c4d88b8825b7a8"><div class="ttname"><a href="group__heap.html#gac74e94ad9b0c9b57c1c4d88b8825b7a8">mi_heap_reallocn</a></div><div class="ttdeci">void * mi_heap_reallocn(mi_heap_t *heap, void *p, size_t count, size_t size)</div></div>
+<div class="ttc" id="group__extended_html_gae5b17ff027cd2150b43a33040250cf3f"><div class="ttname"><a href="group__extended.html#gae5b17ff027cd2150b43a33040250cf3f">mi_register_output</a></div><div class="ttdeci">void mi_register_output(mi_output_fun *out, void *arg)</div><div class="ttdoc">Register an output function.</div></div>
+<div class="ttc" id="group__cpp_html_structmi__stl__allocator"><div class="ttname"><a href="group__cpp.html#structmi__stl__allocator">mi_stl_allocator</a></div><div class="ttdoc">std::allocator implementation for mimalloc for use in STL containers.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:858</div></div>
+<div class="ttc" id="group__heap_html_gaa1a1c7a1f4da6826b5a25b70ef878368"><div class="ttname"><a href="group__heap.html#gaa1a1c7a1f4da6826b5a25b70ef878368">mi_heap_malloc_small</a></div><div class="ttdeci">void * mi_heap_malloc_small(mi_heap_t *heap, size_t size)</div><div class="ttdoc">Allocate a small object in a specific heap.</div></div>
+<div class="ttc" id="group__heap_html_gaaef3395f66be48f37bdc8322509c5d81"><div class="ttname"><a href="group__heap.html#gaaef3395f66be48f37bdc8322509c5d81">mi_heap_realloc</a></div><div class="ttdeci">void * mi_heap_realloc(mi_heap_t *heap, void *p, size_t newsize)</div></div>
+<div class="ttc" id="group__posix_html_ga06d07cf357bbac5c73ba5d0c0c421e17"><div class="ttname"><a href="group__posix.html#ga06d07cf357bbac5c73ba5d0c0c421e17">mi_malloc_usable_size</a></div><div class="ttdeci">size_t mi_malloc_usable_size(const void *p)</div></div>
+<div class="ttc" id="group__extended_html_gad823d23444a4b77a40f66bf075a98a0c"><div class="ttname"><a href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c">mi_output_fun</a></div><div class="ttdeci">void() mi_output_fun(const char *msg, void *arg)</div><div class="ttdoc">Type of output functions.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:368</div></div>
+<div class="ttc" id="group__malloc_html_gac7cffe13f1f458ed16789488bf92b9b2"><div class="ttname"><a href="group__malloc.html#gac7cffe13f1f458ed16789488bf92b9b2">mi_strdup</a></div><div class="ttdeci">char * mi_strdup(const char *s)</div><div class="ttdoc">Allocate and duplicate a string.</div></div>
+<div class="ttc" id="group__heap_html_gaf96c788a1bf553fe2d371de9365e047c"><div class="ttname"><a href="group__heap.html#gaf96c788a1bf553fe2d371de9365e047c">mi_heap_realloc_aligned_at</a></div><div class="ttdeci">void * mi_heap_realloc_aligned_at(mi_heap_t *heap, void *p, size_t newsize, size_t alignment, size_t offset)</div></div>
+<div class="ttc" id="group__malloc_html_gafe68ac7c5e24a65cd55c9d6b152211a0"><div class="ttname"><a href="group__malloc.html#gafe68ac7c5e24a65cd55c9d6b152211a0">mi_reallocf</a></div><div class="ttdeci">void * mi_reallocf(void *p, size_t newsize)</div><div class="ttdoc">Re-allocate memory to newsize bytes,.</div></div>
+<div class="ttc" id="group__malloc_html_ga97fedb4f7107c592fd7f0f0a8949a57d"><div class="ttname"><a href="group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d">mi_calloc</a></div><div class="ttdeci">void * mi_calloc(size_t count, size_t size)</div><div class="ttdoc">Allocate zero-initialized count elements of size bytes.</div></div>
+<div class="ttc" id="group__heap_html_ga45fb43a62776fbebbdf1edd99b527954"><div class="ttname"><a href="group__heap.html#ga45fb43a62776fbebbdf1edd99b527954">mi_heap_zalloc_aligned_at</a></div><div class="ttdeci">void * mi_heap_zalloc_aligned_at(mi_heap_t *heap, size_t size, size_t alignment, size_t offset)</div></div>
+<div class="ttc" id="group__extended_html_ga7136c2e55cb22c98ecf95d08d6debb99"><div class="ttname"><a href="group__extended.html#ga7136c2e55cb22c98ecf95d08d6debb99">mi_malloc_small</a></div><div class="ttdeci">void * mi_malloc_small(size_t size)</div><div class="ttdoc">Allocate a small object.</div></div>
+<div class="ttc" id="group__analysis_html_ga628c237489c2679af84a4d0d143b3dd5"><div class="ttname"><a href="group__analysis.html#ga628c237489c2679af84a4d0d143b3dd5">mi_check_owned</a></div><div class="ttdeci">bool mi_check_owned(const void *p)</div><div class="ttdoc">Check safely if any pointer is part of the default heap of this thread.</div></div>
+<div class="ttc" id="group__heap_html_gab5b87e1805306f70df38789fcfcf6653"><div class="ttname"><a href="group__heap.html#gab5b87e1805306f70df38789fcfcf6653">mi_heap_malloc_aligned</a></div><div class="ttdeci">void * mi_heap_malloc_aligned(mi_heap_t *heap, size_t size, size_t alignment)</div></div>
+<div class="ttc" id="group__options_html_ga7e8af195cc81d3fa64ccf2662caa565a"><div class="ttname"><a href="group__options.html#ga7e8af195cc81d3fa64ccf2662caa565a">mi_option_get</a></div><div class="ttdeci">long mi_option_get(mi_option_t option)</div></div>
+<div class="ttc" id="group__heap_html_ga5d03fbe062ffcf38f0f417fd968357fc"><div class="ttname"><a href="group__heap.html#ga5d03fbe062ffcf38f0f417fd968357fc">mi_heap_get_backing</a></div><div class="ttdeci">mi_heap_t * mi_heap_get_backing()</div><div class="ttdoc">Get the backing heap.</div></div>
+<div class="ttc" id="group__posix_html_ga0d28d5cf61e6bfbb18c63092939fe5c9"><div class="ttname"><a href="group__posix.html#ga0d28d5cf61e6bfbb18c63092939fe5c9">mi_free_aligned</a></div><div class="ttdeci">void mi_free_aligned(void *p, size_t alignment)</div></div>
+<div class="ttc" id="group__cpp_html_gaad048a9fce3d02c5909cd05c6ec24545"><div class="ttname"><a href="group__cpp.html#gaad048a9fce3d02c5909cd05c6ec24545">mi_new</a></div><div class="ttdeci">void * mi_new(std::size_t n) noexcept(false)</div><div class="ttdoc">like mi_malloc(), but when out of memory, use std::get_new_handler and raise std::bad_alloc exception...</div></div>
+<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5">mi_option_reset_delay</a></div><div class="ttdoc">Delay in milli-seconds before resetting a page (100ms by default)</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:766</div></div>
+<div class="ttc" id="group__heap_html_ga766f672ba56f2fbfeb9d9dbb0b7f6b11"><div class="ttname"><a href="group__heap.html#ga766f672ba56f2fbfeb9d9dbb0b7f6b11">mi_heap_new</a></div><div class="ttdeci">mi_heap_t * mi_heap_new()</div><div class="ttdoc">Create a new heap that can be used for allocation.</div></div>
+<div class="ttc" id="group__heap_html_ga9cbed01e42c0647907295de92c3fa296"><div class="ttname"><a href="group__heap.html#ga9cbed01e42c0647907295de92c3fa296">mi_heap_malloc</a></div><div class="ttdeci">void * mi_heap_malloc(mi_heap_t *heap, size_t size)</div><div class="ttdoc">Allocate in a specific heap.</div></div>
+<div class="ttc" id="group__analysis_html_ab47526df656d8837ec3e97f11b83f835"><div class="ttname"><a href="group__analysis.html#ab47526df656d8837ec3e97f11b83f835">mi_heap_area_t::committed</a></div><div class="ttdeci">size_t committed</div><div class="ttdoc">current committed bytes of this area</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:717</div></div>
+<div class="ttc" id="group__options_html_gafebf7ed116adb38ae5218bc3ce06884c"><div class="ttname"><a href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a></div><div class="ttdeci">mi_option_t</div><div class="ttdoc">Runtime options.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:753</div></div>
+<div class="ttc" id="group__analysis_html_ga0d67c1789faaa15ff366c024fcaf6377"><div class="ttname"><a href="group__analysis.html#ga0d67c1789faaa15ff366c024fcaf6377">mi_heap_check_owned</a></div><div class="ttdeci">bool mi_heap_check_owned(mi_heap_t *heap, const void *p)</div><div class="ttdoc">Check safely if any pointer is part of a heap.</div></div>
+<div class="ttc" id="group__heap_html_gab8631ec88c8d26641b68b5d25dcd4422"><div class="ttname"><a href="group__heap.html#gab8631ec88c8d26641b68b5d25dcd4422">mi_heap_set_default</a></div><div class="ttdeci">mi_heap_t * mi_heap_set_default(mi_heap_t *heap)</div><div class="ttdoc">Set the default heap to use for mi_malloc() et al.</div></div>
+</div><!-- fragment --></div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="navelem"><b>mimalloc-doc.h</b></li>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+#projectlogo img {
+ padding: 1ex;
+}
+tt, code, kbd, samp, div.memproto, div.fragment, div.line, table.memname {
+ font-family: Consolas, Monaco, Inconsolata, "Courier New", monospace;
+}
+.image img, .textblock img {
+ max-width: 99%;
+ max-height: 350px;
+}
+table.memname, .memname{
+ font-weight: bold;
+}
+code {
+ background-color: #EEE;
+ padding: 0ex 0.25ex;
+}
+body {
+ margin: 1ex 1ex 0ex 1ex;
+ border: 1px solid black;
+}
+.contents table, .contents div, .contents p, .contents dl {
+ font-size: 16px;
+ line-height: 1.44;
+}
+body #nav-tree .label {
+ font-size: 14px;
+}
+a{
+ text-decoration: underline;
+}
+#side-nav {
+ margin-left: 1ex;
+ border-left: 1px solid black;
+}
+#nav-tree {
+ padding-left: 1ex;
+}
+#nav-path {
+ display: none;
+}
+div.fragment {
+ background-color: #EEE;
+ padding: 0.25ex 0.5ex;
+ border-color: black;
+}
+#nav-sync img {
+ display: none;
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="20mm"
+ height="20mm"
+ viewBox="0 0 10 10"
+ version="1.1"
+ id="svg8"
+ sodipodi:docname="mimalloc-logo.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <defs
+ id="defs2">
+ <linearGradient
+ id="linearGradient6471"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#008da3;stop-opacity:1;"
+ offset="0"
+ id="stop6469" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="23.706667"
+ inkscape:cx="24.864771"
+ inkscape:cy="35.79485"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="3840"
+ inkscape:window-height="2050"
+ inkscape:window-x="-12"
+ inkscape:window-y="-12"
+ inkscape:window-maximized="1"
+ inkscape:snap-object-midpoints="false"
+ inkscape:snap-bbox="false"
+ inkscape:snap-bbox-midpoints="false"
+ inkscape:bbox-nodes="false"
+ inkscape:bbox-paths="false"
+ inkscape:snap-bbox-edge-midpoints="false"
+ showguides="false"
+ showborder="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid815"
+ units="mm"
+ spacingx="0.99999997"
+ spacingy="0.99999997" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-287)">
+ <circle
+ id="path840"
+ cx="5"
+ cy="292"
+ style="fill:#0d8ca4;fill-opacity:0.64444448;fill-rule:nonzero;stroke:#000000;stroke-width:0.56603777;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ r="4.7169809" />
+ <ellipse
+ id="path4522"
+ style="fill:none;stroke:#000000;stroke-width:0.6;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+ cx="5.171"
+ cy="292"
+ r="4.8711185" />
+ <g
+ aria-label="malloc"
+ transform="matrix(0.9031136,0,0,0.80782132,0.58122269,37.023319)"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;line-height:1.25;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.28599727"
+ id="text6501">
+ <path
+ inkscape:connector-curvature="0"
+ d="m 1.7799307,318.87079 c 0.029047,0 0.053624,-0.009 0.075968,-0.0313 0.020109,-0.0201 0.031281,-0.0447 0.031281,-0.076 v -0.70159 c 0.013406,-0.0223 0.031281,-0.0469 0.05139,-0.0693 0.017875,-0.0223 0.049156,-0.0447 0.089374,-0.0693 0.040218,-0.0223 0.082671,-0.0358 0.1273581,-0.0358 0.040218,0 0.078202,0.0179 0.1117177,0.0536 0.031281,0.0358 0.049156,0.0827 0.049156,0.143 v 0.67924 c 0,0.0313 0.00894,0.0559 0.031281,0.076 0.020109,0.0223 0.044687,0.0313 0.075968,0.0313 0.029047,0 0.053624,-0.009 0.075968,-0.0313 0.020109,-0.0201 0.031281,-0.0447 0.031281,-0.076 v -0.67924 -0.0223 c 0.0067,-0.0112 0.01564,-0.0223 0.024578,-0.0358 0.00894,-0.0134 0.022344,-0.0268 0.040218,-0.0447 0.017875,-0.0179 0.03575,-0.0335 0.053624,-0.0469 0.017875,-0.0134 0.042453,-0.0246 0.069265,-0.0335 0.026812,-0.009 0.053625,-0.0134 0.080437,-0.0134 0.040218,0 0.078202,0.0179 0.1117177,0.0536 0.031281,0.0358 0.049156,0.0827 0.049156,0.143 v 0.67924 c 0,0.0313 0.00894,0.0559 0.031281,0.076 0.020109,0.0223 0.044687,0.0313 0.075968,0.0313 0.029047,0 0.053624,-0.009 0.075968,-0.0313 0.020109,-0.0201 0.031281,-0.0447 0.031281,-0.076 v -0.67924 c 0,-0.11396 -0.037984,-0.21003 -0.1094833,-0.29047 -0.073734,-0.0804 -0.1631078,-0.12066 -0.2658881,-0.12066 -0.073734,0 -0.1407643,0.0156 -0.1988575,0.0425 -0.058093,0.0268 -0.1094833,0.0626 -0.1541704,0.10725 -0.075968,-0.0983 -0.1720452,-0.14971 -0.290466,-0.14971 -0.1027802,0 -0.1943887,0.029 -0.2748255,0.0849 -0.00894,-0.0179 -0.022343,-0.0335 -0.040218,-0.0469 -0.017875,-0.0134 -0.037984,-0.0201 -0.060328,-0.0201 -0.031281,0 -0.055859,0.0112 -0.075968,0.0313 -0.022343,0.0223 -0.031281,0.0469 -0.031281,0.076 v 0.96524 c 0,0.0313 0.00894,0.0559 0.031281,0.076 0.020109,0.0223 0.044687,0.0313 0.075968,0.0313 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.28599727"
+ id="path6515" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 4.2824069,317.69105 c -0.01564,0 -0.029047,0.004 -0.042453,0.009 -0.013406,0.007 -0.026812,0.0156 -0.03575,0.0246 -0.00894,0.0112 -0.017875,0.0223 -0.022344,0.0335 -0.078202,-0.0559 -0.1631079,-0.0849 -0.2569507,-0.0849 -0.145233,0 -0.2658881,0.0626 -0.359731,0.18322 -0.093843,0.12066 -0.1407642,0.26366 -0.1407642,0.42453 0,0.16311 0.046921,0.30611 0.1407642,0.42676 0.093843,0.12066 0.214498,0.18098 0.359731,0.18098 0.093843,0 0.1787483,-0.0268 0.2569507,-0.0849 0.00894,0.0201 0.022344,0.0358 0.040218,0.0491 0.017875,0.0134 0.037984,0.0179 0.060328,0.0179 0.029047,0 0.053625,-0.009 0.075968,-0.0313 0.020109,-0.0201 0.031281,-0.0447 0.031281,-0.076 v -0.96524 c 0,-0.029 -0.011172,-0.0536 -0.031281,-0.076 -0.022343,-0.0201 -0.046921,-0.0313 -0.075968,-0.0313 z m -0.107249,0.77979 c -0.017875,0.0424 -0.040218,0.0782 -0.067031,0.10948 -0.026812,0.0313 -0.055859,0.0559 -0.08714,0.0715 -0.031281,0.0156 -0.064796,0.0224 -0.096077,0.0224 -0.073734,0 -0.1407643,-0.0358 -0.1988575,-0.10949 -0.058093,-0.0737 -0.08714,-0.16757 -0.08714,-0.28376 0,-0.11395 0.029047,-0.2078 0.08714,-0.28153 0.058093,-0.0737 0.1251238,-0.11172 0.1988575,-0.11172 0.049156,0 0.098312,0.0179 0.1429986,0.0536 0.044687,0.0358 0.080437,0.0871 0.107249,0.1497 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.28599727"
+ id="path6517" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 4.7471525,317.2263 c -0.031281,0 -0.055859,0.0112 -0.075968,0.0313 -0.022343,0.0223 -0.031281,0.0469 -0.031281,0.076 v 1.21549 c 0,0.10502 0.0067,0.18545 0.022343,0.24131 0.00447,0.0268 0.017875,0.0469 0.037984,0.0603 0.017875,0.0134 0.040218,0.0201 0.064796,0.0201 0.00894,0 0.017875,0 0.026812,-0.002 0.00447,-0.002 0.011172,-0.004 0.017875,-0.009 0.0067,-0.002 0.013406,-0.004 0.017875,-0.009 0.00447,-0.004 0.011172,-0.009 0.01564,-0.0134 0.00447,-0.004 0.0067,-0.0112 0.011172,-0.0156 0.00447,-0.004 0.0067,-0.0112 0.00894,-0.0179 0.00223,-0.007 0.00447,-0.0134 0.0067,-0.0201 0,-0.007 0.00223,-0.0134 0.00223,-0.0201 v -0.007 c 0,-0.002 -0.00223,-0.004 -0.00223,-0.007 0,-0.002 0,-0.004 0,-0.009 -0.011172,-0.0447 -0.01564,-0.10725 -0.01564,-0.19216 v -1.21549 c 0,-0.029 -0.011172,-0.0536 -0.031281,-0.076 -0.022343,-0.0201 -0.046921,-0.0313 -0.075968,-0.0313 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.28599727"
+ id="path6519" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 5.2655226,317.2263 c -0.031281,0 -0.055859,0.0112 -0.075968,0.0313 -0.022343,0.0223 -0.031281,0.0469 -0.031281,0.076 v 1.21549 c 0,0.10502 0.0067,0.18545 0.022344,0.24131 0.00447,0.0268 0.017875,0.0469 0.037984,0.0603 0.017875,0.0134 0.040218,0.0201 0.064796,0.0201 0.00894,0 0.017875,0 0.026812,-0.002 0.00447,-0.002 0.011172,-0.004 0.017875,-0.009 0.0067,-0.002 0.013406,-0.004 0.017875,-0.009 0.00447,-0.004 0.011172,-0.009 0.01564,-0.0134 0.00447,-0.004 0.0067,-0.0112 0.011172,-0.0156 0.00447,-0.004 0.0067,-0.0112 0.00894,-0.0179 0.00223,-0.007 0.00447,-0.0134 0.0067,-0.0201 0,-0.007 0.00223,-0.0134 0.00223,-0.0201 v -0.007 c 0,-0.002 -0.00223,-0.004 -0.00223,-0.007 0,-0.002 0,-0.004 0,-0.009 -0.011172,-0.0447 -0.01564,-0.10725 -0.01564,-0.19216 v -1.21549 c 0,-0.029 -0.011172,-0.0536 -0.031281,-0.076 -0.022343,-0.0201 -0.046921,-0.0313 -0.075968,-0.0313 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.28599727"
+ id="path6521" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 6.6061344,318.28092 c 0,-0.16087 -0.049156,-0.30387 -0.1429986,-0.42453 -0.093843,-0.12065 -0.2144979,-0.18322 -0.3574966,-0.18322 -0.145233,0 -0.2658881,0.0626 -0.3597309,0.18322 -0.093843,0.12066 -0.1407643,0.26366 -0.1407643,0.42453 0,0.16311 0.046921,0.30611 0.1407643,0.42676 0.093843,0.12066 0.2144979,0.18098 0.3597309,0.18098 0.1429987,0 0.2636538,-0.0603 0.3574966,-0.18098 0.093843,-0.12065 0.1429986,-0.26365 0.1429986,-0.42676 z m -0.2144979,0 c 0,0.11619 -0.031281,0.21003 -0.089374,0.28376 -0.058093,0.0737 -0.1251238,0.10949 -0.1966231,0.10949 -0.073734,0 -0.1407643,-0.0358 -0.1988575,-0.10949 -0.058093,-0.0737 -0.08714,-0.16757 -0.08714,-0.28376 0,-0.11395 0.029047,-0.2078 0.08714,-0.28153 0.058093,-0.0737 0.1251238,-0.11172 0.1988575,-0.11172 0.071499,0 0.1385299,0.038 0.1966231,0.11172 0.058093,0.0737 0.089374,0.16758 0.089374,0.28153 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.28599727"
+ id="path6523" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 7.6406407,318.47754 c -0.00894,-0.004 -0.01564,-0.007 -0.024578,-0.009 -0.00894,-0.002 -0.017875,-0.004 -0.026812,-0.004 -0.044687,0 -0.075968,0.0201 -0.093843,0.0559 -0.026812,0.0514 -0.060328,0.0894 -0.098312,0.11618 -0.040218,0.0268 -0.080437,0.038 -0.1206551,0.038 -0.071499,0 -0.1340612,-0.0358 -0.1921544,-0.10949 -0.058093,-0.0737 -0.084905,-0.16757 -0.084905,-0.28376 0,-0.11395 0.026812,-0.2078 0.084905,-0.28153 0.058093,-0.0737 0.1206551,-0.11172 0.1921544,-0.11172 0.080437,0 0.1474674,0.0447 0.2055606,0.1296 0.020109,0.0335 0.049156,0.0491 0.089374,0.0491 0.022343,0 0.040218,-0.004 0.058093,-0.0179 0.0067,-0.004 0.013406,-0.009 0.020109,-0.0156 0.0067,-0.007 0.011172,-0.0134 0.01564,-0.0224 0.00447,-0.009 0.0067,-0.0156 0.00894,-0.0246 0.00223,-0.009 0.00447,-0.0179 0.00447,-0.0268 0,-0.004 -0.00223,-0.0112 -0.00223,-0.0156 0,-0.004 -0.00223,-0.009 -0.00223,-0.0134 -0.00223,-0.004 -0.00447,-0.0112 -0.0067,-0.0156 -0.00223,-0.004 -0.00447,-0.009 -0.0067,-0.0134 -0.031281,-0.0447 -0.064796,-0.0827 -0.1027803,-0.11619 -0.037984,-0.0335 -0.080437,-0.0603 -0.1295925,-0.0804 -0.049156,-0.0201 -0.1005459,-0.0313 -0.1519361,-0.0313 -0.1429986,0 -0.2614194,0.0626 -0.3530279,0.18322 -0.093843,0.12066 -0.1385299,0.26366 -0.1385299,0.42453 0,0.16311 0.044687,0.30611 0.1385299,0.42676 0.091608,0.12066 0.2100293,0.18098 0.3530279,0.18098 0.084905,0 0.1631078,-0.0246 0.2346072,-0.0737 0.071499,-0.0492 0.1273581,-0.11172 0.1720452,-0.19215 0.0067,-0.0134 0.011172,-0.0313 0.013406,-0.0514 0,-0.0425 -0.020109,-0.0737 -0.055859,-0.0939 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.28797817px;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.28599727"
+ id="path6525" />
+ </g>
+ <g
+ aria-label="m"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.3694315px;line-height:1.25;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.15923578"
+ id="text848">
+ <path
+ d="m 2.3718985,293.17081 c 0.080862,0 0.1492836,-0.0249 0.211485,-0.0871 0.055981,-0.056 0.087082,-0.1244 0.087082,-0.21148 v -1.95313 c 0.037321,-0.0622 0.087082,-0.13062 0.1430634,-0.19282 0.049761,-0.0622 0.1368433,-0.1244 0.2488059,-0.19283 0.1119627,-0.0622 0.2301455,-0.0995 0.3545485,-0.0995 0.1119626,0 0.2177051,0.0498 0.3110074,0.14929 0.087082,0.0995 0.1368432,0.23014 0.1368432,0.39808 v 1.89093 c 0,0.0871 0.024881,0.1555 0.087082,0.21148 0.055981,0.0622 0.124403,0.0871 0.211485,0.0871 0.080862,0 0.1492836,-0.0249 0.2114851,-0.0871 0.055981,-0.056 0.087082,-0.1244 0.087082,-0.21148 v -1.89093 -0.0622 c 0.018661,-0.0311 0.043541,-0.0622 0.068422,-0.0995 0.024881,-0.0373 0.062201,-0.0746 0.1119626,-0.1244 0.049761,-0.0498 0.099522,-0.0933 0.1492836,-0.13063 0.049761,-0.0373 0.1181828,-0.0684 0.1928246,-0.0933 0.074642,-0.0249 0.1492835,-0.0373 0.2239253,-0.0373 0.1119626,0 0.2177052,0.0498 0.3110074,0.14929 0.087082,0.0995 0.1368432,0.23014 0.1368432,0.39808 v 1.89093 c 0,0.0871 0.024881,0.1555 0.087082,0.21148 0.055981,0.0622 0.124403,0.0871 0.211485,0.0871 0.080862,0 0.1492836,-0.0249 0.2114851,-0.0871 0.055981,-0.056 0.087082,-0.1244 0.087082,-0.21148 v -1.89093 c 0,-0.31722 -0.1057425,-0.58469 -0.3047872,-0.80861 -0.2052649,-0.22393 -0.4540708,-0.33589 -0.7401976,-0.33589 -0.2052649,0 -0.3918693,0.0435 -0.5535932,0.11818 -0.1617238,0.0746 -0.3047872,0.17416 -0.4291902,0.29857 -0.211485,-0.27369 -0.4789514,-0.41675 -0.8086192,-0.41675 -0.2861268,0 -0.5411529,0.0809 -0.7650782,0.23636 -0.024881,-0.0498 -0.062202,-0.0933 -0.1119627,-0.13062 -0.049761,-0.0373 -0.1057425,-0.056 -0.167944,-0.056 -0.087082,0 -0.1555037,0.0311 -0.211485,0.0871 -0.062202,0.0622 -0.087082,0.13062 -0.087082,0.21149 v 2.6871 c 0,0.0871 0.024881,0.1555 0.087082,0.21148 0.055981,0.0622 0.1244029,0.0871 0.211485,0.0871 z"
+ style="stroke-width:0.15923578"
+ id="path834" />
+ </g>
+ <g
+ id="g28"
+ transform="translate(-0.23995531,0.02790178)">
+ <g
+ id="g835">
+ <g
+ transform="matrix(1.0000001,0,0,0.98554676,-7.6075554e-7,4.2369817)"
+ id="g25">
+ <path
+ d="m 7.426334,293.15097 c 0.080862,0 0.1492836,-0.0249 0.2114851,-0.0871 0.055981,-0.056 0.087082,-0.1244 0.087082,-0.21149 v -2.6871 c 0,-0.0809 -0.031101,-0.14928 -0.087082,-0.21149 -0.062201,-0.056 -0.1306232,-0.0871 -0.2114851,-0.0871 -0.087082,0 -0.1555037,0.0311 -0.211485,0.0871 -0.062202,0.0622 -0.087082,0.13063 -0.087082,0.21149 v 2.6871 c 0,0.0871 0.024881,0.15551 0.087082,0.21149 0.055981,0.0622 0.124403,0.0871 0.211485,0.0871 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:1.25;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.15923578"
+ id="path896"
+ inkscape:connector-curvature="0" />
+ </g>
+ <path
+ d="m 7.4249389,289.61754 c 0.080862,0 0.1492836,-0.0249 0.2114851,-0.0871 0.055981,-0.056 0.086228,-0.1244 0.087082,-0.21149 l 0.0014,-0.14231 c 7.93e-4,-0.0809 -0.031101,-0.14929 -0.087082,-0.21149 -0.062201,-0.056 -0.1306232,-0.0871 -0.2114851,-0.0871 -0.087082,0 -0.1555037,0.0311 -0.211485,0.0871 -0.062202,0.0622 -0.086289,0.13062 -0.087082,0.21149 l -0.0014,0.14231 c -8.538e-4,0.0871 0.024881,0.15551 0.087082,0.21149 0.055981,0.0622 0.124403,0.0871 0.211485,0.0871 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:1.25;font-family:RoutedGothicEx;-inkscape-font-specification:'RoutedGothicEx, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.15923578"
+ id="path898"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="scsscscsscs" />
+ </g>
+ </g>
+ </g>
+</svg>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Modules</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('modules.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="headertitle">
+<div class="title">Modules</div> </div>
+</div><!--header-->
+<div class="contents">
+<div class="textblock">Here is a list of all modules:</div><div class="directory">
+<table class="directory">
+<tr id="row_0_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="group__malloc.html" target="_self">Basic Allocation</a></td><td class="desc">The basic allocation interface </td></tr>
+<tr id="row_1_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="group__extended.html" target="_self">Extended Functions</a></td><td class="desc">Extended functionality </td></tr>
+<tr id="row_2_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="group__aligned.html" target="_self">Aligned Allocation</a></td><td class="desc">Allocating aligned memory blocks </td></tr>
+<tr id="row_3_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="group__heap.html" target="_self">Heap Allocation</a></td><td class="desc">First-class heaps that can be destroyed in one go </td></tr>
+<tr id="row_4_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="group__zeroinit.html" target="_self">Zero initialized re-allocation</a></td><td class="desc">The zero-initialized re-allocations are only valid on memory that was originally allocated with zero initialization too </td></tr>
+<tr id="row_5_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="group__typed.html" target="_self">Typed Macros</a></td><td class="desc">Typed allocation macros </td></tr>
+<tr id="row_6_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="group__analysis.html" target="_self">Heap Introspection</a></td><td class="desc">Inspect the heap at runtime </td></tr>
+<tr id="row_7_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="group__options.html" target="_self">Runtime Options</a></td><td class="desc">Set runtime behavior </td></tr>
+<tr id="row_8_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="group__posix.html" target="_self">Posix</a></td><td class="desc"><code>mi_</code> prefixed implementations of various Posix, Unix, and C++ allocation functions </td></tr>
+<tr id="row_9_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="group__cpp.html" target="_self">C++ wrappers</a></td><td class="desc"><code>mi_</code> prefixed implementations of various allocation functions that use C++ semantics on out-of-memory, generally calling <code>std::get_new_handler</code> and raising a <code>std::bad_alloc</code> exception on failure </td></tr>
+</table>
+</div><!-- directory -->
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+var modules =
+[
+ [ "Basic Allocation", "group__malloc.html", "group__malloc" ],
+ [ "Extended Functions", "group__extended.html", "group__extended" ],
+ [ "Aligned Allocation", "group__aligned.html", "group__aligned" ],
+ [ "Heap Allocation", "group__heap.html", "group__heap" ],
+ [ "Zero initialized re-allocation", "group__zeroinit.html", "group__zeroinit" ],
+ [ "Typed Macros", "group__typed.html", "group__typed" ],
+ [ "Heap Introspection", "group__analysis.html", "group__analysis" ],
+ [ "Runtime Options", "group__options.html", "group__options" ],
+ [ "Posix", "group__posix.html", "group__posix" ],
+ [ "C++ wrappers", "group__cpp.html", "group__cpp" ]
+];
\ No newline at end of file
--- /dev/null
+#nav-tree .children_ul {
+ margin:0;
+ padding:4px;
+}
+
+#nav-tree ul {
+ list-style:none outside none;
+ margin:0px;
+ padding:0px;
+}
+
+#nav-tree li {
+ white-space:nowrap;
+ margin:0px;
+ padding:0px;
+}
+
+#nav-tree .plus {
+ margin:0px;
+}
+
+#nav-tree .selected {
+ background-image: url('tab_a.png');
+ background-repeat:repeat-x;
+ color: #fff;
+ text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0);
+}
+
+#nav-tree img {
+ margin:0px;
+ padding:0px;
+ border:0px;
+ vertical-align: middle;
+}
+
+#nav-tree a {
+ text-decoration:none;
+ padding:0px;
+ margin:0px;
+ outline:none;
+}
+
+#nav-tree .label {
+ margin:0px;
+ padding:0px;
+ font: 12px 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif;
+}
+
+#nav-tree .label a {
+ padding:2px;
+}
+
+#nav-tree .selected a {
+ text-decoration:none;
+ color:#fff;
+}
+
+#nav-tree .children_ul {
+ margin:0px;
+ padding:0px;
+}
+
+#nav-tree .item {
+ margin:0px;
+ padding:0px;
+}
+
+#nav-tree {
+ padding: 0px 0px;
+ background-color: #FAFAFF;
+ font-size:14px;
+ overflow:auto;
+}
+
+#doc-content {
+ overflow:auto;
+ display:block;
+ padding:0px;
+ margin:0px;
+ -webkit-overflow-scrolling : touch; /* iOS 5+ */
+}
+
+#side-nav {
+ padding:0 6px 0 0;
+ margin: 0px;
+ display:block;
+ position: absolute;
+ left: 0px;
+ width: 180px;
+}
+
+.ui-resizable .ui-resizable-handle {
+ display:block;
+}
+
+.ui-resizable-e {
+ background-image:url("splitbar.png");
+ background-size:100%;
+ background-repeat:repeat-y;
+ background-attachment: scroll;
+ cursor:ew-resize;
+ height:100%;
+ right:0;
+ top:0;
+ width:6px;
+}
+
+.ui-resizable-handle {
+ display:none;
+ font-size:0.1px;
+ position:absolute;
+ z-index:1;
+}
+
+#nav-tree-contents {
+ margin: 6px 0px 0px 0px;
+}
+
+#nav-tree {
+ background-image:url('nav_h.png');
+ background-repeat:repeat-x;
+ background-color: #F2F3F3;
+ -webkit-overflow-scrolling : touch; /* iOS 5+ */
+}
+
+#nav-sync {
+ position:absolute;
+ top:5px;
+ right:24px;
+ z-index:0;
+}
+
+#nav-sync img {
+ opacity:0.3;
+}
+
+#nav-sync img:hover {
+ opacity:0.9;
+}
+
+@media print
+{
+ #nav-tree { display: none; }
+ div.ui-resizable-handle { display: none; position: relative; }
+}
+
--- /dev/null
+/*
+ @licstart The following is the entire license notice for the
+ JavaScript code in this file.
+
+ Copyright (C) 1997-2017 by Dimitri van Heesch
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ @licend The above is the entire license notice
+ for the JavaScript code in this file
+ */
+var navTreeSubIndices = new Array();
+var arrowDown = '▼';
+var arrowRight = '►';
+
+function getData(varName)
+{
+ var i = varName.lastIndexOf('/');
+ var n = i>=0 ? varName.substring(i+1) : varName;
+ return eval(n.replace(/\-/g,'_'));
+}
+
+function stripPath(uri)
+{
+ return uri.substring(uri.lastIndexOf('/')+1);
+}
+
+function stripPath2(uri)
+{
+ var i = uri.lastIndexOf('/');
+ var s = uri.substring(i+1);
+ var m = uri.substring(0,i+1).match(/\/d\w\/d\w\w\/$/);
+ return m ? uri.substring(i-6) : s;
+}
+
+function hashValue()
+{
+ return $(location).attr('hash').substring(1).replace(/[^\w\-]/g,'');
+}
+
+function hashUrl()
+{
+ return '#'+hashValue();
+}
+
+function pathName()
+{
+ return $(location).attr('pathname').replace(/[^-A-Za-z0-9+&@#/%?=~_|!:,.;\(\)]/g, '');
+}
+
+function localStorageSupported()
+{
+ try {
+ return 'localStorage' in window && window['localStorage'] !== null && window.localStorage.getItem;
+ }
+ catch(e) {
+ return false;
+ }
+}
+
+
+function storeLink(link)
+{
+ if (!$("#nav-sync").hasClass('sync') && localStorageSupported()) {
+ window.localStorage.setItem('navpath',link);
+ }
+}
+
+function deleteLink()
+{
+ if (localStorageSupported()) {
+ window.localStorage.setItem('navpath','');
+ }
+}
+
+function cachedLink()
+{
+ if (localStorageSupported()) {
+ return window.localStorage.getItem('navpath');
+ } else {
+ return '';
+ }
+}
+
+function getScript(scriptName,func,show)
+{
+ var head = document.getElementsByTagName("head")[0];
+ var script = document.createElement('script');
+ script.id = scriptName;
+ script.type = 'text/javascript';
+ script.onload = func;
+ script.src = scriptName+'.js';
+ if ($.browser.msie && $.browser.version<=8) {
+ // script.onload does not work with older versions of IE
+ script.onreadystatechange = function() {
+ if (script.readyState=='complete' || script.readyState=='loaded') {
+ func(); if (show) showRoot();
+ }
+ }
+ }
+ head.appendChild(script);
+}
+
+function createIndent(o,domNode,node,level)
+{
+ var level=-1;
+ var n = node;
+ while (n.parentNode) { level++; n=n.parentNode; }
+ if (node.childrenData) {
+ var imgNode = document.createElement("span");
+ imgNode.className = 'arrow';
+ imgNode.style.paddingLeft=(16*level).toString()+'px';
+ imgNode.innerHTML=arrowRight;
+ node.plus_img = imgNode;
+ node.expandToggle = document.createElement("a");
+ node.expandToggle.href = "javascript:void(0)";
+ node.expandToggle.onclick = function() {
+ if (node.expanded) {
+ $(node.getChildrenUL()).slideUp("fast");
+ node.plus_img.innerHTML=arrowRight;
+ node.expanded = false;
+ } else {
+ expandNode(o, node, false, false);
+ }
+ }
+ node.expandToggle.appendChild(imgNode);
+ domNode.appendChild(node.expandToggle);
+ } else {
+ var span = document.createElement("span");
+ span.className = 'arrow';
+ span.style.width = 16*(level+1)+'px';
+ span.innerHTML = ' ';
+ domNode.appendChild(span);
+ }
+}
+
+var animationInProgress = false;
+
+function gotoAnchor(anchor,aname,updateLocation)
+{
+ var pos, docContent = $('#doc-content');
+ var ancParent = $(anchor.parent());
+ if (ancParent.hasClass('memItemLeft') ||
+ ancParent.hasClass('fieldname') ||
+ ancParent.hasClass('fieldtype') ||
+ ancParent.is(':header'))
+ {
+ pos = ancParent.position().top;
+ } else if (anchor.position()) {
+ pos = anchor.position().top;
+ }
+ if (pos) {
+ var dist = Math.abs(Math.min(
+ pos-docContent.offset().top,
+ docContent[0].scrollHeight-
+ docContent.height()-docContent.scrollTop()));
+ animationInProgress=true;
+ docContent.animate({
+ scrollTop: pos + docContent.scrollTop() - docContent.offset().top
+ },Math.max(50,Math.min(500,dist)),function(){
+ if (updateLocation) window.location.href=aname;
+ animationInProgress=false;
+ });
+ }
+}
+
+function newNode(o, po, text, link, childrenData, lastNode)
+{
+ var node = new Object();
+ node.children = Array();
+ node.childrenData = childrenData;
+ node.depth = po.depth + 1;
+ node.relpath = po.relpath;
+ node.isLast = lastNode;
+
+ node.li = document.createElement("li");
+ po.getChildrenUL().appendChild(node.li);
+ node.parentNode = po;
+
+ node.itemDiv = document.createElement("div");
+ node.itemDiv.className = "item";
+
+ node.labelSpan = document.createElement("span");
+ node.labelSpan.className = "label";
+
+ createIndent(o,node.itemDiv,node,0);
+ node.itemDiv.appendChild(node.labelSpan);
+ node.li.appendChild(node.itemDiv);
+
+ var a = document.createElement("a");
+ node.labelSpan.appendChild(a);
+ node.label = document.createTextNode(text);
+ node.expanded = false;
+ a.appendChild(node.label);
+ if (link) {
+ var url;
+ if (link.substring(0,1)=='^') {
+ url = link.substring(1);
+ link = url;
+ } else {
+ url = node.relpath+link;
+ }
+ a.className = stripPath(link.replace('#',':'));
+ if (link.indexOf('#')!=-1) {
+ var aname = '#'+link.split('#')[1];
+ var srcPage = stripPath(pathName());
+ var targetPage = stripPath(link.split('#')[0]);
+ a.href = srcPage!=targetPage ? url : "javascript:void(0)";
+ a.onclick = function(){
+ storeLink(link);
+ if (!$(a).parent().parent().hasClass('selected'))
+ {
+ $('.item').removeClass('selected');
+ $('.item').removeAttr('id');
+ $(a).parent().parent().addClass('selected');
+ $(a).parent().parent().attr('id','selected');
+ }
+ var anchor = $(aname);
+ gotoAnchor(anchor,aname,true);
+ };
+ } else {
+ a.href = url;
+ a.onclick = function() { storeLink(link); }
+ }
+ } else {
+ if (childrenData != null)
+ {
+ a.className = "nolink";
+ a.href = "javascript:void(0)";
+ a.onclick = node.expandToggle.onclick;
+ }
+ }
+
+ node.childrenUL = null;
+ node.getChildrenUL = function() {
+ if (!node.childrenUL) {
+ node.childrenUL = document.createElement("ul");
+ node.childrenUL.className = "children_ul";
+ node.childrenUL.style.display = "none";
+ node.li.appendChild(node.childrenUL);
+ }
+ return node.childrenUL;
+ };
+
+ return node;
+}
+
+function showRoot()
+{
+ var headerHeight = $("#top").height();
+ var footerHeight = $("#nav-path").height();
+ var windowHeight = $(window).height() - headerHeight - footerHeight;
+ (function (){ // retry until we can scroll to the selected item
+ try {
+ var navtree=$('#nav-tree');
+ navtree.scrollTo('#selected',0,{offset:-windowHeight/2});
+ } catch (err) {
+ setTimeout(arguments.callee, 0);
+ }
+ })();
+}
+
+function expandNode(o, node, imm, showRoot)
+{
+ if (node.childrenData && !node.expanded) {
+ if (typeof(node.childrenData)==='string') {
+ var varName = node.childrenData;
+ getScript(node.relpath+varName,function(){
+ node.childrenData = getData(varName);
+ expandNode(o, node, imm, showRoot);
+ }, showRoot);
+ } else {
+ if (!node.childrenVisited) {
+ getNode(o, node);
+ } if (imm || ($.browser.msie && $.browser.version>8)) {
+ // somehow slideDown jumps to the start of tree for IE9 :-(
+ $(node.getChildrenUL()).show();
+ } else {
+ $(node.getChildrenUL()).slideDown("fast");
+ }
+ node.plus_img.innerHTML = arrowDown;
+ node.expanded = true;
+ }
+ }
+}
+
+function glowEffect(n,duration)
+{
+ n.addClass('glow').delay(duration).queue(function(next){
+ $(this).removeClass('glow');next();
+ });
+}
+
+function highlightAnchor()
+{
+ var aname = hashUrl();
+ var anchor = $(aname);
+ if (anchor.parent().attr('class')=='memItemLeft'){
+ var rows = $('.memberdecls tr[class$="'+hashValue()+'"]');
+ glowEffect(rows.children(),300); // member without details
+ } else if (anchor.parent().attr('class')=='fieldname'){
+ glowEffect(anchor.parent().parent(),1000); // enum value
+ } else if (anchor.parent().attr('class')=='fieldtype'){
+ glowEffect(anchor.parent().parent(),1000); // struct field
+ } else if (anchor.parent().is(":header")) {
+ glowEffect(anchor.parent(),1000); // section header
+ } else {
+ glowEffect(anchor.next(),1000); // normal member
+ }
+ gotoAnchor(anchor,aname,false);
+}
+
+function selectAndHighlight(hash,n)
+{
+ var a;
+ if (hash) {
+ var link=stripPath(pathName())+':'+hash.substring(1);
+ a=$('.item a[class$="'+link+'"]');
+ }
+ if (a && a.length) {
+ a.parent().parent().addClass('selected');
+ a.parent().parent().attr('id','selected');
+ highlightAnchor();
+ } else if (n) {
+ $(n.itemDiv).addClass('selected');
+ $(n.itemDiv).attr('id','selected');
+ }
+ if ($('#nav-tree-contents .item:first').hasClass('selected')) {
+ $('#nav-sync').css('top','30px');
+ } else {
+ $('#nav-sync').css('top','5px');
+ }
+ showRoot();
+}
+
+function showNode(o, node, index, hash)
+{
+ if (node && node.childrenData) {
+ if (typeof(node.childrenData)==='string') {
+ var varName = node.childrenData;
+ getScript(node.relpath+varName,function(){
+ node.childrenData = getData(varName);
+ showNode(o,node,index,hash);
+ },true);
+ } else {
+ if (!node.childrenVisited) {
+ getNode(o, node);
+ }
+ $(node.getChildrenUL()).css({'display':'block'});
+ node.plus_img.innerHTML = arrowDown;
+ node.expanded = true;
+ var n = node.children[o.breadcrumbs[index]];
+ if (index+1<o.breadcrumbs.length) {
+ showNode(o,n,index+1,hash);
+ } else {
+ if (typeof(n.childrenData)==='string') {
+ var varName = n.childrenData;
+ getScript(n.relpath+varName,function(){
+ n.childrenData = getData(varName);
+ node.expanded=false;
+ showNode(o,node,index,hash); // retry with child node expanded
+ },true);
+ } else {
+ var rootBase = stripPath(o.toroot.replace(/\..+$/, ''));
+ if (rootBase=="index" || rootBase=="pages" || rootBase=="search") {
+ expandNode(o, n, true, true);
+ }
+ selectAndHighlight(hash,n);
+ }
+ }
+ }
+ } else {
+ selectAndHighlight(hash);
+ }
+}
+
+function removeToInsertLater(element) {
+ var parentNode = element.parentNode;
+ var nextSibling = element.nextSibling;
+ parentNode.removeChild(element);
+ return function() {
+ if (nextSibling) {
+ parentNode.insertBefore(element, nextSibling);
+ } else {
+ parentNode.appendChild(element);
+ }
+ };
+}
+
+function getNode(o, po)
+{
+ var insertFunction = removeToInsertLater(po.li);
+ po.childrenVisited = true;
+ var l = po.childrenData.length-1;
+ for (var i in po.childrenData) {
+ var nodeData = po.childrenData[i];
+ po.children[i] = newNode(o, po, nodeData[0], nodeData[1], nodeData[2],
+ i==l);
+ }
+ insertFunction();
+}
+
+function gotoNode(o,subIndex,root,hash,relpath)
+{
+ var nti = navTreeSubIndices[subIndex][root+hash];
+ o.breadcrumbs = $.extend(true, [], nti ? nti : navTreeSubIndices[subIndex][root]);
+ if (!o.breadcrumbs && root!=NAVTREE[0][1]) { // fallback: show index
+ navTo(o,NAVTREE[0][1],"",relpath);
+ $('.item').removeClass('selected');
+ $('.item').removeAttr('id');
+ }
+ if (o.breadcrumbs) {
+ o.breadcrumbs.unshift(0); // add 0 for root node
+ showNode(o, o.node, 0, hash);
+ }
+}
+
+function navTo(o,root,hash,relpath)
+{
+ var link = cachedLink();
+ if (link) {
+ var parts = link.split('#');
+ root = parts[0];
+ if (parts.length>1) hash = '#'+parts[1].replace(/[^\w\-]/g,'');
+ else hash='';
+ }
+ if (hash.match(/^#l\d+$/)) {
+ var anchor=$('a[name='+hash.substring(1)+']');
+ glowEffect(anchor.parent(),1000); // line number
+ hash=''; // strip line number anchors
+ }
+ var url=root+hash;
+ var i=-1;
+ while (NAVTREEINDEX[i+1]<=url) i++;
+ if (i==-1) { i=0; root=NAVTREE[0][1]; } // fallback: show index
+ if (navTreeSubIndices[i]) {
+ gotoNode(o,i,root,hash,relpath)
+ } else {
+ getScript(relpath+'navtreeindex'+i,function(){
+ navTreeSubIndices[i] = eval('NAVTREEINDEX'+i);
+ if (navTreeSubIndices[i]) {
+ gotoNode(o,i,root,hash,relpath);
+ }
+ },true);
+ }
+}
+
+function showSyncOff(n,relpath)
+{
+ n.html('<img src="'+relpath+'sync_off.png" title="'+SYNCOFFMSG+'"/>');
+}
+
+function showSyncOn(n,relpath)
+{
+ n.html('<img src="'+relpath+'sync_on.png" title="'+SYNCONMSG+'"/>');
+}
+
+function toggleSyncButton(relpath)
+{
+ var navSync = $('#nav-sync');
+ if (navSync.hasClass('sync')) {
+ navSync.removeClass('sync');
+ showSyncOff(navSync,relpath);
+ storeLink(stripPath2(pathName())+hashUrl());
+ } else {
+ navSync.addClass('sync');
+ showSyncOn(navSync,relpath);
+ deleteLink();
+ }
+}
+
+function initNavTree(toroot,relpath)
+{
+ var o = new Object();
+ o.toroot = toroot;
+ o.node = new Object();
+ o.node.li = document.getElementById("nav-tree-contents");
+ o.node.childrenData = NAVTREE;
+ o.node.children = new Array();
+ o.node.childrenUL = document.createElement("ul");
+ o.node.getChildrenUL = function() { return o.node.childrenUL; };
+ o.node.li.appendChild(o.node.childrenUL);
+ o.node.depth = 0;
+ o.node.relpath = relpath;
+ o.node.expanded = false;
+ o.node.isLast = true;
+ o.node.plus_img = document.createElement("span");
+ o.node.plus_img.className = 'arrow';
+ o.node.plus_img.innerHTML = arrowRight;
+
+ if (localStorageSupported()) {
+ var navSync = $('#nav-sync');
+ if (cachedLink()) {
+ showSyncOff(navSync,relpath);
+ navSync.removeClass('sync');
+ } else {
+ showSyncOn(navSync,relpath);
+ }
+ navSync.click(function(){ toggleSyncButton(relpath); });
+ }
+
+ $(window).load(function(){
+ navTo(o,toroot,hashUrl(),relpath);
+ showRoot();
+ });
+
+ $(window).bind('hashchange', function(){
+ if (window.location.hash && window.location.hash.length>1){
+ var a;
+ if ($(location).attr('hash')){
+ var clslink=stripPath(pathName())+':'+hashValue();
+ a=$('.item a[class$="'+clslink.replace(/</g,'\\3c ')+'"]');
+ }
+ if (a==null || !$(a).parent().parent().hasClass('selected')){
+ $('.item').removeClass('selected');
+ $('.item').removeAttr('id');
+ }
+ var link=stripPath2(pathName());
+ navTo(o,link,hashUrl(),relpath);
+ } else if (!animationInProgress) {
+ $('#doc-content').scrollTop(0);
+ $('.item').removeClass('selected');
+ $('.item').removeAttr('id');
+ navTo(o,toroot,hashUrl(),relpath);
+ }
+ })
+}
+/* @license-end */
--- /dev/null
+/*
+@ @licstart The following is the entire license notice for the
+JavaScript code in this file.
+
+Copyright (C) 1997-2017 by Dimitri van Heesch
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+@licend The above is the entire license notice
+for the JavaScript code in this file
+*/
+var NAVTREE =
+[
+ [ "mi-malloc", "index.html", [
+ [ "Building", "build.html", null ],
+ [ "Using the library", "using.html", null ],
+ [ "Environment Options", "environment.html", null ],
+ [ "Overriding Malloc", "overrides.html", null ],
+ [ "Performance", "bench.html", null ],
+ [ "Modules", "modules.html", "modules" ],
+ [ "Data Structures", "annotated.html", [
+ [ "Data Structures", "annotated.html", "annotated_dup" ],
+ [ "Data Structure Index", "classes.html", null ],
+ [ "Data Fields", "functions.html", [
+ [ "All", "functions.html", null ],
+ [ "Variables", "functions_vars.html", null ]
+ ] ]
+ ] ]
+ ] ]
+];
+
+var NAVTREEINDEX =
+[
+"annotated.html"
+];
+
+var SYNCONMSG = 'click to disable panel synchronisation';
+var SYNCOFFMSG = 'click to enable panel synchronisation';
\ No newline at end of file
--- /dev/null
+var NAVTREEINDEX0 =
+{
+"annotated.html":[6,0],
+"bench.html":[4],
+"build.html":[0],
+"classes.html":[6,1],
+"environment.html":[2],
+"functions.html":[6,2,0],
+"functions_vars.html":[6,2,1],
+"group__aligned.html":[5,2],
+"group__aligned.html#ga08647c4593f3b2eef24a919a73eba3a3":[5,2,1],
+"group__aligned.html#ga0cadbcf5b89a7b6fb171bc8df8734819":[5,2,6],
+"group__aligned.html#ga4028d1cf4aa4c87c880747044a8322ae":[5,2,4],
+"group__aligned.html#ga53dddb4724042a90315b94bc268fb4c9":[5,2,0],
+"group__aligned.html#ga5850da130c936bd77db039dcfbc8295d":[5,2,3],
+"group__aligned.html#ga5f8c2353766db522565e642fafd8a3f8":[5,2,7],
+"group__aligned.html#ga68930196751fa2cca9e1fd0d71bade56":[5,2,2],
+"group__aligned.html#gaf66a9ae6c6f08bd6be6fb6ea771faffb":[5,2,5],
+"group__analysis.html":[5,6],
+"group__analysis.html#a332a6c14d736a99699d5453a1cb04b41":[5,6,0,0],
+"group__analysis.html#ab47526df656d8837ec3e97f11b83f835":[5,6,0,2],
+"group__analysis.html#ab820302c5cd0df133eb8e51650a008b4":[5,6,0,4],
+"group__analysis.html#ae0085e6e1cf059a4eb7767e30e9991b8":[5,6,0,1],
+"group__analysis.html#ae848a3e6840414891035423948ca0383":[5,6,0,3],
+"group__analysis.html#ga0d67c1789faaa15ff366c024fcaf6377":[5,6,3],
+"group__analysis.html#ga628c237489c2679af84a4d0d143b3dd5":[5,6,2],
+"group__analysis.html#ga70c46687dc6e9dc98b232b02646f8bed":[5,6,5],
+"group__analysis.html#gaa862aa8ed8d57d84cae41fc1022d71af":[5,6,4],
+"group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65":[5,6,1],
+"group__analysis.html#structmi__heap__area__t":[5,6,0],
+"group__cpp.html":[5,9],
+"group__cpp.html#ga756f4b2bc6a7ecd0a90baea8e90c7907":[5,9,7],
+"group__cpp.html#gaab78a32f55149e9fbf432d5288e38e1e":[5,9,6],
+"group__cpp.html#gaad048a9fce3d02c5909cd05c6ec24545":[5,9,1],
+"group__cpp.html#gab5e29558926d934c3f1cae8c815f942c":[5,9,3],
+"group__cpp.html#gae7bc4f56cd57ed3359060ff4f38bda81":[5,9,4],
+"group__cpp.html#gaeaded64eda71ed6b1d569d3e723abc4a":[5,9,5],
+"group__cpp.html#gaef2c2bdb4f70857902d3c8903ac095f3":[5,9,2],
+"group__cpp.html#structmi__stl__allocator":[5,9,0],
+"group__extended.html":[5,1],
+"group__extended.html#ga089c859d9eddc5f9b4bd946cd53cebee":[5,1,21],
+"group__extended.html#ga0ae4581e85453456a0d658b2b98bf7bf":[5,1,18],
+"group__extended.html#ga1ea64283508718d9d645c38efc2f4305":[5,1,0],
+"group__extended.html#ga220f29f40a44404b0061c15bc1c31152":[5,1,22],
+"group__extended.html#ga251d369cda3f1c2a955c555486ed90e5":[5,1,2],
+"group__extended.html#ga299dae78d25ce112e384a98b7309c5be":[5,1,1],
+"group__extended.html#ga2d126e5c62d3badc35445e5d84166df2":[5,1,15],
+"group__extended.html#ga3132f521fb756fc0e8ec0b74fb58df50":[5,1,13],
+"group__extended.html#ga3460a6ca91af97be4058f523d3cb8ece":[5,1,9],
+"group__extended.html#ga3bb8468b8cfcc6e2a61d98aee85c5f99":[5,1,17],
+"group__extended.html#ga421430e2226d7d468529cec457396756":[5,1,4],
+"group__extended.html#ga537f13b299ddf801e49a5a94fde02c79":[5,1,16],
+"group__extended.html#ga5f071b10d4df1c3658e04e7fd67a94e6":[5,1,6],
+"group__extended.html#ga7136c2e55cb22c98ecf95d08d6debb99":[5,1,8],
+"group__extended.html#ga7795a13d20087447281858d2c771cca1":[5,1,12],
+"group__extended.html#ga854b1de8cb067c7316286c28b2fcd3d1":[5,1,14],
+"group__extended.html#gaa1d55e0e894be240827e5d87ec3a1f45":[5,1,10],
+"group__extended.html#gaad25050b19f30cd79397b227e0157a3f":[5,1,7],
+"group__extended.html#gab1dac8476c46cb9eecab767eb40c1525":[5,1,20],
+"group__extended.html#gac057927cd06c854b45fe7847e921bd47":[5,1,5],
+"group__extended.html#gad823d23444a4b77a40f66bf075a98a0c":[5,1,3],
+"group__extended.html#gae5b17ff027cd2150b43a33040250cf3f":[5,1,11],
+"group__extended.html#gaf8e73efc2cbca9ebfdfb166983a04c17":[5,1,19],
+"group__heap.html":[5,3],
+"group__heap.html#ga00e95ba1e01acac3cfd95bb7a357a6f0":[5,3,20],
+"group__heap.html#ga08ca6419a5c057a4d965868998eef487":[5,3,3],
+"group__heap.html#ga139d6b09dbf50c3c2523d0f4d1cfdeb5":[5,3,22],
+"group__heap.html#ga23acd7680fb0976dde3783254c6c874b":[5,3,11],
+"group__heap.html#ga2ab1af8d438819b55319c7ef51d1e409":[5,3,5],
+"group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2":[5,3,0],
+"group__heap.html#ga45fb43a62776fbebbdf1edd99b527954":[5,3,26],
+"group__heap.html#ga4a21070eb4e7cce018133c8d5f4b0527":[5,3,18],
+"group__heap.html#ga4af03a6e2b93fae77424d93f889705c3":[5,3,2],
+"group__heap.html#ga5d03fbe062ffcf38f0f417fd968357fc":[5,3,7],
+"group__heap.html#ga766f672ba56f2fbfeb9d9dbb0b7f6b11":[5,3,14],
+"group__heap.html#ga7922f7495cde30b1984d0e6072419298":[5,3,4],
+"group__heap.html#ga851da6c43fe0b71c1376cee8aef90db0":[5,3,13],
+"group__heap.html#ga8db4cbb87314a989a9a187464d6b5e05":[5,3,8],
+"group__heap.html#ga8e3dbd46650dd26573cf307a2c8f1f5a":[5,3,23],
+"group__heap.html#ga903104592c8ed53417a3762da6241133":[5,3,24],
+"group__heap.html#ga9cbed01e42c0647907295de92c3fa296":[5,3,9],
+"group__heap.html#ga9f9c0844edb9717f4feacd79116b8e0d":[5,3,6],
+"group__heap.html#gaa1a1c7a1f4da6826b5a25b70ef878368":[5,3,12],
+"group__heap.html#gaa450a59c6c7ae5fdbd1c2b80a8329ef0":[5,3,25],
+"group__heap.html#gaa6702b3c48e9e53e50e81b36f5011d55":[5,3,1],
+"group__heap.html#gaaef3395f66be48f37bdc8322509c5d81":[5,3,15],
+"group__heap.html#gab5b87e1805306f70df38789fcfcf6653":[5,3,10],
+"group__heap.html#gab8631ec88c8d26641b68b5d25dcd4422":[5,3,21],
+"group__heap.html#gac74e94ad9b0c9b57c1c4d88b8825b7a8":[5,3,19],
+"group__heap.html#gaf96c788a1bf553fe2d371de9365e047c":[5,3,17],
+"group__heap.html#gafc603b696bd14cae6da28658f950d98c":[5,3,16],
+"group__malloc.html":[5,0],
+"group__malloc.html#ga08cec32dd5bbe7da91c78d19f1b5bebe":[5,0,8],
+"group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6":[5,0,4],
+"group__malloc.html#ga23a0fbb452b5dce8e31fab1a1958cacc":[5,0,9],
+"group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a":[5,0,3],
+"group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853":[5,0,7],
+"group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d":[5,0,0],
+"group__malloc.html#gaaabf971c2571891433477e2d21a35266":[5,0,11],
+"group__malloc.html#gaaee66a1d483c3e28f585525fb96707e4":[5,0,1],
+"group__malloc.html#gac7cffe13f1f458ed16789488bf92b9b2":[5,0,10],
+"group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6":[5,0,5],
+"group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95":[5,0,2],
+"group__malloc.html#gafdd9d8bb2986e668ba9884f28af38000":[5,0,12],
+"group__malloc.html#gafe68ac7c5e24a65cd55c9d6b152211a0":[5,0,6],
+"group__options.html":[5,7],
+"group__options.html#ga04180ae41b0d601421dd62ced40ca050":[5,7,2],
+"group__options.html#ga459ad98f18b3fc9275474807fe0ca188":[5,7,4],
+"group__options.html#ga65518b69ec5d32336b50e07f74b3f629":[5,7,8],
+"group__options.html#ga7e8af195cc81d3fa64ccf2662caa565a":[5,7,3],
+"group__options.html#ga7ef623e440e6e5545cb08c94e71e4b90":[5,7,6],
+"group__options.html#ga9a13d05fcb77489cb06d4d017ebd8bed":[5,7,7],
+"group__options.html#gaebf6ff707a2e688ebb1a2296ca564054":[5,7,1],
+"group__options.html#gaf84921c32375e25754dc2ee6a911fa60":[5,7,5],
+"group__options.html#gafebf7ed116adb38ae5218bc3ce06884c":[5,7,0],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda":[5,7,0,1],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74":[5,7,0,11],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5":[5,7,0,10],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c":[5,7,0,13],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b":[5,7,0,3],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1":[5,7,0,7],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad":[5,7,0,4],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e":[5,7,0,5],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf":[5,7,0,14],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a":[5,7,0,15],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777":[5,7,0,2],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536":[5,7,0,12],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2":[5,7,0,6],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968":[5,7,0,8],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d":[5,7,0,9],
+"group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0":[5,7,0,0],
+"group__posix.html":[5,8],
+"group__posix.html#ga06d07cf357bbac5c73ba5d0c0c421e17":[5,8,7],
+"group__posix.html#ga0d28d5cf61e6bfbb18c63092939fe5c9":[5,8,3],
+"group__posix.html#ga1326d2e4388630b5f81ca7206318b8e5":[5,8,1],
+"group__posix.html#ga4531c9e775bb3ae12db57c1ba8a5d7de":[5,8,6],
+"group__posix.html#ga48fad8648a2f1dab9c87ea9448a52088":[5,8,11],
+"group__posix.html#ga705dc7a64bffacfeeb0141501a5c35d7":[5,8,2],
+"group__posix.html#ga72e9d7ffb5fe94d69bc722c8506e27bc":[5,8,5],
+"group__posix.html#ga73baaf5951f5165ba0763d0c06b6a93b":[5,8,12],
+"group__posix.html#gaab7fa71ea93b96873f5d9883db57d40e":[5,8,8],
+"group__posix.html#gacff84f226ba9feb2031b8992e5579447":[5,8,9],
+"group__posix.html#gad5a69c8fea96aa2b7a7c818c2130090a":[5,8,0],
+"group__posix.html#gae01389eedab8d67341ff52e2aad80ebb":[5,8,4],
+"group__posix.html#gaeb325c39b887d3b90d85d1eb1712fb1e":[5,8,10],
+"group__typed.html":[5,5],
+"group__typed.html#ga0619a62c5fd886f1016030abe91f0557":[5,5,7],
+"group__typed.html#ga1158b49a55dfa81f58a4426a7578f523":[5,5,9],
+"group__typed.html#ga3e50a1600958fcaf1a7f3560c9174f9e":[5,5,5],
+"group__typed.html#ga4e5d1f1707c90e5f55e023ac5f45fe74":[5,5,1],
+"group__typed.html#ga653bcb24ac495bc19940ecd6898f9cd7":[5,5,2],
+"group__typed.html#ga6b75cb9c4b9c647661d0924552dc6e83":[5,5,3],
+"group__typed.html#gac77a61bdaf680a803785fe307820b48c":[5,5,10],
+"group__typed.html#gad6e87e86e994aa14416ae9b5d4c188fe":[5,5,6],
+"group__typed.html#gae5cb6e0fafc9f23169c5622e077afe8b":[5,5,8],
+"group__typed.html#gae80c47c9d4cab10961fff1a8ac98fc07":[5,5,0],
+"group__typed.html#gaf213d5422ec35e7f6caad827c79bc948":[5,5,4],
+"group__zeroinit.html":[5,4],
+"group__zeroinit.html#ga375fa8a611c51905e592d5d467c49664":[5,4,4],
+"group__zeroinit.html#ga3e7e5c291acf1c7fd7ffd9914a9f945f":[5,4,6],
+"group__zeroinit.html#ga496452c96f1de8c500be9fddf52edaf7":[5,4,2],
+"group__zeroinit.html#ga4ff5e92ad73585418a072c9d059e5cf9":[5,4,7],
+"group__zeroinit.html#ga8648c5fbb22a80f0262859099f06dfbd":[5,4,0],
+"group__zeroinit.html#ga8c292e142110229a2980b37ab036dbc6":[5,4,8],
+"group__zeroinit.html#ga9f3f999396c8f77ca5e80e7b40ac29e3":[5,4,1],
+"group__zeroinit.html#gac90da54fa7e5d10bdc97ce0b51dce2eb":[5,4,5],
+"group__zeroinit.html#gacd71a7bce96aab38ae6de17af2eb2cf0":[5,4,9],
+"group__zeroinit.html#gacfad83f14eb5d6a42a497a898e19fc76":[5,4,3],
+"group__zeroinit.html#gae8b358c417e61d5307da002702b0a8e1":[5,4,10],
+"index.html":[],
+"modules.html":[5],
+"overrides.html":[3],
+"pages.html":[],
+"using.html":[1]
+};
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Overriding Malloc</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('overrides.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="PageDoc"><div class="header">
+ <div class="headertitle">
+<div class="title">Overriding Malloc </div> </div>
+</div><!--header-->
+<div class="contents">
+<div class="textblock"><p>Overriding the standard <code>malloc</code> can be done either <em>dynamically</em> or <em>statically</em>.</p>
+<h2>Dynamic override</h2>
+<p>This is the recommended way to override the standard malloc interface.</p>
+<h3>Linux, BSD</h3>
+<p>On these systems we preload the mimalloc shared library so all calls to the standard <code>malloc</code> interface are resolved to the <em>mimalloc</em> library.</p>
+<ul>
+<li><code>env LD_PRELOAD=/usr/lib/libmimalloc.so myprogram</code></li>
+</ul>
+<p>You can set extra environment variables to check that mimalloc is running, like: </p><div class="fragment"><div class="line">env MIMALLOC_VERBOSE=1 LD_PRELOAD=/usr/lib/libmimalloc.so myprogram</div></div><!-- fragment --><p> or run with the debug version to get detailed statistics: </p><div class="fragment"><div class="line">env MIMALLOC_SHOW_STATS=1 LD_PRELOAD=/usr/lib/libmimalloc-debug.so myprogram</div></div><!-- fragment --><h3>MacOS</h3>
+<p>On macOS we can also preload the mimalloc shared library so all calls to the standard <code>malloc</code> interface are resolved to the <em>mimalloc</em> library.</p>
+<ul>
+<li><code>env DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=/usr/lib/libmimalloc.dylib myprogram</code></li>
+</ul>
+<p>Note that certain security restrictions may apply when doing this from the <a href="https://stackoverflow.com/questions/43941322/dyld-insert-libraries-ignored-when-calling-application-through-bash">shell</a>.</p>
+<p>(Note: macOS support for dynamic overriding is recent, please report any issues.)</p>
+<h3>Windows</h3>
+<p>Overriding on Windows is robust and has the particular advantage to be able to redirect all malloc/free calls that go through the (dynamic) C runtime allocator, including those from other DLL's or libraries.</p>
+<p>The overriding on Windows requires that you link your program explicitly with the mimalloc DLL and use the C-runtime library as a DLL (using the <code>/MD</code> or <code>/MDd</code> switch). Also, the <code>mimalloc-redirect.dll</code> (or <code>mimalloc-redirect32.dll</code>) must be available in the same folder as the main <code>mimalloc-override.dll</code> at runtime (as it is a dependency). The redirection DLL ensures that all calls to the C runtime malloc API get redirected to mimalloc (in <code>mimalloc-override.dll</code>).</p>
+<p>To ensure the mimalloc DLL is loaded at run-time it is easiest to insert some call to the mimalloc API in the <code>main</code> function, like <code>mi_version()</code> (or use the <code>/INCLUDE:mi_version</code> switch on the linker). See the <code>mimalloc-override-test</code> project for an example on how to use this. For best performance on Windows with C++, it is also recommended to also override the <code>new</code>/<code>delete</code> operations (by including <a href="https://github.com/microsoft/mimalloc/blob/master/include/mimalloc-new-delete.h"><code>mimalloc-new-delete.h</code></a> a single(!) source file in your project).</p>
+<p>The environment variable <code>MIMALLOC_DISABLE_REDIRECT=1</code> can be used to disable dynamic overriding at run-time. Use <code>MIMALLOC_VERBOSE=1</code> to check if mimalloc was successfully redirected.</p>
+<p>(Note: in principle, it is possible to even patch existing executables without any recompilation if they are linked with the dynamic C runtime (<code>ucrtbase.dll</code>) – just put the <code>mimalloc-override.dll</code> into the import table (and put <code>mimalloc-redirect.dll</code> in the same folder) Such patching can be done for example with <a href="https://ntcore.com/?page_id=388">CFF Explorer</a>).</p>
+<h2>Static override</h2>
+<p>On Unix systems, you can also statically link with <em>mimalloc</em> to override the standard malloc interface. The recommended way is to link the final program with the <em>mimalloc</em> single object file (<code>mimalloc-override.o</code>). We use an object file instead of a library file as linkers give preference to that over archives to resolve symbols. To ensure that the standard malloc interface resolves to the <em>mimalloc</em> library, link it as the first object file. For example:</p>
+<div class="fragment"><div class="line">gcc -o myprogram mimalloc-<span class="keyword">override</span>.o myfile1.c ...</div></div><!-- fragment --><h2>List of Overrides:</h2>
+<p>The specific functions that get redirected to the <em>mimalloc</em> library are:</p>
+<div class="fragment"><div class="line"><span class="comment">// C</span></div><div class="line"><span class="keywordtype">void</span>* malloc(<span class="keywordtype">size_t</span> size);</div><div class="line"><span class="keywordtype">void</span>* calloc(<span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> n);</div><div class="line"><span class="keywordtype">void</span>* realloc(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><span class="keywordtype">void</span> free(<span class="keywordtype">void</span>* p);</div><div class="line"></div><div class="line"><span class="comment">// C++</span></div><div class="line"><span class="keywordtype">void</span> <span class="keyword">operator</span> <span class="keyword">delete</span>(<span class="keywordtype">void</span>* p);</div><div class="line"><span class="keywordtype">void</span> <span class="keyword">operator</span> <span class="keyword">delete</span>[](<span class="keywordtype">void</span>* p);</div><div class="line"></div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span>(std::size_t n) noexcept(<span class="keyword">false</span>);</div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span>[](std::size_t n) noexcept(<span class="keyword">false</span>);</div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span>( std::size_t n, std::align_val_t align) noexcept(<span class="keyword">false</span>);</div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span>[]( std::size_t n, std::align_val_t align) noexcept(<span class="keyword">false</span>);</div><div class="line"></div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span> ( std::size_t count, <span class="keyword">const</span> std::nothrow_t& tag);</div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span>[]( std::size_t count, <span class="keyword">const</span> std::nothrow_t& tag);</div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span> ( std::size_t count, std::align_val_t al, <span class="keyword">const</span> std::nothrow_t&);</div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span>[]( std::size_t count, std::align_val_t al, <span class="keyword">const</span> std::nothrow_t&);</div><div class="line"></div><div class="line"><span class="comment">// Posix</span></div><div class="line"><span class="keywordtype">int</span> posix_memalign(<span class="keywordtype">void</span>** p, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> size);</div><div class="line"></div><div class="line"><span class="comment">// Linux</span></div><div class="line"><span class="keywordtype">void</span>* memalign(<span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> size);</div><div class="line"><span class="keywordtype">void</span>* aligned_alloc(<span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> size);</div><div class="line"><span class="keywordtype">void</span>* valloc(<span class="keywordtype">size_t</span> size);</div><div class="line"><span class="keywordtype">void</span>* pvalloc(<span class="keywordtype">size_t</span> size);</div><div class="line"><span class="keywordtype">size_t</span> malloc_usable_size(<span class="keywordtype">void</span> *p);</div><div class="line"></div><div class="line"><span class="comment">// BSD</span></div><div class="line"><span class="keywordtype">void</span>* reallocarray( <span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size );</div><div class="line"><span class="keywordtype">void</span>* reallocf(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><span class="keywordtype">void</span> cfree(<span class="keywordtype">void</span>* p);</div><div class="line"></div><div class="line"><span class="comment">// Windows</span></div><div class="line"><span class="keywordtype">void</span>* _expand(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><span class="keywordtype">size_t</span> _msize(<span class="keywordtype">void</span>* p);</div><div class="line"></div><div class="line"><span class="keywordtype">void</span>* _malloc_dbg(<span class="keywordtype">size_t</span> size, <span class="keywordtype">int</span> block_type, <span class="keyword">const</span> <span class="keywordtype">char</span>* fname, <span class="keywordtype">int</span> line);</div><div class="line"><span class="keywordtype">void</span>* _realloc_dbg(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize, <span class="keywordtype">int</span> block_type, <span class="keyword">const</span> <span class="keywordtype">char</span>* fname, <span class="keywordtype">int</span> line);</div><div class="line"><span class="keywordtype">void</span>* _calloc_dbg(<span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size, <span class="keywordtype">int</span> block_type, <span class="keyword">const</span> <span class="keywordtype">char</span>* fname, <span class="keywordtype">int</span> line);</div><div class="line"><span class="keywordtype">void</span>* _expand_dbg(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> size, <span class="keywordtype">int</span> block_type, <span class="keyword">const</span> <span class="keywordtype">char</span>* fname, <span class="keywordtype">int</span> line);</div><div class="line"><span class="keywordtype">size_t</span> _msize_dbg(<span class="keywordtype">void</span>* p, <span class="keywordtype">int</span> block_type);</div><div class="line"><span class="keywordtype">void</span> _free_dbg(<span class="keywordtype">void</span>* p, <span class="keywordtype">int</span> block_type);</div></div><!-- fragment --> </div></div><!-- PageDoc -->
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Related Pages</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('pages.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="header">
+ <div class="headertitle">
+<div class="title">Related Pages</div> </div>
+</div><!--header-->
+<div class="contents">
+<div class="textblock">Here is a list of all related documentation pages:</div><div class="directory">
+<table class="directory">
+<tr id="row_0_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="build.html" target="_self">Building</a></td><td class="desc"></td></tr>
+<tr id="row_1_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="using.html" target="_self">Using the library</a></td><td class="desc"></td></tr>
+<tr id="row_2_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="environment.html" target="_self">Environment Options</a></td><td class="desc"></td></tr>
+<tr id="row_3_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="overrides.html" target="_self">Overriding Malloc</a></td><td class="desc"></td></tr>
+<tr id="row_4_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="bench.html" target="_self">Performance</a></td><td class="desc"></td></tr>
+</table>
+</div><!-- directory -->
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+/*
+ @licstart The following is the entire license notice for the
+ JavaScript code in this file.
+
+ Copyright (C) 1997-2017 by Dimitri van Heesch
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ @licend The above is the entire license notice
+ for the JavaScript code in this file
+ */
+function initResizable()
+{
+ var cookie_namespace = 'doxygen';
+ var sidenav,navtree,content,header,collapsed,collapsedWidth=0,barWidth=6,desktop_vp=768,titleHeight;
+
+ function readCookie(cookie)
+ {
+ var myCookie = cookie_namespace+"_"+cookie+"=";
+ if (document.cookie) {
+ var index = document.cookie.indexOf(myCookie);
+ if (index != -1) {
+ var valStart = index + myCookie.length;
+ var valEnd = document.cookie.indexOf(";", valStart);
+ if (valEnd == -1) {
+ valEnd = document.cookie.length;
+ }
+ var val = document.cookie.substring(valStart, valEnd);
+ return val;
+ }
+ }
+ return 0;
+ }
+
+ function writeCookie(cookie, val, expiration)
+ {
+ if (val==undefined) return;
+ if (expiration == null) {
+ var date = new Date();
+ date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
+ expiration = date.toGMTString();
+ }
+ document.cookie = cookie_namespace + "_" + cookie + "=" + val + "; expires=" + expiration+"; path=/";
+ }
+
+ function resizeWidth()
+ {
+ var windowWidth = $(window).width() + "px";
+ var sidenavWidth = $(sidenav).outerWidth();
+ content.css({marginLeft:parseInt(sidenavWidth)+"px"});
+ writeCookie('width',sidenavWidth-barWidth, null);
+ }
+
+ function restoreWidth(navWidth)
+ {
+ var windowWidth = $(window).width() + "px";
+ content.css({marginLeft:parseInt(navWidth)+barWidth+"px"});
+ sidenav.css({width:navWidth + "px"});
+ }
+
+ function resizeHeight()
+ {
+ var headerHeight = header.outerHeight();
+ var footerHeight = footer.outerHeight();
+ var windowHeight = $(window).height() - headerHeight - footerHeight;
+ content.css({height:windowHeight + "px"});
+ navtree.css({height:windowHeight + "px"});
+ sidenav.css({height:windowHeight + "px"});
+ var width=$(window).width();
+ if (width!=collapsedWidth) {
+ if (width<desktop_vp && collapsedWidth>=desktop_vp) {
+ if (!collapsed) {
+ collapseExpand();
+ }
+ } else if (width>desktop_vp && collapsedWidth<desktop_vp) {
+ if (collapsed) {
+ collapseExpand();
+ }
+ }
+ collapsedWidth=width;
+ }
+ }
+
+ function collapseExpand()
+ {
+ if (sidenav.width()>0) {
+ restoreWidth(0);
+ collapsed=true;
+ }
+ else {
+ var width = readCookie('width');
+ if (width>200 && width<$(window).width()) { restoreWidth(width); } else { restoreWidth(200); }
+ collapsed=false;
+ }
+ }
+
+ header = $("#top");
+ sidenav = $("#side-nav");
+ content = $("#doc-content");
+ navtree = $("#nav-tree");
+ footer = $("#nav-path");
+ $(".side-nav-resizable").resizable({resize: function(e, ui) { resizeWidth(); } });
+ $(sidenav).resizable({ minWidth: 0 });
+ $(window).resize(function() { resizeHeight(); });
+ var device = navigator.userAgent.toLowerCase();
+ var touch_device = device.match(/(iphone|ipod|ipad|android)/);
+ if (touch_device) { /* wider split bar for touch only devices */
+ $(sidenav).css({ paddingRight:'20px' });
+ $('.ui-resizable-e').css({ width:'20px' });
+ $('#nav-sync').css({ right:'34px' });
+ barWidth=20;
+ }
+ var width = readCookie('width');
+ if (width) { restoreWidth(width); } else { resizeWidth(); }
+ resizeHeight();
+ var url = location.href;
+ var i=url.indexOf("#");
+ if (i>=0) window.location.hash=url.substr(i);
+ var _preventDefault = function(evt) { evt.preventDefault(); };
+ $("#splitbar").bind("dragstart", _preventDefault).bind("selectstart", _preventDefault);
+ $(".ui-resizable-handle").dblclick(collapseExpand);
+ $(window).load(resizeHeight);
+}
+/* @license-end */
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_0.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['_5fmi_5foption_5flast',['_mi_option_last',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a',1,'mimalloc-doc.h']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_1.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['aligned_20allocation',['Aligned Allocation',['../group__aligned.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_2.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['block_5fsize',['block_size',['../group__analysis.html#a332a6c14d736a99699d5453a1cb04b41',1,'mi_heap_area_t']]],
+ ['blocks',['blocks',['../group__analysis.html#ae0085e6e1cf059a4eb7767e30e9991b8',1,'mi_heap_area_t']]],
+ ['building',['Building',['../build.html',1,'']]],
+ ['basic_20allocation',['Basic Allocation',['../group__malloc.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_3.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['committed',['committed',['../group__analysis.html#ab47526df656d8837ec3e97f11b83f835',1,'mi_heap_area_t']]],
+ ['c_2b_2b_20wrappers',['C++ wrappers',['../group__cpp.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_4.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['environment_20options',['Environment Options',['../environment.html',1,'']]],
+ ['extended_20functions',['Extended Functions',['../group__extended.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_5.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['heap_20introspection',['Heap Introspection',['../group__analysis.html',1,'']]],
+ ['heap_20allocation',['Heap Allocation',['../group__heap.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_6.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['mi_5f_5fposix_5fmemalign',['mi__posix_memalign',['../group__posix.html#gad5a69c8fea96aa2b7a7c818c2130090a',1,'mimalloc-doc.h']]],
+ ['mi_5faligned_5falloc',['mi_aligned_alloc',['../group__posix.html#ga1326d2e4388630b5f81ca7206318b8e5',1,'mimalloc-doc.h']]],
+ ['mi_5fblock_5fvisit_5ffun',['mi_block_visit_fun',['../group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65',1,'mimalloc-doc.h']]],
+ ['mi_5fcalloc',['mi_calloc',['../group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d',1,'mimalloc-doc.h']]],
+ ['mi_5fcalloc_5faligned',['mi_calloc_aligned',['../group__aligned.html#ga53dddb4724042a90315b94bc268fb4c9',1,'mimalloc-doc.h']]],
+ ['mi_5fcalloc_5faligned_5fat',['mi_calloc_aligned_at',['../group__aligned.html#ga08647c4593f3b2eef24a919a73eba3a3',1,'mimalloc-doc.h']]],
+ ['mi_5fcalloc_5ftp',['mi_calloc_tp',['../group__typed.html#gae80c47c9d4cab10961fff1a8ac98fc07',1,'mimalloc-doc.h']]],
+ ['mi_5fcfree',['mi_cfree',['../group__posix.html#ga705dc7a64bffacfeeb0141501a5c35d7',1,'mimalloc-doc.h']]],
+ ['mi_5fcheck_5fowned',['mi_check_owned',['../group__analysis.html#ga628c237489c2679af84a4d0d143b3dd5',1,'mimalloc-doc.h']]],
+ ['mi_5fcollect',['mi_collect',['../group__extended.html#ga421430e2226d7d468529cec457396756',1,'mimalloc-doc.h']]],
+ ['mi_5fdeferred_5ffree_5ffun',['mi_deferred_free_fun',['../group__extended.html#ga299dae78d25ce112e384a98b7309c5be',1,'mimalloc-doc.h']]],
+ ['mi_5ferror_5ffun',['mi_error_fun',['../group__extended.html#ga251d369cda3f1c2a955c555486ed90e5',1,'mimalloc-doc.h']]],
+ ['mi_5fexpand',['mi_expand',['../group__malloc.html#gaaee66a1d483c3e28f585525fb96707e4',1,'mimalloc-doc.h']]],
+ ['mi_5ffree',['mi_free',['../group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95',1,'mimalloc-doc.h']]],
+ ['mi_5ffree_5faligned',['mi_free_aligned',['../group__posix.html#ga0d28d5cf61e6bfbb18c63092939fe5c9',1,'mimalloc-doc.h']]],
+ ['mi_5ffree_5fsize',['mi_free_size',['../group__posix.html#gae01389eedab8d67341ff52e2aad80ebb',1,'mimalloc-doc.h']]],
+ ['mi_5ffree_5fsize_5faligned',['mi_free_size_aligned',['../group__posix.html#ga72e9d7ffb5fe94d69bc722c8506e27bc',1,'mimalloc-doc.h']]],
+ ['mi_5fgood_5fsize',['mi_good_size',['../group__extended.html#gac057927cd06c854b45fe7847e921bd47',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5farea_5ft',['mi_heap_area_t',['../group__analysis.html#structmi__heap__area__t',1,'']]],
+ ['mi_5fheap_5fcalloc',['mi_heap_calloc',['../group__heap.html#gaa6702b3c48e9e53e50e81b36f5011d55',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fcalloc_5faligned',['mi_heap_calloc_aligned',['../group__heap.html#ga4af03a6e2b93fae77424d93f889705c3',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fcalloc_5faligned_5fat',['mi_heap_calloc_aligned_at',['../group__heap.html#ga08ca6419a5c057a4d965868998eef487',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fcalloc_5ftp',['mi_heap_calloc_tp',['../group__typed.html#ga4e5d1f1707c90e5f55e023ac5f45fe74',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fcheck_5fowned',['mi_heap_check_owned',['../group__analysis.html#ga0d67c1789faaa15ff366c024fcaf6377',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fcollect',['mi_heap_collect',['../group__heap.html#ga7922f7495cde30b1984d0e6072419298',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fcontains_5fblock',['mi_heap_contains_block',['../group__analysis.html#gaa862aa8ed8d57d84cae41fc1022d71af',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fdelete',['mi_heap_delete',['../group__heap.html#ga2ab1af8d438819b55319c7ef51d1e409',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fdestroy',['mi_heap_destroy',['../group__heap.html#ga9f9c0844edb9717f4feacd79116b8e0d',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fget_5fbacking',['mi_heap_get_backing',['../group__heap.html#ga5d03fbe062ffcf38f0f417fd968357fc',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fget_5fdefault',['mi_heap_get_default',['../group__heap.html#ga8db4cbb87314a989a9a187464d6b5e05',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fmalloc',['mi_heap_malloc',['../group__heap.html#ga9cbed01e42c0647907295de92c3fa296',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fmalloc_5faligned',['mi_heap_malloc_aligned',['../group__heap.html#gab5b87e1805306f70df38789fcfcf6653',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fmalloc_5faligned_5fat',['mi_heap_malloc_aligned_at',['../group__heap.html#ga23acd7680fb0976dde3783254c6c874b',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fmalloc_5fsmall',['mi_heap_malloc_small',['../group__heap.html#gaa1a1c7a1f4da6826b5a25b70ef878368',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fmalloc_5ftp',['mi_heap_malloc_tp',['../group__typed.html#ga653bcb24ac495bc19940ecd6898f9cd7',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fmallocn',['mi_heap_mallocn',['../group__heap.html#ga851da6c43fe0b71c1376cee8aef90db0',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fmallocn_5ftp',['mi_heap_mallocn_tp',['../group__typed.html#ga6b75cb9c4b9c647661d0924552dc6e83',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fnew',['mi_heap_new',['../group__heap.html#ga766f672ba56f2fbfeb9d9dbb0b7f6b11',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frealloc',['mi_heap_realloc',['../group__heap.html#gaaef3395f66be48f37bdc8322509c5d81',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frealloc_5faligned',['mi_heap_realloc_aligned',['../group__heap.html#gafc603b696bd14cae6da28658f950d98c',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frealloc_5faligned_5fat',['mi_heap_realloc_aligned_at',['../group__heap.html#gaf96c788a1bf553fe2d371de9365e047c',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5freallocf',['mi_heap_reallocf',['../group__heap.html#ga4a21070eb4e7cce018133c8d5f4b0527',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5freallocn',['mi_heap_reallocn',['../group__heap.html#gac74e94ad9b0c9b57c1c4d88b8825b7a8',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5freallocn_5ftp',['mi_heap_reallocn_tp',['../group__typed.html#gaf213d5422ec35e7f6caad827c79bc948',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frealpath',['mi_heap_realpath',['../group__heap.html#ga00e95ba1e01acac3cfd95bb7a357a6f0',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frecalloc',['mi_heap_recalloc',['../group__zeroinit.html#ga8648c5fbb22a80f0262859099f06dfbd',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frecalloc_5faligned',['mi_heap_recalloc_aligned',['../group__zeroinit.html#ga9f3f999396c8f77ca5e80e7b40ac29e3',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frecalloc_5faligned_5fat',['mi_heap_recalloc_aligned_at',['../group__zeroinit.html#ga496452c96f1de8c500be9fddf52edaf7',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frecalloc_5ftp',['mi_heap_recalloc_tp',['../group__typed.html#ga3e50a1600958fcaf1a7f3560c9174f9e',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frezalloc',['mi_heap_rezalloc',['../group__zeroinit.html#gacfad83f14eb5d6a42a497a898e19fc76',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frezalloc_5faligned',['mi_heap_rezalloc_aligned',['../group__zeroinit.html#ga375fa8a611c51905e592d5d467c49664',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frezalloc_5faligned_5fat',['mi_heap_rezalloc_aligned_at',['../group__zeroinit.html#gac90da54fa7e5d10bdc97ce0b51dce2eb',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fset_5fdefault',['mi_heap_set_default',['../group__heap.html#gab8631ec88c8d26641b68b5d25dcd4422',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fstrdup',['mi_heap_strdup',['../group__heap.html#ga139d6b09dbf50c3c2523d0f4d1cfdeb5',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fstrndup',['mi_heap_strndup',['../group__heap.html#ga8e3dbd46650dd26573cf307a2c8f1f5a',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5ft',['mi_heap_t',['../group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fvisit_5fblocks',['mi_heap_visit_blocks',['../group__analysis.html#ga70c46687dc6e9dc98b232b02646f8bed',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fzalloc',['mi_heap_zalloc',['../group__heap.html#ga903104592c8ed53417a3762da6241133',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fzalloc_5faligned',['mi_heap_zalloc_aligned',['../group__heap.html#gaa450a59c6c7ae5fdbd1c2b80a8329ef0',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fzalloc_5faligned_5fat',['mi_heap_zalloc_aligned_at',['../group__heap.html#ga45fb43a62776fbebbdf1edd99b527954',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fzalloc_5ftp',['mi_heap_zalloc_tp',['../group__typed.html#gad6e87e86e994aa14416ae9b5d4c188fe',1,'mimalloc-doc.h']]],
+ ['mi_5fis_5fin_5fheap_5fregion',['mi_is_in_heap_region',['../group__extended.html#ga5f071b10d4df1c3658e04e7fd67a94e6',1,'mimalloc-doc.h']]],
+ ['mi_5fis_5fredirected',['mi_is_redirected',['../group__extended.html#gaad25050b19f30cd79397b227e0157a3f',1,'mimalloc-doc.h']]],
+ ['mi_5fmalloc',['mi_malloc',['../group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a',1,'mimalloc-doc.h']]],
+ ['mi_5fmalloc_5faligned',['mi_malloc_aligned',['../group__aligned.html#ga68930196751fa2cca9e1fd0d71bade56',1,'mimalloc-doc.h']]],
+ ['mi_5fmalloc_5faligned_5fat',['mi_malloc_aligned_at',['../group__aligned.html#ga5850da130c936bd77db039dcfbc8295d',1,'mimalloc-doc.h']]],
+ ['mi_5fmalloc_5fsize',['mi_malloc_size',['../group__posix.html#ga4531c9e775bb3ae12db57c1ba8a5d7de',1,'mimalloc-doc.h']]],
+ ['mi_5fmalloc_5fsmall',['mi_malloc_small',['../group__extended.html#ga7136c2e55cb22c98ecf95d08d6debb99',1,'mimalloc-doc.h']]],
+ ['mi_5fmalloc_5ftp',['mi_malloc_tp',['../group__typed.html#ga0619a62c5fd886f1016030abe91f0557',1,'mimalloc-doc.h']]],
+ ['mi_5fmalloc_5fusable_5fsize',['mi_malloc_usable_size',['../group__posix.html#ga06d07cf357bbac5c73ba5d0c0c421e17',1,'mimalloc-doc.h']]],
+ ['mi_5fmallocn',['mi_mallocn',['../group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6',1,'mimalloc-doc.h']]],
+ ['mi_5fmallocn_5ftp',['mi_mallocn_tp',['../group__typed.html#gae5cb6e0fafc9f23169c5622e077afe8b',1,'mimalloc-doc.h']]],
+ ['mi_5fmemalign',['mi_memalign',['../group__posix.html#gaab7fa71ea93b96873f5d9883db57d40e',1,'mimalloc-doc.h']]],
+ ['mi_5fnew',['mi_new',['../group__cpp.html#gaad048a9fce3d02c5909cd05c6ec24545',1,'mimalloc-doc.h']]],
+ ['mi_5fnew_5faligned',['mi_new_aligned',['../group__cpp.html#gaef2c2bdb4f70857902d3c8903ac095f3',1,'mimalloc-doc.h']]],
+ ['mi_5fnew_5faligned_5fnothrow',['mi_new_aligned_nothrow',['../group__cpp.html#gab5e29558926d934c3f1cae8c815f942c',1,'mimalloc-doc.h']]],
+ ['mi_5fnew_5fn',['mi_new_n',['../group__cpp.html#gae7bc4f56cd57ed3359060ff4f38bda81',1,'mimalloc-doc.h']]],
+ ['mi_5fnew_5fnothrow',['mi_new_nothrow',['../group__cpp.html#gaeaded64eda71ed6b1d569d3e723abc4a',1,'mimalloc-doc.h']]],
+ ['mi_5fnew_5frealloc',['mi_new_realloc',['../group__cpp.html#gaab78a32f55149e9fbf432d5288e38e1e',1,'mimalloc-doc.h']]],
+ ['mi_5fnew_5freallocn',['mi_new_reallocn',['../group__cpp.html#ga756f4b2bc6a7ecd0a90baea8e90c7907',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fdisable',['mi_option_disable',['../group__options.html#gaebf6ff707a2e688ebb1a2296ca564054',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5feager_5fcommit',['mi_option_eager_commit',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5feager_5fcommit_5fdelay',['mi_option_eager_commit_delay',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5feager_5fregion_5fcommit',['mi_option_eager_region_commit',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fenable',['mi_option_enable',['../group__options.html#ga04180ae41b0d601421dd62ced40ca050',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fget',['mi_option_get',['../group__options.html#ga7e8af195cc81d3fa64ccf2662caa565a',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fis_5fenabled',['mi_option_is_enabled',['../group__options.html#ga459ad98f18b3fc9275474807fe0ca188',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5flarge_5fos_5fpages',['mi_option_large_os_pages',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fos_5ftag',['mi_option_os_tag',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fpage_5freset',['mi_option_page_reset',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5freserve_5fhuge_5fos_5fpages',['mi_option_reserve_huge_os_pages',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5freset_5fdecommits',['mi_option_reset_decommits',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5freset_5fdelay',['mi_option_reset_delay',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fsegment_5fcache',['mi_option_segment_cache',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fsegment_5freset',['mi_option_segment_reset',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fset',['mi_option_set',['../group__options.html#gaf84921c32375e25754dc2ee6a911fa60',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fset_5fdefault',['mi_option_set_default',['../group__options.html#ga7ef623e440e6e5545cb08c94e71e4b90',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fset_5fenabled',['mi_option_set_enabled',['../group__options.html#ga9a13d05fcb77489cb06d4d017ebd8bed',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fset_5fenabled_5fdefault',['mi_option_set_enabled_default',['../group__options.html#ga65518b69ec5d32336b50e07f74b3f629',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fshow_5ferrors',['mi_option_show_errors',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fshow_5fstats',['mi_option_show_stats',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5ft',['mi_option_t',['../group__options.html#gafebf7ed116adb38ae5218bc3ce06884c',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fuse_5fnuma_5fnodes',['mi_option_use_numa_nodes',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fverbose',['mi_option_verbose',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777',1,'mimalloc-doc.h']]],
+ ['mi_5foutput_5ffun',['mi_output_fun',['../group__extended.html#gad823d23444a4b77a40f66bf075a98a0c',1,'mimalloc-doc.h']]],
+ ['mi_5fposix_5fmemalign',['mi_posix_memalign',['../group__posix.html#gacff84f226ba9feb2031b8992e5579447',1,'mimalloc-doc.h']]],
+ ['mi_5fpvalloc',['mi_pvalloc',['../group__posix.html#gaeb325c39b887d3b90d85d1eb1712fb1e',1,'mimalloc-doc.h']]],
+ ['mi_5frealloc',['mi_realloc',['../group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6',1,'mimalloc-doc.h']]],
+ ['mi_5frealloc_5faligned',['mi_realloc_aligned',['../group__aligned.html#ga4028d1cf4aa4c87c880747044a8322ae',1,'mimalloc-doc.h']]],
+ ['mi_5frealloc_5faligned_5fat',['mi_realloc_aligned_at',['../group__aligned.html#gaf66a9ae6c6f08bd6be6fb6ea771faffb',1,'mimalloc-doc.h']]],
+ ['mi_5freallocarray',['mi_reallocarray',['../group__posix.html#ga48fad8648a2f1dab9c87ea9448a52088',1,'mimalloc-doc.h']]],
+ ['mi_5freallocf',['mi_reallocf',['../group__malloc.html#gafe68ac7c5e24a65cd55c9d6b152211a0',1,'mimalloc-doc.h']]],
+ ['mi_5freallocn',['mi_reallocn',['../group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853',1,'mimalloc-doc.h']]],
+ ['mi_5freallocn_5ftp',['mi_reallocn_tp',['../group__typed.html#ga1158b49a55dfa81f58a4426a7578f523',1,'mimalloc-doc.h']]],
+ ['mi_5frealpath',['mi_realpath',['../group__malloc.html#ga08cec32dd5bbe7da91c78d19f1b5bebe',1,'mimalloc-doc.h']]],
+ ['mi_5frecalloc',['mi_recalloc',['../group__malloc.html#ga23a0fbb452b5dce8e31fab1a1958cacc',1,'mimalloc-doc.h']]],
+ ['mi_5frecalloc_5faligned',['mi_recalloc_aligned',['../group__zeroinit.html#ga3e7e5c291acf1c7fd7ffd9914a9f945f',1,'mimalloc-doc.h']]],
+ ['mi_5frecalloc_5faligned_5fat',['mi_recalloc_aligned_at',['../group__zeroinit.html#ga4ff5e92ad73585418a072c9d059e5cf9',1,'mimalloc-doc.h']]],
+ ['mi_5fregister_5fdeferred_5ffree',['mi_register_deferred_free',['../group__extended.html#ga3460a6ca91af97be4058f523d3cb8ece',1,'mimalloc-doc.h']]],
+ ['mi_5fregister_5ferror',['mi_register_error',['../group__extended.html#gaa1d55e0e894be240827e5d87ec3a1f45',1,'mimalloc-doc.h']]],
+ ['mi_5fregister_5foutput',['mi_register_output',['../group__extended.html#gae5b17ff027cd2150b43a33040250cf3f',1,'mimalloc-doc.h']]],
+ ['mi_5freserve_5fhuge_5fos_5fpages_5fat',['mi_reserve_huge_os_pages_at',['../group__extended.html#ga7795a13d20087447281858d2c771cca1',1,'mimalloc-doc.h']]],
+ ['mi_5freserve_5fhuge_5fos_5fpages_5finterleave',['mi_reserve_huge_os_pages_interleave',['../group__extended.html#ga3132f521fb756fc0e8ec0b74fb58df50',1,'mimalloc-doc.h']]],
+ ['mi_5frezalloc',['mi_rezalloc',['../group__zeroinit.html#ga8c292e142110229a2980b37ab036dbc6',1,'mimalloc-doc.h']]],
+ ['mi_5frezalloc_5faligned',['mi_rezalloc_aligned',['../group__zeroinit.html#gacd71a7bce96aab38ae6de17af2eb2cf0',1,'mimalloc-doc.h']]],
+ ['mi_5frezalloc_5faligned_5fat',['mi_rezalloc_aligned_at',['../group__zeroinit.html#gae8b358c417e61d5307da002702b0a8e1',1,'mimalloc-doc.h']]],
+ ['mi_5fsmall_5fsize_5fmax',['MI_SMALL_SIZE_MAX',['../group__extended.html#ga1ea64283508718d9d645c38efc2f4305',1,'mimalloc-doc.h']]],
+ ['mi_5fstats_5fmerge',['mi_stats_merge',['../group__extended.html#ga854b1de8cb067c7316286c28b2fcd3d1',1,'mimalloc-doc.h']]],
+ ['mi_5fstats_5fprint',['mi_stats_print',['../group__extended.html#ga2d126e5c62d3badc35445e5d84166df2',1,'mimalloc-doc.h']]],
+ ['mi_5fstats_5fprint_5fout',['mi_stats_print_out',['../group__extended.html#ga537f13b299ddf801e49a5a94fde02c79',1,'mimalloc-doc.h']]],
+ ['mi_5fstats_5freset',['mi_stats_reset',['../group__extended.html#ga3bb8468b8cfcc6e2a61d98aee85c5f99',1,'mimalloc-doc.h']]],
+ ['mi_5fstl_5fallocator',['mi_stl_allocator',['../group__cpp.html#structmi__stl__allocator',1,'']]],
+ ['mi_5fstrdup',['mi_strdup',['../group__malloc.html#gac7cffe13f1f458ed16789488bf92b9b2',1,'mimalloc-doc.h']]],
+ ['mi_5fstrndup',['mi_strndup',['../group__malloc.html#gaaabf971c2571891433477e2d21a35266',1,'mimalloc-doc.h']]],
+ ['mi_5fthread_5fdone',['mi_thread_done',['../group__extended.html#ga0ae4581e85453456a0d658b2b98bf7bf',1,'mimalloc-doc.h']]],
+ ['mi_5fthread_5finit',['mi_thread_init',['../group__extended.html#gaf8e73efc2cbca9ebfdfb166983a04c17',1,'mimalloc-doc.h']]],
+ ['mi_5fthread_5fstats_5fprint_5fout',['mi_thread_stats_print_out',['../group__extended.html#gab1dac8476c46cb9eecab767eb40c1525',1,'mimalloc-doc.h']]],
+ ['mi_5fusable_5fsize',['mi_usable_size',['../group__extended.html#ga089c859d9eddc5f9b4bd946cd53cebee',1,'mimalloc-doc.h']]],
+ ['mi_5fvalloc',['mi_valloc',['../group__posix.html#ga73baaf5951f5165ba0763d0c06b6a93b',1,'mimalloc-doc.h']]],
+ ['mi_5fzalloc',['mi_zalloc',['../group__malloc.html#gafdd9d8bb2986e668ba9884f28af38000',1,'mimalloc-doc.h']]],
+ ['mi_5fzalloc_5faligned',['mi_zalloc_aligned',['../group__aligned.html#ga0cadbcf5b89a7b6fb171bc8df8734819',1,'mimalloc-doc.h']]],
+ ['mi_5fzalloc_5faligned_5fat',['mi_zalloc_aligned_at',['../group__aligned.html#ga5f8c2353766db522565e642fafd8a3f8',1,'mimalloc-doc.h']]],
+ ['mi_5fzalloc_5fsmall',['mi_zalloc_small',['../group__extended.html#ga220f29f40a44404b0061c15bc1c31152',1,'mimalloc-doc.h']]],
+ ['mi_5fzalloc_5ftp',['mi_zalloc_tp',['../group__typed.html#gac77a61bdaf680a803785fe307820b48c',1,'mimalloc-doc.h']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_7.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['overriding_20malloc',['Overriding Malloc',['../overrides.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_8.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['performance',['Performance',['../bench.html',1,'']]],
+ ['posix',['Posix',['../group__posix.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_9.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['runtime_20options',['Runtime Options',['../group__options.html',1,'']]],
+ ['reserved',['reserved',['../group__analysis.html#ae848a3e6840414891035423948ca0383',1,'mi_heap_area_t']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_a.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['typed_20macros',['Typed Macros',['../group__typed.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_b.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['used',['used',['../group__analysis.html#ab820302c5cd0df133eb8e51650a008b4',1,'mi_heap_area_t']]],
+ ['using_20the_20library',['Using the library',['../using.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_c.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['zero_20initialized_20re_2dallocation',['Zero initialized re-allocation',['../group__zeroinit.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="all_d.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['zero_20initialized_20re_2dallocation',['Zero initialized re-allocation',['../group__zeroinit.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="classes_0.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['mi_5fheap_5farea_5ft',['mi_heap_area_t',['../group__analysis.html#structmi__heap__area__t',1,'']]],
+ ['mi_5fstl_5fallocator',['mi_stl_allocator',['../group__cpp.html#structmi__stl__allocator',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="enums_0.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['mi_5foption_5ft',['mi_option_t',['../group__options.html#gafebf7ed116adb38ae5218bc3ce06884c',1,'mimalloc-doc.h']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="enumvalues_0.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['_5fmi_5foption_5flast',['_mi_option_last',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a',1,'mimalloc-doc.h']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="enumvalues_1.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['mi_5foption_5feager_5fcommit',['mi_option_eager_commit',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5feager_5fcommit_5fdelay',['mi_option_eager_commit_delay',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5feager_5fregion_5fcommit',['mi_option_eager_region_commit',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5flarge_5fos_5fpages',['mi_option_large_os_pages',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fos_5ftag',['mi_option_os_tag',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fpage_5freset',['mi_option_page_reset',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5freserve_5fhuge_5fos_5fpages',['mi_option_reserve_huge_os_pages',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5freset_5fdecommits',['mi_option_reset_decommits',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5freset_5fdelay',['mi_option_reset_delay',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fsegment_5fcache',['mi_option_segment_cache',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fsegment_5freset',['mi_option_segment_reset',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fshow_5ferrors',['mi_option_show_errors',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fshow_5fstats',['mi_option_show_stats',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fuse_5fnuma_5fnodes',['mi_option_use_numa_nodes',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fverbose',['mi_option_verbose',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777',1,'mimalloc-doc.h']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="functions_0.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['mi_5f_5fposix_5fmemalign',['mi__posix_memalign',['../group__posix.html#gad5a69c8fea96aa2b7a7c818c2130090a',1,'mimalloc-doc.h']]],
+ ['mi_5faligned_5falloc',['mi_aligned_alloc',['../group__posix.html#ga1326d2e4388630b5f81ca7206318b8e5',1,'mimalloc-doc.h']]],
+ ['mi_5fcalloc',['mi_calloc',['../group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d',1,'mimalloc-doc.h']]],
+ ['mi_5fcalloc_5faligned',['mi_calloc_aligned',['../group__aligned.html#ga53dddb4724042a90315b94bc268fb4c9',1,'mimalloc-doc.h']]],
+ ['mi_5fcalloc_5faligned_5fat',['mi_calloc_aligned_at',['../group__aligned.html#ga08647c4593f3b2eef24a919a73eba3a3',1,'mimalloc-doc.h']]],
+ ['mi_5fcfree',['mi_cfree',['../group__posix.html#ga705dc7a64bffacfeeb0141501a5c35d7',1,'mimalloc-doc.h']]],
+ ['mi_5fcheck_5fowned',['mi_check_owned',['../group__analysis.html#ga628c237489c2679af84a4d0d143b3dd5',1,'mimalloc-doc.h']]],
+ ['mi_5fcollect',['mi_collect',['../group__extended.html#ga421430e2226d7d468529cec457396756',1,'mimalloc-doc.h']]],
+ ['mi_5fexpand',['mi_expand',['../group__malloc.html#gaaee66a1d483c3e28f585525fb96707e4',1,'mimalloc-doc.h']]],
+ ['mi_5ffree',['mi_free',['../group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95',1,'mimalloc-doc.h']]],
+ ['mi_5ffree_5faligned',['mi_free_aligned',['../group__posix.html#ga0d28d5cf61e6bfbb18c63092939fe5c9',1,'mimalloc-doc.h']]],
+ ['mi_5ffree_5fsize',['mi_free_size',['../group__posix.html#gae01389eedab8d67341ff52e2aad80ebb',1,'mimalloc-doc.h']]],
+ ['mi_5ffree_5fsize_5faligned',['mi_free_size_aligned',['../group__posix.html#ga72e9d7ffb5fe94d69bc722c8506e27bc',1,'mimalloc-doc.h']]],
+ ['mi_5fgood_5fsize',['mi_good_size',['../group__extended.html#gac057927cd06c854b45fe7847e921bd47',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fcalloc',['mi_heap_calloc',['../group__heap.html#gaa6702b3c48e9e53e50e81b36f5011d55',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fcalloc_5faligned',['mi_heap_calloc_aligned',['../group__heap.html#ga4af03a6e2b93fae77424d93f889705c3',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fcalloc_5faligned_5fat',['mi_heap_calloc_aligned_at',['../group__heap.html#ga08ca6419a5c057a4d965868998eef487',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fcheck_5fowned',['mi_heap_check_owned',['../group__analysis.html#ga0d67c1789faaa15ff366c024fcaf6377',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fcollect',['mi_heap_collect',['../group__heap.html#ga7922f7495cde30b1984d0e6072419298',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fcontains_5fblock',['mi_heap_contains_block',['../group__analysis.html#gaa862aa8ed8d57d84cae41fc1022d71af',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fdelete',['mi_heap_delete',['../group__heap.html#ga2ab1af8d438819b55319c7ef51d1e409',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fdestroy',['mi_heap_destroy',['../group__heap.html#ga9f9c0844edb9717f4feacd79116b8e0d',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fget_5fbacking',['mi_heap_get_backing',['../group__heap.html#ga5d03fbe062ffcf38f0f417fd968357fc',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fget_5fdefault',['mi_heap_get_default',['../group__heap.html#ga8db4cbb87314a989a9a187464d6b5e05',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fmalloc',['mi_heap_malloc',['../group__heap.html#ga9cbed01e42c0647907295de92c3fa296',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fmalloc_5faligned',['mi_heap_malloc_aligned',['../group__heap.html#gab5b87e1805306f70df38789fcfcf6653',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fmalloc_5faligned_5fat',['mi_heap_malloc_aligned_at',['../group__heap.html#ga23acd7680fb0976dde3783254c6c874b',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fmalloc_5fsmall',['mi_heap_malloc_small',['../group__heap.html#gaa1a1c7a1f4da6826b5a25b70ef878368',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fmallocn',['mi_heap_mallocn',['../group__heap.html#ga851da6c43fe0b71c1376cee8aef90db0',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fnew',['mi_heap_new',['../group__heap.html#ga766f672ba56f2fbfeb9d9dbb0b7f6b11',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frealloc',['mi_heap_realloc',['../group__heap.html#gaaef3395f66be48f37bdc8322509c5d81',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frealloc_5faligned',['mi_heap_realloc_aligned',['../group__heap.html#gafc603b696bd14cae6da28658f950d98c',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frealloc_5faligned_5fat',['mi_heap_realloc_aligned_at',['../group__heap.html#gaf96c788a1bf553fe2d371de9365e047c',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5freallocf',['mi_heap_reallocf',['../group__heap.html#ga4a21070eb4e7cce018133c8d5f4b0527',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5freallocn',['mi_heap_reallocn',['../group__heap.html#gac74e94ad9b0c9b57c1c4d88b8825b7a8',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frealpath',['mi_heap_realpath',['../group__heap.html#ga00e95ba1e01acac3cfd95bb7a357a6f0',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frecalloc',['mi_heap_recalloc',['../group__zeroinit.html#ga8648c5fbb22a80f0262859099f06dfbd',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frecalloc_5faligned',['mi_heap_recalloc_aligned',['../group__zeroinit.html#ga9f3f999396c8f77ca5e80e7b40ac29e3',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frecalloc_5faligned_5fat',['mi_heap_recalloc_aligned_at',['../group__zeroinit.html#ga496452c96f1de8c500be9fddf52edaf7',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frezalloc',['mi_heap_rezalloc',['../group__zeroinit.html#gacfad83f14eb5d6a42a497a898e19fc76',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frezalloc_5faligned',['mi_heap_rezalloc_aligned',['../group__zeroinit.html#ga375fa8a611c51905e592d5d467c49664',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5frezalloc_5faligned_5fat',['mi_heap_rezalloc_aligned_at',['../group__zeroinit.html#gac90da54fa7e5d10bdc97ce0b51dce2eb',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fset_5fdefault',['mi_heap_set_default',['../group__heap.html#gab8631ec88c8d26641b68b5d25dcd4422',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fstrdup',['mi_heap_strdup',['../group__heap.html#ga139d6b09dbf50c3c2523d0f4d1cfdeb5',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fstrndup',['mi_heap_strndup',['../group__heap.html#ga8e3dbd46650dd26573cf307a2c8f1f5a',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fvisit_5fblocks',['mi_heap_visit_blocks',['../group__analysis.html#ga70c46687dc6e9dc98b232b02646f8bed',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fzalloc',['mi_heap_zalloc',['../group__heap.html#ga903104592c8ed53417a3762da6241133',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fzalloc_5faligned',['mi_heap_zalloc_aligned',['../group__heap.html#gaa450a59c6c7ae5fdbd1c2b80a8329ef0',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5fzalloc_5faligned_5fat',['mi_heap_zalloc_aligned_at',['../group__heap.html#ga45fb43a62776fbebbdf1edd99b527954',1,'mimalloc-doc.h']]],
+ ['mi_5fis_5fin_5fheap_5fregion',['mi_is_in_heap_region',['../group__extended.html#ga5f071b10d4df1c3658e04e7fd67a94e6',1,'mimalloc-doc.h']]],
+ ['mi_5fis_5fredirected',['mi_is_redirected',['../group__extended.html#gaad25050b19f30cd79397b227e0157a3f',1,'mimalloc-doc.h']]],
+ ['mi_5fmalloc',['mi_malloc',['../group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a',1,'mimalloc-doc.h']]],
+ ['mi_5fmalloc_5faligned',['mi_malloc_aligned',['../group__aligned.html#ga68930196751fa2cca9e1fd0d71bade56',1,'mimalloc-doc.h']]],
+ ['mi_5fmalloc_5faligned_5fat',['mi_malloc_aligned_at',['../group__aligned.html#ga5850da130c936bd77db039dcfbc8295d',1,'mimalloc-doc.h']]],
+ ['mi_5fmalloc_5fsize',['mi_malloc_size',['../group__posix.html#ga4531c9e775bb3ae12db57c1ba8a5d7de',1,'mimalloc-doc.h']]],
+ ['mi_5fmalloc_5fsmall',['mi_malloc_small',['../group__extended.html#ga7136c2e55cb22c98ecf95d08d6debb99',1,'mimalloc-doc.h']]],
+ ['mi_5fmalloc_5fusable_5fsize',['mi_malloc_usable_size',['../group__posix.html#ga06d07cf357bbac5c73ba5d0c0c421e17',1,'mimalloc-doc.h']]],
+ ['mi_5fmallocn',['mi_mallocn',['../group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6',1,'mimalloc-doc.h']]],
+ ['mi_5fmemalign',['mi_memalign',['../group__posix.html#gaab7fa71ea93b96873f5d9883db57d40e',1,'mimalloc-doc.h']]],
+ ['mi_5fnew',['mi_new',['../group__cpp.html#gaad048a9fce3d02c5909cd05c6ec24545',1,'mimalloc-doc.h']]],
+ ['mi_5fnew_5faligned',['mi_new_aligned',['../group__cpp.html#gaef2c2bdb4f70857902d3c8903ac095f3',1,'mimalloc-doc.h']]],
+ ['mi_5fnew_5faligned_5fnothrow',['mi_new_aligned_nothrow',['../group__cpp.html#gab5e29558926d934c3f1cae8c815f942c',1,'mimalloc-doc.h']]],
+ ['mi_5fnew_5fn',['mi_new_n',['../group__cpp.html#gae7bc4f56cd57ed3359060ff4f38bda81',1,'mimalloc-doc.h']]],
+ ['mi_5fnew_5fnothrow',['mi_new_nothrow',['../group__cpp.html#gaeaded64eda71ed6b1d569d3e723abc4a',1,'mimalloc-doc.h']]],
+ ['mi_5fnew_5frealloc',['mi_new_realloc',['../group__cpp.html#gaab78a32f55149e9fbf432d5288e38e1e',1,'mimalloc-doc.h']]],
+ ['mi_5fnew_5freallocn',['mi_new_reallocn',['../group__cpp.html#ga756f4b2bc6a7ecd0a90baea8e90c7907',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fdisable',['mi_option_disable',['../group__options.html#gaebf6ff707a2e688ebb1a2296ca564054',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fenable',['mi_option_enable',['../group__options.html#ga04180ae41b0d601421dd62ced40ca050',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fget',['mi_option_get',['../group__options.html#ga7e8af195cc81d3fa64ccf2662caa565a',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fis_5fenabled',['mi_option_is_enabled',['../group__options.html#ga459ad98f18b3fc9275474807fe0ca188',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fset',['mi_option_set',['../group__options.html#gaf84921c32375e25754dc2ee6a911fa60',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fset_5fdefault',['mi_option_set_default',['../group__options.html#ga7ef623e440e6e5545cb08c94e71e4b90',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fset_5fenabled',['mi_option_set_enabled',['../group__options.html#ga9a13d05fcb77489cb06d4d017ebd8bed',1,'mimalloc-doc.h']]],
+ ['mi_5foption_5fset_5fenabled_5fdefault',['mi_option_set_enabled_default',['../group__options.html#ga65518b69ec5d32336b50e07f74b3f629',1,'mimalloc-doc.h']]],
+ ['mi_5fposix_5fmemalign',['mi_posix_memalign',['../group__posix.html#gacff84f226ba9feb2031b8992e5579447',1,'mimalloc-doc.h']]],
+ ['mi_5fpvalloc',['mi_pvalloc',['../group__posix.html#gaeb325c39b887d3b90d85d1eb1712fb1e',1,'mimalloc-doc.h']]],
+ ['mi_5frealloc',['mi_realloc',['../group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6',1,'mimalloc-doc.h']]],
+ ['mi_5frealloc_5faligned',['mi_realloc_aligned',['../group__aligned.html#ga4028d1cf4aa4c87c880747044a8322ae',1,'mimalloc-doc.h']]],
+ ['mi_5frealloc_5faligned_5fat',['mi_realloc_aligned_at',['../group__aligned.html#gaf66a9ae6c6f08bd6be6fb6ea771faffb',1,'mimalloc-doc.h']]],
+ ['mi_5freallocarray',['mi_reallocarray',['../group__posix.html#ga48fad8648a2f1dab9c87ea9448a52088',1,'mimalloc-doc.h']]],
+ ['mi_5freallocf',['mi_reallocf',['../group__malloc.html#gafe68ac7c5e24a65cd55c9d6b152211a0',1,'mimalloc-doc.h']]],
+ ['mi_5freallocn',['mi_reallocn',['../group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853',1,'mimalloc-doc.h']]],
+ ['mi_5frealpath',['mi_realpath',['../group__malloc.html#ga08cec32dd5bbe7da91c78d19f1b5bebe',1,'mimalloc-doc.h']]],
+ ['mi_5frecalloc',['mi_recalloc',['../group__malloc.html#ga23a0fbb452b5dce8e31fab1a1958cacc',1,'mimalloc-doc.h']]],
+ ['mi_5frecalloc_5faligned',['mi_recalloc_aligned',['../group__zeroinit.html#ga3e7e5c291acf1c7fd7ffd9914a9f945f',1,'mimalloc-doc.h']]],
+ ['mi_5frecalloc_5faligned_5fat',['mi_recalloc_aligned_at',['../group__zeroinit.html#ga4ff5e92ad73585418a072c9d059e5cf9',1,'mimalloc-doc.h']]],
+ ['mi_5fregister_5fdeferred_5ffree',['mi_register_deferred_free',['../group__extended.html#ga3460a6ca91af97be4058f523d3cb8ece',1,'mimalloc-doc.h']]],
+ ['mi_5fregister_5ferror',['mi_register_error',['../group__extended.html#gaa1d55e0e894be240827e5d87ec3a1f45',1,'mimalloc-doc.h']]],
+ ['mi_5fregister_5foutput',['mi_register_output',['../group__extended.html#gae5b17ff027cd2150b43a33040250cf3f',1,'mimalloc-doc.h']]],
+ ['mi_5freserve_5fhuge_5fos_5fpages_5fat',['mi_reserve_huge_os_pages_at',['../group__extended.html#ga7795a13d20087447281858d2c771cca1',1,'mimalloc-doc.h']]],
+ ['mi_5freserve_5fhuge_5fos_5fpages_5finterleave',['mi_reserve_huge_os_pages_interleave',['../group__extended.html#ga3132f521fb756fc0e8ec0b74fb58df50',1,'mimalloc-doc.h']]],
+ ['mi_5frezalloc',['mi_rezalloc',['../group__zeroinit.html#ga8c292e142110229a2980b37ab036dbc6',1,'mimalloc-doc.h']]],
+ ['mi_5frezalloc_5faligned',['mi_rezalloc_aligned',['../group__zeroinit.html#gacd71a7bce96aab38ae6de17af2eb2cf0',1,'mimalloc-doc.h']]],
+ ['mi_5frezalloc_5faligned_5fat',['mi_rezalloc_aligned_at',['../group__zeroinit.html#gae8b358c417e61d5307da002702b0a8e1',1,'mimalloc-doc.h']]],
+ ['mi_5fstats_5fmerge',['mi_stats_merge',['../group__extended.html#ga854b1de8cb067c7316286c28b2fcd3d1',1,'mimalloc-doc.h']]],
+ ['mi_5fstats_5fprint',['mi_stats_print',['../group__extended.html#ga2d126e5c62d3badc35445e5d84166df2',1,'mimalloc-doc.h']]],
+ ['mi_5fstats_5fprint_5fout',['mi_stats_print_out',['../group__extended.html#ga537f13b299ddf801e49a5a94fde02c79',1,'mimalloc-doc.h']]],
+ ['mi_5fstats_5freset',['mi_stats_reset',['../group__extended.html#ga3bb8468b8cfcc6e2a61d98aee85c5f99',1,'mimalloc-doc.h']]],
+ ['mi_5fstrdup',['mi_strdup',['../group__malloc.html#gac7cffe13f1f458ed16789488bf92b9b2',1,'mimalloc-doc.h']]],
+ ['mi_5fstrndup',['mi_strndup',['../group__malloc.html#gaaabf971c2571891433477e2d21a35266',1,'mimalloc-doc.h']]],
+ ['mi_5fthread_5fdone',['mi_thread_done',['../group__extended.html#ga0ae4581e85453456a0d658b2b98bf7bf',1,'mimalloc-doc.h']]],
+ ['mi_5fthread_5finit',['mi_thread_init',['../group__extended.html#gaf8e73efc2cbca9ebfdfb166983a04c17',1,'mimalloc-doc.h']]],
+ ['mi_5fthread_5fstats_5fprint_5fout',['mi_thread_stats_print_out',['../group__extended.html#gab1dac8476c46cb9eecab767eb40c1525',1,'mimalloc-doc.h']]],
+ ['mi_5fusable_5fsize',['mi_usable_size',['../group__extended.html#ga089c859d9eddc5f9b4bd946cd53cebee',1,'mimalloc-doc.h']]],
+ ['mi_5fvalloc',['mi_valloc',['../group__posix.html#ga73baaf5951f5165ba0763d0c06b6a93b',1,'mimalloc-doc.h']]],
+ ['mi_5fzalloc',['mi_zalloc',['../group__malloc.html#gafdd9d8bb2986e668ba9884f28af38000',1,'mimalloc-doc.h']]],
+ ['mi_5fzalloc_5faligned',['mi_zalloc_aligned',['../group__aligned.html#ga0cadbcf5b89a7b6fb171bc8df8734819',1,'mimalloc-doc.h']]],
+ ['mi_5fzalloc_5faligned_5fat',['mi_zalloc_aligned_at',['../group__aligned.html#ga5f8c2353766db522565e642fafd8a3f8',1,'mimalloc-doc.h']]],
+ ['mi_5fzalloc_5fsmall',['mi_zalloc_small',['../group__extended.html#ga220f29f40a44404b0061c15bc1c31152',1,'mimalloc-doc.h']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="functions_1.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['void',['void',['../group__extended.html#gadc49452cc1634aa03ac83ffe9b97a19c',1,'mimalloc-doc.h']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="groups_0.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['aligned_20allocation',['Aligned Allocation',['../group__aligned.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="groups_1.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['basic_20allocation',['Basic Allocation',['../group__malloc.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="groups_2.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['c_2b_2b_20wrappers',['C++ wrappers',['../group__cpp.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="groups_3.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['extended_20functions',['Extended Functions',['../group__extended.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="groups_4.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['heap_20introspection',['Heap Introspection',['../group__analysis.html',1,'']]],
+ ['heap_20allocation',['Heap Allocation',['../group__heap.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="groups_5.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['posix',['Posix',['../group__posix.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="groups_6.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['runtime_20options',['Runtime Options',['../group__options.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="groups_7.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['typed_20macros',['Typed Macros',['../group__typed.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="groups_8.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['zero_20initialized_20re_2dallocation',['Zero initialized re-allocation',['../group__zeroinit.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="NoMatches">No Matches</div>
+</div>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="pages_0.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['building',['Building',['../build.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="pages_1.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['environment_20options',['Environment Options',['../environment.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="pages_2.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['overriding_20malloc',['Overriding Malloc',['../overrides.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="pages_3.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['performance',['Performance',['../bench.html',1,'']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="pages_4.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['using_20the_20library',['Using the library',['../using.html',1,'']]]
+];
--- /dev/null
+/*---------------- Search Box */
+
+#FSearchBox {
+ float: left;
+}
+
+#MSearchBox {
+ white-space : nowrap;
+ float: none;
+ margin-top: 0px;
+ right: 0px;
+ width: 170px;
+ height: 24px;
+ z-index: 102;
+ display: inline;
+ position: absolute;
+}
+
+#MSearchBox .left
+{
+ display:block;
+ position:absolute;
+ left:10px;
+ width:20px;
+ height:19px;
+ background:url('search_l.png') no-repeat;
+ background-position:right;
+}
+
+#MSearchSelect {
+ display:block;
+ position:absolute;
+ width:20px;
+ height:19px;
+}
+
+.left #MSearchSelect {
+ left:4px;
+}
+
+.right #MSearchSelect {
+ right:5px;
+}
+
+#MSearchField {
+ display:block;
+ position:absolute;
+ height:19px;
+ background:url('search_m.png') repeat-x;
+ border:none;
+ width:111px;
+ margin-left:20px;
+ padding-left:4px;
+ color: #909090;
+ outline: none;
+ font: 9pt Arial, Verdana, sans-serif;
+ -webkit-border-radius: 0px;
+}
+
+#FSearchBox #MSearchField {
+ margin-left:15px;
+}
+
+#MSearchBox .right {
+ display:block;
+ position:absolute;
+ right:10px;
+ top:0px;
+ width:20px;
+ height:19px;
+ background:url('search_r.png') no-repeat;
+ background-position:left;
+}
+
+#MSearchClose {
+ display: none;
+ position: absolute;
+ top: 4px;
+ background : none;
+ border: none;
+ margin: 0px 4px 0px 0px;
+ padding: 0px 0px;
+ outline: none;
+}
+
+.left #MSearchClose {
+ left: 6px;
+}
+
+.right #MSearchClose {
+ right: 2px;
+}
+
+.MSearchBoxActive #MSearchField {
+ color: #000000;
+}
+
+/*---------------- Search filter selection */
+
+#MSearchSelectWindow {
+ display: none;
+ position: absolute;
+ left: 0; top: 0;
+ border: 1px solid #4F5657;
+ background-color: #F2F3F3;
+ z-index: 10001;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-top-left-radius: 4px;
+ -webkit-border-top-right-radius: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+ -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+}
+
+.SelectItem {
+ font: 8pt Arial, Verdana, sans-serif;
+ padding-left: 2px;
+ padding-right: 12px;
+ border: 0px;
+}
+
+span.SelectionMark {
+ margin-right: 4px;
+ font-family: monospace;
+ outline-style: none;
+ text-decoration: none;
+}
+
+a.SelectItem {
+ display: block;
+ outline-style: none;
+ color: #000000;
+ text-decoration: none;
+ padding-left: 6px;
+ padding-right: 12px;
+}
+
+a.SelectItem:focus,
+a.SelectItem:active {
+ color: #000000;
+ outline-style: none;
+ text-decoration: none;
+}
+
+a.SelectItem:hover {
+ color: #FFFFFF;
+ background-color: #0F1010;
+ outline-style: none;
+ text-decoration: none;
+ cursor: pointer;
+ display: block;
+}
+
+/*---------------- Search results window */
+
+iframe#MSearchResults {
+ width: 60ex;
+ height: 15em;
+}
+
+#MSearchResultsWindow {
+ display: none;
+ position: absolute;
+ left: 0; top: 0;
+ border: 1px solid #000;
+ background-color: #DADDDE;
+ z-index:10000;
+}
+
+/* ----------------------------------- */
+
+
+#SRIndex {
+ clear:both;
+ padding-bottom: 15px;
+}
+
+.SREntry {
+ font-size: 10pt;
+ padding-left: 1ex;
+}
+
+.SRPage .SREntry {
+ font-size: 8pt;
+ padding: 1px 5px;
+}
+
+body.SRPage {
+ margin: 5px 2px;
+}
+
+.SRChildren {
+ padding-left: 3ex; padding-bottom: .5em
+}
+
+.SRPage .SRChildren {
+ display: none;
+}
+
+.SRSymbol {
+ font-weight: bold;
+ color: #121414;
+ font-family: Arial, Verdana, sans-serif;
+ text-decoration: none;
+ outline: none;
+}
+
+a.SRScope {
+ display: block;
+ color: #121414;
+ font-family: Arial, Verdana, sans-serif;
+ text-decoration: none;
+ outline: none;
+}
+
+a.SRSymbol:focus, a.SRSymbol:active,
+a.SRScope:focus, a.SRScope:active {
+ text-decoration: underline;
+}
+
+span.SRScope {
+ padding-left: 4px;
+}
+
+.SRPage .SRStatus {
+ padding: 2px 5px;
+ font-size: 8pt;
+ font-style: italic;
+}
+
+.SRResult {
+ display: none;
+}
+
+DIV.searchresults {
+ margin-left: 10px;
+ margin-right: 10px;
+}
+
+/*---------------- External search page results */
+
+.searchresult {
+ background-color: #DFE1E2;
+}
+
+.pages b {
+ color: white;
+ padding: 5px 5px 3px 5px;
+ background-image: url("../tab_a.png");
+ background-repeat: repeat-x;
+ text-shadow: 0 1px 1px #000000;
+}
+
+.pages {
+ line-height: 17px;
+ margin-left: 4px;
+ text-decoration: none;
+}
+
+.hl {
+ font-weight: bold;
+}
+
+#searchresults {
+ margin-bottom: 20px;
+}
+
+.searchpages {
+ margin-top: 10px;
+}
+
--- /dev/null
+/*
+ @licstart The following is the entire license notice for the
+ JavaScript code in this file.
+
+ Copyright (C) 1997-2017 by Dimitri van Heesch
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ @licend The above is the entire license notice
+ for the JavaScript code in this file
+ */
+function convertToId(search)
+{
+ var result = '';
+ for (i=0;i<search.length;i++)
+ {
+ var c = search.charAt(i);
+ var cn = c.charCodeAt(0);
+ if (c.match(/[a-z0-9\u0080-\uFFFF]/))
+ {
+ result+=c;
+ }
+ else if (cn<16)
+ {
+ result+="_0"+cn.toString(16);
+ }
+ else
+ {
+ result+="_"+cn.toString(16);
+ }
+ }
+ return result;
+}
+
+function getXPos(item)
+{
+ var x = 0;
+ if (item.offsetWidth)
+ {
+ while (item && item!=document.body)
+ {
+ x += item.offsetLeft;
+ item = item.offsetParent;
+ }
+ }
+ return x;
+}
+
+function getYPos(item)
+{
+ var y = 0;
+ if (item.offsetWidth)
+ {
+ while (item && item!=document.body)
+ {
+ y += item.offsetTop;
+ item = item.offsetParent;
+ }
+ }
+ return y;
+}
+
+/* A class handling everything associated with the search panel.
+
+ Parameters:
+ name - The name of the global variable that will be
+ storing this instance. Is needed to be able to set timeouts.
+ resultPath - path to use for external files
+*/
+function SearchBox(name, resultsPath, inFrame, label)
+{
+ if (!name || !resultsPath) { alert("Missing parameters to SearchBox."); }
+
+ // ---------- Instance variables
+ this.name = name;
+ this.resultsPath = resultsPath;
+ this.keyTimeout = 0;
+ this.keyTimeoutLength = 500;
+ this.closeSelectionTimeout = 300;
+ this.lastSearchValue = "";
+ this.lastResultsPage = "";
+ this.hideTimeout = 0;
+ this.searchIndex = 0;
+ this.searchActive = false;
+ this.insideFrame = inFrame;
+ this.searchLabel = label;
+
+ // ----------- DOM Elements
+
+ this.DOMSearchField = function()
+ { return document.getElementById("MSearchField"); }
+
+ this.DOMSearchSelect = function()
+ { return document.getElementById("MSearchSelect"); }
+
+ this.DOMSearchSelectWindow = function()
+ { return document.getElementById("MSearchSelectWindow"); }
+
+ this.DOMPopupSearchResults = function()
+ { return document.getElementById("MSearchResults"); }
+
+ this.DOMPopupSearchResultsWindow = function()
+ { return document.getElementById("MSearchResultsWindow"); }
+
+ this.DOMSearchClose = function()
+ { return document.getElementById("MSearchClose"); }
+
+ this.DOMSearchBox = function()
+ { return document.getElementById("MSearchBox"); }
+
+ // ------------ Event Handlers
+
+ // Called when focus is added or removed from the search field.
+ this.OnSearchFieldFocus = function(isActive)
+ {
+ this.Activate(isActive);
+ }
+
+ this.OnSearchSelectShow = function()
+ {
+ var searchSelectWindow = this.DOMSearchSelectWindow();
+ var searchField = this.DOMSearchSelect();
+
+ if (this.insideFrame)
+ {
+ var left = getXPos(searchField);
+ var top = getYPos(searchField);
+ left += searchField.offsetWidth + 6;
+ top += searchField.offsetHeight;
+
+ // show search selection popup
+ searchSelectWindow.style.display='block';
+ left -= searchSelectWindow.offsetWidth;
+ searchSelectWindow.style.left = left + 'px';
+ searchSelectWindow.style.top = top + 'px';
+ }
+ else
+ {
+ var left = getXPos(searchField);
+ var top = getYPos(searchField);
+ top += searchField.offsetHeight;
+
+ // show search selection popup
+ searchSelectWindow.style.display='block';
+ searchSelectWindow.style.left = left + 'px';
+ searchSelectWindow.style.top = top + 'px';
+ }
+
+ // stop selection hide timer
+ if (this.hideTimeout)
+ {
+ clearTimeout(this.hideTimeout);
+ this.hideTimeout=0;
+ }
+ return false; // to avoid "image drag" default event
+ }
+
+ this.OnSearchSelectHide = function()
+ {
+ this.hideTimeout = setTimeout(this.name +".CloseSelectionWindow()",
+ this.closeSelectionTimeout);
+ }
+
+ // Called when the content of the search field is changed.
+ this.OnSearchFieldChange = function(evt)
+ {
+ if (this.keyTimeout) // kill running timer
+ {
+ clearTimeout(this.keyTimeout);
+ this.keyTimeout = 0;
+ }
+
+ var e = (evt) ? evt : window.event; // for IE
+ if (e.keyCode==40 || e.keyCode==13)
+ {
+ if (e.shiftKey==1)
+ {
+ this.OnSearchSelectShow();
+ var win=this.DOMSearchSelectWindow();
+ for (i=0;i<win.childNodes.length;i++)
+ {
+ var child = win.childNodes[i]; // get span within a
+ if (child.className=='SelectItem')
+ {
+ child.focus();
+ return;
+ }
+ }
+ return;
+ }
+ else if (window.frames.MSearchResults.searchResults)
+ {
+ var elem = window.frames.MSearchResults.searchResults.NavNext(0);
+ if (elem) elem.focus();
+ }
+ }
+ else if (e.keyCode==27) // Escape out of the search field
+ {
+ this.DOMSearchField().blur();
+ this.DOMPopupSearchResultsWindow().style.display = 'none';
+ this.DOMSearchClose().style.display = 'none';
+ this.lastSearchValue = '';
+ this.Activate(false);
+ return;
+ }
+
+ // strip whitespaces
+ var searchValue = this.DOMSearchField().value.replace(/ +/g, "");
+
+ if (searchValue != this.lastSearchValue) // search value has changed
+ {
+ if (searchValue != "") // non-empty search
+ {
+ // set timer for search update
+ this.keyTimeout = setTimeout(this.name + '.Search()',
+ this.keyTimeoutLength);
+ }
+ else // empty search field
+ {
+ this.DOMPopupSearchResultsWindow().style.display = 'none';
+ this.DOMSearchClose().style.display = 'none';
+ this.lastSearchValue = '';
+ }
+ }
+ }
+
+ this.SelectItemCount = function(id)
+ {
+ var count=0;
+ var win=this.DOMSearchSelectWindow();
+ for (i=0;i<win.childNodes.length;i++)
+ {
+ var child = win.childNodes[i]; // get span within a
+ if (child.className=='SelectItem')
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ this.SelectItemSet = function(id)
+ {
+ var i,j=0;
+ var win=this.DOMSearchSelectWindow();
+ for (i=0;i<win.childNodes.length;i++)
+ {
+ var child = win.childNodes[i]; // get span within a
+ if (child.className=='SelectItem')
+ {
+ var node = child.firstChild;
+ if (j==id)
+ {
+ node.innerHTML='•';
+ }
+ else
+ {
+ node.innerHTML=' ';
+ }
+ j++;
+ }
+ }
+ }
+
+ // Called when an search filter selection is made.
+ // set item with index id as the active item
+ this.OnSelectItem = function(id)
+ {
+ this.searchIndex = id;
+ this.SelectItemSet(id);
+ var searchValue = this.DOMSearchField().value.replace(/ +/g, "");
+ if (searchValue!="" && this.searchActive) // something was found -> do a search
+ {
+ this.Search();
+ }
+ }
+
+ this.OnSearchSelectKey = function(evt)
+ {
+ var e = (evt) ? evt : window.event; // for IE
+ if (e.keyCode==40 && this.searchIndex<this.SelectItemCount()) // Down
+ {
+ this.searchIndex++;
+ this.OnSelectItem(this.searchIndex);
+ }
+ else if (e.keyCode==38 && this.searchIndex>0) // Up
+ {
+ this.searchIndex--;
+ this.OnSelectItem(this.searchIndex);
+ }
+ else if (e.keyCode==13 || e.keyCode==27)
+ {
+ this.OnSelectItem(this.searchIndex);
+ this.CloseSelectionWindow();
+ this.DOMSearchField().focus();
+ }
+ return false;
+ }
+
+ // --------- Actions
+
+ // Closes the results window.
+ this.CloseResultsWindow = function()
+ {
+ this.DOMPopupSearchResultsWindow().style.display = 'none';
+ this.DOMSearchClose().style.display = 'none';
+ this.Activate(false);
+ }
+
+ this.CloseSelectionWindow = function()
+ {
+ this.DOMSearchSelectWindow().style.display = 'none';
+ }
+
+ // Performs a search.
+ this.Search = function()
+ {
+ this.keyTimeout = 0;
+
+ // strip leading whitespace
+ var searchValue = this.DOMSearchField().value.replace(/^ +/, "");
+
+ var code = searchValue.toLowerCase().charCodeAt(0);
+ var idxChar = searchValue.substr(0, 1).toLowerCase();
+ if ( 0xD800 <= code && code <= 0xDBFF && searchValue > 1) // surrogate pair
+ {
+ idxChar = searchValue.substr(0, 2);
+ }
+
+ var resultsPage;
+ var resultsPageWithSearch;
+ var hasResultsPage;
+
+ var idx = indexSectionsWithContent[this.searchIndex].indexOf(idxChar);
+ if (idx!=-1)
+ {
+ var hexCode=idx.toString(16);
+ resultsPage = this.resultsPath + '/' + indexSectionNames[this.searchIndex] + '_' + hexCode + '.html';
+ resultsPageWithSearch = resultsPage+'?'+escape(searchValue);
+ hasResultsPage = true;
+ }
+ else // nothing available for this search term
+ {
+ resultsPage = this.resultsPath + '/nomatches.html';
+ resultsPageWithSearch = resultsPage;
+ hasResultsPage = false;
+ }
+
+ window.frames.MSearchResults.location = resultsPageWithSearch;
+ var domPopupSearchResultsWindow = this.DOMPopupSearchResultsWindow();
+
+ if (domPopupSearchResultsWindow.style.display!='block')
+ {
+ var domSearchBox = this.DOMSearchBox();
+ this.DOMSearchClose().style.display = 'inline';
+ if (this.insideFrame)
+ {
+ var domPopupSearchResults = this.DOMPopupSearchResults();
+ domPopupSearchResultsWindow.style.position = 'relative';
+ domPopupSearchResultsWindow.style.display = 'block';
+ var width = document.body.clientWidth - 8; // the -8 is for IE :-(
+ domPopupSearchResultsWindow.style.width = width + 'px';
+ domPopupSearchResults.style.width = width + 'px';
+ }
+ else
+ {
+ var domPopupSearchResults = this.DOMPopupSearchResults();
+ var left = getXPos(domSearchBox) + 150; // domSearchBox.offsetWidth;
+ var top = getYPos(domSearchBox) + 20; // domSearchBox.offsetHeight + 1;
+ domPopupSearchResultsWindow.style.display = 'block';
+ left -= domPopupSearchResults.offsetWidth;
+ domPopupSearchResultsWindow.style.top = top + 'px';
+ domPopupSearchResultsWindow.style.left = left + 'px';
+ }
+ }
+
+ this.lastSearchValue = searchValue;
+ this.lastResultsPage = resultsPage;
+ }
+
+ // -------- Activation Functions
+
+ // Activates or deactivates the search panel, resetting things to
+ // their default values if necessary.
+ this.Activate = function(isActive)
+ {
+ if (isActive || // open it
+ this.DOMPopupSearchResultsWindow().style.display == 'block'
+ )
+ {
+ this.DOMSearchBox().className = 'MSearchBoxActive';
+
+ var searchField = this.DOMSearchField();
+
+ if (searchField.value == this.searchLabel) // clear "Search" term upon entry
+ {
+ searchField.value = '';
+ this.searchActive = true;
+ }
+ }
+ else if (!isActive) // directly remove the panel
+ {
+ this.DOMSearchBox().className = 'MSearchBoxInactive';
+ this.DOMSearchField().value = this.searchLabel;
+ this.searchActive = false;
+ this.lastSearchValue = ''
+ this.lastResultsPage = '';
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+// The class that handles everything on the search results page.
+function SearchResults(name)
+{
+ // The number of matches from the last run of <Search()>.
+ this.lastMatchCount = 0;
+ this.lastKey = 0;
+ this.repeatOn = false;
+
+ // Toggles the visibility of the passed element ID.
+ this.FindChildElement = function(id)
+ {
+ var parentElement = document.getElementById(id);
+ var element = parentElement.firstChild;
+
+ while (element && element!=parentElement)
+ {
+ if (element.nodeName == 'DIV' && element.className == 'SRChildren')
+ {
+ return element;
+ }
+
+ if (element.nodeName == 'DIV' && element.hasChildNodes())
+ {
+ element = element.firstChild;
+ }
+ else if (element.nextSibling)
+ {
+ element = element.nextSibling;
+ }
+ else
+ {
+ do
+ {
+ element = element.parentNode;
+ }
+ while (element && element!=parentElement && !element.nextSibling);
+
+ if (element && element!=parentElement)
+ {
+ element = element.nextSibling;
+ }
+ }
+ }
+ }
+
+ this.Toggle = function(id)
+ {
+ var element = this.FindChildElement(id);
+ if (element)
+ {
+ if (element.style.display == 'block')
+ {
+ element.style.display = 'none';
+ }
+ else
+ {
+ element.style.display = 'block';
+ }
+ }
+ }
+
+ // Searches for the passed string. If there is no parameter,
+ // it takes it from the URL query.
+ //
+ // Always returns true, since other documents may try to call it
+ // and that may or may not be possible.
+ this.Search = function(search)
+ {
+ if (!search) // get search word from URL
+ {
+ search = window.location.search;
+ search = search.substring(1); // Remove the leading '?'
+ search = unescape(search);
+ }
+
+ search = search.replace(/^ +/, ""); // strip leading spaces
+ search = search.replace(/ +$/, ""); // strip trailing spaces
+ search = search.toLowerCase();
+ search = convertToId(search);
+
+ var resultRows = document.getElementsByTagName("div");
+ var matches = 0;
+
+ var i = 0;
+ while (i < resultRows.length)
+ {
+ var row = resultRows.item(i);
+ if (row.className == "SRResult")
+ {
+ var rowMatchName = row.id.toLowerCase();
+ rowMatchName = rowMatchName.replace(/^sr\d*_/, ''); // strip 'sr123_'
+
+ if (search.length<=rowMatchName.length &&
+ rowMatchName.substr(0, search.length)==search)
+ {
+ row.style.display = 'block';
+ matches++;
+ }
+ else
+ {
+ row.style.display = 'none';
+ }
+ }
+ i++;
+ }
+ document.getElementById("Searching").style.display='none';
+ if (matches == 0) // no results
+ {
+ document.getElementById("NoMatches").style.display='block';
+ }
+ else // at least one result
+ {
+ document.getElementById("NoMatches").style.display='none';
+ }
+ this.lastMatchCount = matches;
+ return true;
+ }
+
+ // return the first item with index index or higher that is visible
+ this.NavNext = function(index)
+ {
+ var focusItem;
+ while (1)
+ {
+ var focusName = 'Item'+index;
+ focusItem = document.getElementById(focusName);
+ if (focusItem && focusItem.parentNode.parentNode.style.display=='block')
+ {
+ break;
+ }
+ else if (!focusItem) // last element
+ {
+ break;
+ }
+ focusItem=null;
+ index++;
+ }
+ return focusItem;
+ }
+
+ this.NavPrev = function(index)
+ {
+ var focusItem;
+ while (1)
+ {
+ var focusName = 'Item'+index;
+ focusItem = document.getElementById(focusName);
+ if (focusItem && focusItem.parentNode.parentNode.style.display=='block')
+ {
+ break;
+ }
+ else if (!focusItem) // last element
+ {
+ break;
+ }
+ focusItem=null;
+ index--;
+ }
+ return focusItem;
+ }
+
+ this.ProcessKeys = function(e)
+ {
+ if (e.type == "keydown")
+ {
+ this.repeatOn = false;
+ this.lastKey = e.keyCode;
+ }
+ else if (e.type == "keypress")
+ {
+ if (!this.repeatOn)
+ {
+ if (this.lastKey) this.repeatOn = true;
+ return false; // ignore first keypress after keydown
+ }
+ }
+ else if (e.type == "keyup")
+ {
+ this.lastKey = 0;
+ this.repeatOn = false;
+ }
+ return this.lastKey!=0;
+ }
+
+ this.Nav = function(evt,itemIndex)
+ {
+ var e = (evt) ? evt : window.event; // for IE
+ if (e.keyCode==13) return true;
+ if (!this.ProcessKeys(e)) return false;
+
+ if (this.lastKey==38) // Up
+ {
+ var newIndex = itemIndex-1;
+ var focusItem = this.NavPrev(newIndex);
+ if (focusItem)
+ {
+ var child = this.FindChildElement(focusItem.parentNode.parentNode.id);
+ if (child && child.style.display == 'block') // children visible
+ {
+ var n=0;
+ var tmpElem;
+ while (1) // search for last child
+ {
+ tmpElem = document.getElementById('Item'+newIndex+'_c'+n);
+ if (tmpElem)
+ {
+ focusItem = tmpElem;
+ }
+ else // found it!
+ {
+ break;
+ }
+ n++;
+ }
+ }
+ }
+ if (focusItem)
+ {
+ focusItem.focus();
+ }
+ else // return focus to search field
+ {
+ parent.document.getElementById("MSearchField").focus();
+ }
+ }
+ else if (this.lastKey==40) // Down
+ {
+ var newIndex = itemIndex+1;
+ var focusItem;
+ var item = document.getElementById('Item'+itemIndex);
+ var elem = this.FindChildElement(item.parentNode.parentNode.id);
+ if (elem && elem.style.display == 'block') // children visible
+ {
+ focusItem = document.getElementById('Item'+itemIndex+'_c0');
+ }
+ if (!focusItem) focusItem = this.NavNext(newIndex);
+ if (focusItem) focusItem.focus();
+ }
+ else if (this.lastKey==39) // Right
+ {
+ var item = document.getElementById('Item'+itemIndex);
+ var elem = this.FindChildElement(item.parentNode.parentNode.id);
+ if (elem) elem.style.display = 'block';
+ }
+ else if (this.lastKey==37) // Left
+ {
+ var item = document.getElementById('Item'+itemIndex);
+ var elem = this.FindChildElement(item.parentNode.parentNode.id);
+ if (elem) elem.style.display = 'none';
+ }
+ else if (this.lastKey==27) // Escape
+ {
+ parent.searchBox.CloseResultsWindow();
+ parent.document.getElementById("MSearchField").focus();
+ }
+ else if (this.lastKey==13) // Enter
+ {
+ return true;
+ }
+ return false;
+ }
+
+ this.NavChild = function(evt,itemIndex,childIndex)
+ {
+ var e = (evt) ? evt : window.event; // for IE
+ if (e.keyCode==13) return true;
+ if (!this.ProcessKeys(e)) return false;
+
+ if (this.lastKey==38) // Up
+ {
+ if (childIndex>0)
+ {
+ var newIndex = childIndex-1;
+ document.getElementById('Item'+itemIndex+'_c'+newIndex).focus();
+ }
+ else // already at first child, jump to parent
+ {
+ document.getElementById('Item'+itemIndex).focus();
+ }
+ }
+ else if (this.lastKey==40) // Down
+ {
+ var newIndex = childIndex+1;
+ var elem = document.getElementById('Item'+itemIndex+'_c'+newIndex);
+ if (!elem) // last child, jump to parent next parent
+ {
+ elem = this.NavNext(itemIndex+1);
+ }
+ if (elem)
+ {
+ elem.focus();
+ }
+ }
+ else if (this.lastKey==27) // Escape
+ {
+ parent.searchBox.CloseResultsWindow();
+ parent.document.getElementById("MSearchField").focus();
+ }
+ else if (this.lastKey==13) // Enter
+ {
+ return true;
+ }
+ return false;
+ }
+}
+
+function setKeyActions(elem,action)
+{
+ elem.setAttribute('onkeydown',action);
+ elem.setAttribute('onkeypress',action);
+ elem.setAttribute('onkeyup',action);
+}
+
+function setClassAttr(elem,attr)
+{
+ elem.setAttribute('class',attr);
+ elem.setAttribute('className',attr);
+}
+
+function createResults()
+{
+ var results = document.getElementById("SRResults");
+ for (var e=0; e<searchData.length; e++)
+ {
+ var id = searchData[e][0];
+ var srResult = document.createElement('div');
+ srResult.setAttribute('id','SR_'+id);
+ setClassAttr(srResult,'SRResult');
+ var srEntry = document.createElement('div');
+ setClassAttr(srEntry,'SREntry');
+ var srLink = document.createElement('a');
+ srLink.setAttribute('id','Item'+e);
+ setKeyActions(srLink,'return searchResults.Nav(event,'+e+')');
+ setClassAttr(srLink,'SRSymbol');
+ srLink.innerHTML = searchData[e][1][0];
+ srEntry.appendChild(srLink);
+ if (searchData[e][1].length==2) // single result
+ {
+ srLink.setAttribute('href',searchData[e][1][1][0]);
+ if (searchData[e][1][1][1])
+ {
+ srLink.setAttribute('target','_parent');
+ }
+ var srScope = document.createElement('span');
+ setClassAttr(srScope,'SRScope');
+ srScope.innerHTML = searchData[e][1][1][2];
+ srEntry.appendChild(srScope);
+ }
+ else // multiple results
+ {
+ srLink.setAttribute('href','javascript:searchResults.Toggle("SR_'+id+'")');
+ var srChildren = document.createElement('div');
+ setClassAttr(srChildren,'SRChildren');
+ for (var c=0; c<searchData[e][1].length-1; c++)
+ {
+ var srChild = document.createElement('a');
+ srChild.setAttribute('id','Item'+e+'_c'+c);
+ setKeyActions(srChild,'return searchResults.NavChild(event,'+e+','+c+')');
+ setClassAttr(srChild,'SRScope');
+ srChild.setAttribute('href',searchData[e][1][c+1][0]);
+ if (searchData[e][1][c+1][1])
+ {
+ srChild.setAttribute('target','_parent');
+ }
+ srChild.innerHTML = searchData[e][1][c+1][2];
+ srChildren.appendChild(srChild);
+ }
+ srEntry.appendChild(srChildren);
+ }
+ srResult.appendChild(srEntry);
+ results.appendChild(srResult);
+ }
+}
+
+function init_search()
+{
+ var results = document.getElementById("MSearchSelectWindow");
+ for (var key in indexSectionLabels)
+ {
+ var link = document.createElement('a');
+ link.setAttribute('class','SelectItem');
+ link.setAttribute('onclick','searchBox.OnSelectItem('+key+')');
+ link.href='javascript:void(0)';
+ link.innerHTML='<span class="SelectionMark"> </span>'+indexSectionLabels[key];
+ results.appendChild(link);
+ }
+ searchBox.OnSelectItem(0);
+}
+/* @license-end */
--- /dev/null
+var indexSectionsWithContent =
+{
+ 0: "_abcehmoprtuz",
+ 1: "m",
+ 2: "m",
+ 3: "bcru",
+ 4: "m",
+ 5: "m",
+ 6: "_m",
+ 7: "abcehprtz",
+ 8: "beopu"
+};
+
+var indexSectionNames =
+{
+ 0: "all",
+ 1: "classes",
+ 2: "functions",
+ 3: "variables",
+ 4: "typedefs",
+ 5: "enums",
+ 6: "enumvalues",
+ 7: "groups",
+ 8: "pages"
+};
+
+var indexSectionLabels =
+{
+ 0: "All",
+ 1: "Data Structures",
+ 2: "Functions",
+ 3: "Variables",
+ 4: "Typedefs",
+ 5: "Enumerations",
+ 6: "Enumerator",
+ 7: "Modules",
+ 8: "Pages"
+};
+
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="typedefs_0.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['mi_5fblock_5fvisit_5ffun',['mi_block_visit_fun',['../group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65',1,'mimalloc-doc.h']]],
+ ['mi_5fdeferred_5ffree_5ffun',['mi_deferred_free_fun',['../group__extended.html#ga299dae78d25ce112e384a98b7309c5be',1,'mimalloc-doc.h']]],
+ ['mi_5ferror_5ffun',['mi_error_fun',['../group__extended.html#ga251d369cda3f1c2a955c555486ed90e5',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5ft',['mi_heap_t',['../group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2',1,'mimalloc-doc.h']]],
+ ['mi_5foutput_5ffun',['mi_output_fun',['../group__extended.html#gad823d23444a4b77a40f66bf075a98a0c',1,'mimalloc-doc.h']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="typedefs_1.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['heartbeat',['heartbeat',['../group__extended.html#ga411f6e94394a2400aa460c796beff8d8',1,'mimalloc-doc.h']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="typedefs_2.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['mi_5fblock_5fvisit_5ffun',['mi_block_visit_fun',['../group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65',1,'mimalloc-doc.h']]],
+ ['mi_5fheap_5ft',['mi_heap_t',['../group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2',1,'mimalloc-doc.h']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="variables_0.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['block_5fsize',['block_size',['../group__analysis.html#a332a6c14d736a99699d5453a1cb04b41',1,'mi_heap_area_t']]],
+ ['blocks',['blocks',['../group__analysis.html#ae0085e6e1cf059a4eb7767e30e9991b8',1,'mi_heap_area_t']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="variables_1.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['committed',['committed',['../group__analysis.html#ab47526df656d8837ec3e97f11b83f835',1,'mi_heap_area_t']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="variables_2.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['reserved',['reserved',['../group__analysis.html#ae848a3e6840414891035423948ca0383',1,'mi_heap_area_t']]]
+];
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="variables_3.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
--- /dev/null
+var searchData=
+[
+ ['used',['used',['../group__analysis.html#ab820302c5cd0df133eb8e51650a008b4',1,'mi_heap_area_t']]]
+];
--- /dev/null
+.tabs, .tabs2, .tabs3 {
+ background-image: url('tab_b.png');
+ width: 100%;
+ z-index: 101;
+ font-size: 13px;
+ font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif;
+}
+
+.tabs2 {
+ font-size: 10px;
+}
+.tabs3 {
+ font-size: 9px;
+}
+
+.tablist {
+ margin: 0;
+ padding: 0;
+ display: table;
+}
+
+.tablist li {
+ float: left;
+ display: table-cell;
+ background-image: url('tab_b.png');
+ line-height: 36px;
+ list-style: none;
+}
+
+.tablist a {
+ display: block;
+ padding: 0 20px;
+ font-weight: bold;
+ background-image:url('tab_s.png');
+ background-repeat:no-repeat;
+ background-position:right;
+ color: #040404;
+ text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);
+ text-decoration: none;
+ outline: none;
+}
+
+.tabs3 .tablist a {
+ padding: 0 10px;
+}
+
+.tablist a:hover {
+ background-image: url('tab_h.png');
+ background-repeat:repeat-x;
+ color: #fff;
+ text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0);
+ text-decoration: none;
+}
+
+.tablist li.current a {
+ background-image: url('tab_a.png');
+ background-repeat:repeat-x;
+ color: #fff;
+ text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0);
+}
+
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen 1.8.15"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<title>mi-malloc: Using the library</title>
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="dynsections.js"></script>
+<link href="navtree.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="resize.js"></script>
+<script type="text/javascript" src="navtreedata.js"></script>
+<script type="text/javascript" src="navtree.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(initResizable);
+/* @license-end */</script>
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="search/searchdata.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+ $(document).ready(function() { init_search(); });
+/* @license-end */
+</script>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
+ <td id="projectalign" style="padding-left: 0.5em;">
+ <div id="projectname">mi-malloc
+  <span id="projectnumber">1.6</span>
+ </div>
+ </td>
+ <td> <div id="MSearchBox" class="MSearchBoxInactive">
+ <span class="left">
+ <img id="MSearchSelect" src="search/mag_sel.png"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ alt=""/>
+ <input type="text" id="MSearchField" value="Search" accesskey="S"
+ onfocus="searchBox.OnSearchFieldFocus(true)"
+ onblur="searchBox.OnSearchFieldFocus(false)"
+ onkeyup="searchBox.OnSearchFieldChange(event)"/>
+ </span><span class="right">
+ <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
+ </span>
+ </div>
+</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<!-- end header part -->
+<!-- Generated by Doxygen 1.8.15 -->
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+var searchBox = new SearchBox("searchBox", "search",false,'Search');
+/* @license-end */
+</script>
+</div><!-- top -->
+<div id="side-nav" class="ui-resizable side-nav-resizable">
+ <div id="nav-tree">
+ <div id="nav-tree-contents">
+ <div id="nav-sync" class="sync"></div>
+ </div>
+ </div>
+ <div id="splitbar" style="-moz-user-select:none;"
+ class="ui-resizable-handle">
+ </div>
+</div>
+<script type="text/javascript">
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
+$(document).ready(function(){initNavTree('using.html','');});
+/* @license-end */
+</script>
+<div id="doc-content">
+<!-- window showing the filter options -->
+<div id="MSearchSelectWindow"
+ onmouseover="return searchBox.OnSearchSelectShow()"
+ onmouseout="return searchBox.OnSearchSelectHide()"
+ onkeydown="return searchBox.OnSearchSelectKey(event)">
+</div>
+
+<!-- iframe showing the search results (closed by default) -->
+<div id="MSearchResultsWindow">
+<iframe src="javascript:void(0)" frameborder="0"
+ name="MSearchResults" id="MSearchResults">
+</iframe>
+</div>
+
+<div class="PageDoc"><div class="header">
+ <div class="headertitle">
+<div class="title">Using the library </div> </div>
+</div><!--header-->
+<div class="contents">
+<div class="textblock"><h3>Build</h3>
+<p>The preferred usage is including <code><mimalloc.h></code>, linking with the shared- or static library, and using the <code>mi_malloc</code> API exclusively for allocation. For example, </p><div class="fragment"><div class="line">gcc -o myprogram -lmimalloc myfile.c</div></div><!-- fragment --><p>mimalloc uses only safe OS calls (<code>mmap</code> and <code>VirtualAlloc</code>) and can co-exist with other allocators linked to the same program. If you use <code>cmake</code>, you can simply use: </p><div class="fragment"><div class="line">find_package(mimalloc 1.0 REQUIRED)</div></div><!-- fragment --><p> in your <code>CMakeLists.txt</code> to find a locally installed mimalloc. Then use either: </p><div class="fragment"><div class="line">target_link_libraries(myapp PUBLIC mimalloc)</div></div><!-- fragment --><p> to link with the shared (dynamic) library, or: </p><div class="fragment"><div class="line">target_link_libraries(myapp PUBLIC mimalloc-<span class="keyword">static</span>)</div></div><!-- fragment --><p> to link with the static library. See <code>test\CMakeLists.txt</code> for an example.</p>
+<h3>C++</h3>
+<p>For best performance in C++ programs, it is also recommended to override the global <code>new</code> and <code>delete</code> operators. For convience, mimalloc provides <a href="https://github.com/microsoft/mimalloc/blob/master/include/mimalloc-new-delete.h"><code>mimalloc-new-delete.h</code></a> which does this for you – just include it in a single(!) source file in your project.</p>
+<p>In C++, mimalloc also provides the <code><a class="el" href="group__cpp.html#structmi__stl__allocator" title="std::allocator implementation for mimalloc for use in STL containers.">mi_stl_allocator</a></code> struct which implements the <code>std::allocator</code> interface. For example: </p><div class="fragment"><div class="line">std::vector<some_struct, mi_stl_allocator<some_struct>> vec;</div><div class="line">vec.push_back(some_struct());</div></div><!-- fragment --><h3>Statistics</h3>
+<p>You can pass environment variables to print verbose messages (<code>MIMALLOC_VERBOSE=1</code>) and statistics (<code>MIMALLOC_SHOW_STATS=1</code>) (in the debug version): </p><div class="fragment"><div class="line">> env MIMALLOC_SHOW_STATS=1 ./cfrac 175451865205073170563711388363</div><div class="line"></div><div class="line">175451865205073170563711388363 = 374456281610909315237213 * 468551</div><div class="line"></div><div class="line">heap stats: peak total freed unit</div><div class="line">normal 2: 16.4 kb 17.5 mb 17.5 mb 16 b ok</div><div class="line">normal 3: 16.3 kb 15.2 mb 15.2 mb 24 b ok</div><div class="line">normal 4: 64 b 4.6 kb 4.6 kb 32 b ok</div><div class="line">normal 5: 80 b 118.4 kb 118.4 kb 40 b ok</div><div class="line">normal 6: 48 b 48 b 48 b 48 b ok</div><div class="line">normal 17: 960 b 960 b 960 b 320 b ok</div><div class="line"></div><div class="line">heap stats: peak total freed unit</div><div class="line"> normal: 33.9 kb 32.8 mb 32.8 mb 1 b ok</div><div class="line"> huge: 0 b 0 b 0 b 1 b ok</div><div class="line"> total: 33.9 kb 32.8 mb 32.8 mb 1 b ok</div><div class="line">malloc requested: 32.8 mb</div><div class="line"></div><div class="line"> committed: 58.2 kb 58.2 kb 58.2 kb 1 b ok</div><div class="line"> reserved: 2.0 mb 2.0 mb 2.0 mb 1 b ok</div><div class="line"> reset: 0 b 0 b 0 b 1 b ok</div><div class="line"> segments: 1 1 1</div><div class="line">-abandoned: 0</div><div class="line"> pages: 6 6 6</div><div class="line">-abandoned: 0</div><div class="line"> mmaps: 3</div><div class="line"> mmap fast: 0</div><div class="line"> mmap slow: 1</div><div class="line"> threads: 0</div><div class="line"> elapsed: 2.022s</div><div class="line"> process: user: 1.781s, system: 0.016s, faults: 756, reclaims: 0, rss: 2.7 mb</div></div><!-- fragment --><p>The above model of using the <code>mi_</code> prefixed API is not always possible though in existing programs that already use the standard malloc interface, and another option is to override the standard malloc interface completely and redirect all calls to the <em>mimalloc</em> library instead.</p>
+<p>See <a class="el" href="overrides.html">Overriding Malloc</a> for more info. </p>
+</div></div><!-- PageDoc -->
+</div><!-- contents -->
+</div><!-- doc-content -->
+<!-- start footer part -->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ <li class="footer">Generated by
+ <a href="http://www.doxygen.org/index.html">
+ <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{FEF7868F-750E-4C21-A04D-22707CC66879}</ProjectGuid>
+ <RootNamespace>mimalloc-override-test</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
+ <ProjectName>mimalloc-override-test</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <ExceptionHandling>false</ExceptionHandling>
+ <CompileAs>Default</CompileAs>
+ <SupportJustMyCode>false</SupportJustMyCode>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <PostBuildEvent />
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <CompileAs>Default</CompileAs>
+ <SupportJustMyCode>false</SupportJustMyCode>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EntryPointSymbol>
+ </EntryPointSymbol>
+ <AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <PostBuildEvent />
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ <EntryPointSymbol>
+ </EntryPointSymbol>
+ <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="mimalloc-override.vcxproj">
+ <Project>{abb5eae7-b3e6-432e-b636-333449892ea7}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\test\main-override.cpp" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\test\main-override.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{ABB5EAE7-B3E6-432E-B636-333449892EA7}</ProjectGuid>
+ <RootNamespace>mimalloc-override</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
+ <ProjectName>mimalloc-override</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.dll</TargetExt>
+ <TargetName>mimalloc-override</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.dll</TargetExt>
+ <TargetName>mimalloc-override</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.dll</TargetExt>
+ <TargetName>mimalloc-override</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.dll</TargetExt>
+ <TargetName>mimalloc-override</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <SupportJustMyCode>false</SupportJustMyCode>
+ <CompileAs>Default</CompileAs>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <IgnoreSpecificDefaultLibraries>
+ </IgnoreSpecificDefaultLibraries>
+ <ModuleDefinitionFile>
+ </ModuleDefinitionFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
+ </Link>
+ <PostBuildEvent>
+ <Command>COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect32.dll $(OutputPath)</Command>
+ </PostBuildEvent>
+ <PostBuildEvent>
+ <Message>Copy mimalloc-redirect32.dll to the output directory</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <SupportJustMyCode>false</SupportJustMyCode>
+ <CompileAs>Default</CompileAs>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(ProjectDir)\..\..\bin\mimalloc-redirect.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <IgnoreSpecificDefaultLibraries>
+ </IgnoreSpecificDefaultLibraries>
+ <ModuleDefinitionFile>
+ </ModuleDefinitionFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
+ </Link>
+ <PostBuildEvent>
+ <Command>COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect.dll $(OutputPath)</Command>
+ </PostBuildEvent>
+ <PostBuildEvent>
+ <Message>copy mimalloc-redirect.dll to the output directory</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <CompileAs>Default</CompileAs>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalDependencies>$(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ModuleDefinitionFile>
+ </ModuleDefinitionFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
+ </Link>
+ <PostBuildEvent>
+ <Command>COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect32.dll $(OutputPath)</Command>
+ </PostBuildEvent>
+ <PostBuildEvent>
+ <Message>Copy mimalloc-redirect32.dll to the output directory</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <CompileAs>Default</CompileAs>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalDependencies>$(ProjectDir)\..\..\bin\mimalloc-redirect.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ModuleDefinitionFile>
+ </ModuleDefinitionFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
+ </Link>
+ <PostBuildEvent>
+ <Command>COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect.dll $(OutputPath)</Command>
+ </PostBuildEvent>
+ <PostBuildEvent>
+ <Message>copy mimalloc-redirect.dll to the output directory</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h" />
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc.h" />
+ <ClInclude Include="..\..\include\mimalloc-atomic.h" />
+ <ClInclude Include="..\..\include\mimalloc-new-delete.h" />
+ <ClInclude Include="..\..\include\mimalloc-override.h" />
+ <ClInclude Include="..\..\include\mimalloc-types.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\src\alloc-aligned.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-override.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-posix.c" />
+ <ClCompile Include="..\..\src\alloc.c" />
+ <ClCompile Include="..\..\src\arena.c" />
+ <ClCompile Include="..\..\src\heap.c" />
+ <ClCompile Include="..\..\src\init.c" />
+ <ClCompile Include="..\..\src\region.c" />
+ <ClCompile Include="..\..\src\options.c" />
+ <ClCompile Include="..\..\src\os.c" />
+ <ClCompile Include="..\..\src\page-queue.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\page.c" />
+ <ClCompile Include="..\..\src\random.c" />
+ <ClCompile Include="..\..\src\segment.c" />
+ <ClCompile Include="..\..\src\stats.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\mimalloc-types.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\mimalloc-atomic.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\mimalloc-override.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\mimalloc-new-delete.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\src\alloc.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\os.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\page.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\page-queue.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\segment.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\stats.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-aligned.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\heap.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\options.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\init.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\region.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-override.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-posix.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\arena.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\random.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{FEF7958F-750E-4C21-A04D-22707CC66878}</ProjectGuid>
+ <RootNamespace>mimalloc-test-stress</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
+ <ProjectName>mimalloc-test-stress</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\test\test-stress.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="mimalloc.vcxproj">
+ <Project>{abb5eae7-b3e6-432e-b636-333449892ea6}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\test\test-stress.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{FEF7858F-750E-4C21-A04D-22707CC66878}</ProjectGuid>
+ <RootNamespace>mimalloctest</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
+ <ProjectName>mimalloc-test</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="mimalloc.vcxproj">
+ <Project>{abb5eae7-b3e6-432e-b636-333449892ea6}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\test\main-override-static.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\test\main-override-static.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+\r
+Microsoft Visual Studio Solution File, Format Version 12.00\r
+# Visual Studio 15\r
+VisualStudioVersion = 15.0.28010.2016\r
+MinimumVisualStudioVersion = 10.0.40219.1\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc", "mimalloc.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA6}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test", "mimalloc-test.vcxproj", "{FEF7858F-750E-4C21-A04D-22707CC66878}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override", "mimalloc-override.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA7}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override-test", "mimalloc-override-test.vcxproj", "{FEF7868F-750E-4C21-A04D-22707CC66879}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-stress", "mimalloc-test-stress.vcxproj", "{FEF7958F-750E-4C21-A04D-22707CC66878}"\r
+EndProject\r
+Global\r
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+ Debug|x64 = Debug|x64\r
+ Debug|x86 = Debug|x86\r
+ Release|x64 = Release|x64\r
+ Release|x86 = Release|x86\r
+ EndGlobalSection\r
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.ActiveCfg = Debug|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.Build.0 = Debug|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.Build.0 = Debug|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.ActiveCfg = Release|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.Build.0 = Release|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.ActiveCfg = Release|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.Build.0 = Release|Win32\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.ActiveCfg = Debug|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.Build.0 = Debug|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.Build.0 = Debug|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.ActiveCfg = Release|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.Build.0 = Release|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.ActiveCfg = Release|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.Build.0 = Release|Win32\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.ActiveCfg = Debug|x64\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.Build.0 = Debug|x64\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.Build.0 = Debug|Win32\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.ActiveCfg = Release|x64\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.Build.0 = Release|x64\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.ActiveCfg = Release|Win32\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.Build.0 = Release|Win32\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32\r
+ EndGlobalSection\r
+ GlobalSection(SolutionProperties) = preSolution\r
+ HideSolutionNode = FALSE\r
+ EndGlobalSection\r
+ GlobalSection(ExtensibilityGlobals) = postSolution\r
+ SolutionGuid = {4297F93D-486A-4243-995F-7D32F59AE82A}\r
+ EndGlobalSection\r
+EndGlobal\r
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{ABB5EAE7-B3E6-432E-B636-333449892EA6}</ProjectGuid>
+ <RootNamespace>mimalloc</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
+ <ProjectName>mimalloc</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.lib</TargetExt>
+ <TargetName>mimalloc-static</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.lib</TargetExt>
+ <TargetName>mimalloc-static</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.lib</TargetExt>
+ <TargetName>mimalloc-static</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.lib</TargetExt>
+ <TargetName>mimalloc-static</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Label="LLVM" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <UseLlvmLib>false</UseLlvmLib>
+ </PropertyGroup>
+ <PropertyGroup Label="LLVM" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <UseLlvmLib>false</UseLlvmLib>
+ </PropertyGroup>
+ <PropertyGroup Label="LLVM" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <UseLlvmLib>false</UseLlvmLib>
+ </PropertyGroup>
+ <PropertyGroup Label="LLVM" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <UseLlvmLib>false</UseLlvmLib>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MI_DEBUG=3;%(PreprocessorDefinitions);</PreprocessorDefinitions>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <SupportJustMyCode>false</SupportJustMyCode>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Lib>
+ <AdditionalLibraryDirectories>
+ </AdditionalLibraryDirectories>
+ <AdditionalDependencies>
+ </AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MI_DEBUG=3;%(PreprocessorDefinitions);</PreprocessorDefinitions>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <SupportJustMyCode>false</SupportJustMyCode>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ <Link>
+ <EntryPointSymbol>
+ </EntryPointSymbol>
+ </Link>
+ <Lib>
+ <AdditionalLibraryDirectories>
+ </AdditionalLibraryDirectories>
+ <AdditionalDependencies>
+ </AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ <Lib>
+ <AdditionalLibraryDirectories>
+ </AdditionalLibraryDirectories>
+ <AdditionalDependencies>
+ </AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EntryPointSymbol>
+ </EntryPointSymbol>
+ </Link>
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ <Lib>
+ <AdditionalLibraryDirectories>
+ </AdditionalLibraryDirectories>
+ <AdditionalDependencies>
+ </AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\src\alloc-aligned.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-override-osx.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-override.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-posix.c" />
+ <ClCompile Include="..\..\src\alloc.c" />
+ <ClCompile Include="..\..\src\arena.c" />
+ <ClCompile Include="..\..\src\heap.c" />
+ <ClCompile Include="..\..\src\init.c" />
+ <ClCompile Include="..\..\src\region.c" />
+ <ClCompile Include="..\..\src\options.c" />
+ <ClCompile Include="..\..\src\page-queue.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\page.c" />
+ <ClCompile Include="..\..\src\random.c" />
+ <ClCompile Include="..\..\src\segment.c" />
+ <ClCompile Include="..\..\src\os.c" />
+ <ClCompile Include="..\..\src\stats.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h" />
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc.h" />
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-atomic.h" />
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-override.h" />
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-types.h" />
+ <ClInclude Include="..\..\include\mimalloc-new-delete.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\src\stats.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\os.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\page.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\segment.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-aligned.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\page-queue.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\heap.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-override-osx.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-override.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\options.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\init.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\region.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-posix.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\arena.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\random.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-atomic.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-override.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-types.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\mimalloc-new-delete.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{FEF7868F-750E-4C21-A04D-22707CC66879}</ProjectGuid>
+ <RootNamespace>mimalloc-override-test</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ <ProjectName>mimalloc-override-test</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <CompileAs>Default</CompileAs>
+ <SupportJustMyCode>false</SupportJustMyCode>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <PostBuildEvent />
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <CompileAs>Default</CompileAs>
+ <SupportJustMyCode>false</SupportJustMyCode>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EntryPointSymbol>
+ </EntryPointSymbol>
+ <AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <PostBuildEvent />
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ <EntryPointSymbol>
+ </EntryPointSymbol>
+ <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\test\main-override.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="mimalloc-override.vcxproj">
+ <Project>{abb5eae7-b3e6-432e-b636-333449892ea7}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{ABB5EAE7-B3E6-432E-B636-333449892EA7}</ProjectGuid>
+ <RootNamespace>mimalloc-override</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ <ProjectName>mimalloc-override</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.dll</TargetExt>
+ <TargetName>mimalloc-override</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.dll</TargetExt>
+ <TargetName>mimalloc-override</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.dll</TargetExt>
+ <TargetName>mimalloc-override</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.dll</TargetExt>
+ <TargetName>mimalloc-override</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <SupportJustMyCode>false</SupportJustMyCode>
+ <CompileAs>Default</CompileAs>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <IgnoreSpecificDefaultLibraries>
+ </IgnoreSpecificDefaultLibraries>
+ <ModuleDefinitionFile>
+ </ModuleDefinitionFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
+ </Link>
+ <PostBuildEvent>
+ <Command>COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect32.dll $(OutputPath)</Command>
+ </PostBuildEvent>
+ <PostBuildEvent>
+ <Message>Copy mimalloc-redirect32.dll to the output directory</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>MI_DEBUG=3;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <SupportJustMyCode>false</SupportJustMyCode>
+ <CompileAs>Default</CompileAs>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(ProjectDir)\..\..\bin\mimalloc-redirect.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <IgnoreSpecificDefaultLibraries>
+ </IgnoreSpecificDefaultLibraries>
+ <ModuleDefinitionFile>
+ </ModuleDefinitionFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
+ </Link>
+ <PostBuildEvent>
+ <Command>COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect.dll $(OutputPath)</Command>
+ </PostBuildEvent>
+ <PostBuildEvent>
+ <Message>copy mimalloc-redirect.dll to the output directory</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <CompileAs>Default</CompileAs>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalDependencies>$(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ModuleDefinitionFile>
+ </ModuleDefinitionFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
+ </Link>
+ <PostBuildEvent>
+ <Command>COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect32.dll $(OutputPath)</Command>
+ </PostBuildEvent>
+ <PostBuildEvent>
+ <Message>Copy mimalloc-redirect32.dll to the output directory</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <CompileAs>Default</CompileAs>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalDependencies>$(ProjectDir)\..\..\bin\mimalloc-redirect.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ModuleDefinitionFile>
+ </ModuleDefinitionFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
+ </Link>
+ <PostBuildEvent>
+ <Command>COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect.dll $(OutputPath)</Command>
+ </PostBuildEvent>
+ <PostBuildEvent>
+ <Message>copy mimalloc-redirect.dll to the output directory</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h" />
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc.h" />
+ <ClInclude Include="..\..\include\mimalloc-atomic.h" />
+ <ClInclude Include="..\..\include\mimalloc-new-delete.h" />
+ <ClInclude Include="..\..\include\mimalloc-override.h" />
+ <ClInclude Include="..\..\include\mimalloc-types.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\src\alloc-aligned.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-override.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-posix.c" />
+ <ClCompile Include="..\..\src\alloc.c" />
+ <ClCompile Include="..\..\src\arena.c" />
+ <ClCompile Include="..\..\src\bitmap.inc.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\heap.c" />
+ <ClCompile Include="..\..\src\init.c" />
+ <ClCompile Include="..\..\src\region.c" />
+ <ClCompile Include="..\..\src\options.c" />
+ <ClCompile Include="..\..\src\os.c" />
+ <ClCompile Include="..\..\src\page-queue.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\page.c" />
+ <ClCompile Include="..\..\src\random.c" />
+ <ClCompile Include="..\..\src\segment.c" />
+ <ClCompile Include="..\..\src\stats.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="..\..\src\alloc.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-aligned.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-override.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-posix.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\heap.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\init.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\region.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\os.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\page.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\page-queue.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\segment.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\stats.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\arena.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\bitmap.inc.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\random.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\options.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\mimalloc-atomic.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\mimalloc-new-delete.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\mimalloc-override.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\mimalloc-types.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{f1fccf27-17b9-42dd-ba51-6070baff85c6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{39cb7e38-69d0-43fb-8406-6a0f7cefc3b4}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{FFF7958F-750E-4C21-A04D-22707CC66878}</ProjectGuid>
+ <RootNamespace>mimalloc-test-api</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ <ProjectName>mimalloc-test-api</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\test\test-api.c">
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="mimalloc.vcxproj">
+ <Project>{abb5eae7-b3e6-432e-b636-333449892ea6}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{FEF7958F-750E-4C21-A04D-22707CC66878}</ProjectGuid>
+ <RootNamespace>mimalloc-test-stress</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ <ProjectName>mimalloc-test-stress</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\test\test-stress.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="mimalloc-override.vcxproj">
+ <Project>{abb5eae7-b3e6-432e-b636-333449892ea7}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{FEF7858F-750E-4C21-A04D-22707CC66878}</ProjectGuid>
+ <RootNamespace>mimalloctest</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ <ProjectName>mimalloc-test</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="mimalloc.vcxproj">
+ <Project>{abb5eae7-b3e6-432e-b636-333449892ea6}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\test\main-override-static.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+\r
+Microsoft Visual Studio Solution File, Format Version 12.00\r
+# Visual Studio Version 16\r
+VisualStudioVersion = 16.0.29709.97\r
+MinimumVisualStudioVersion = 10.0.40219.1\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc", "mimalloc.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA6}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test", "mimalloc-test.vcxproj", "{FEF7858F-750E-4C21-A04D-22707CC66878}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override", "mimalloc-override.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA7}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override-test", "mimalloc-override-test.vcxproj", "{FEF7868F-750E-4C21-A04D-22707CC66879}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-stress", "mimalloc-test-stress.vcxproj", "{FEF7958F-750E-4C21-A04D-22707CC66878}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-api", "mimalloc-test-api.vcxproj", "{FFF7958F-750E-4C21-A04D-22707CC66878}"\r
+EndProject\r
+Global\r
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+ Debug|x64 = Debug|x64\r
+ Debug|x86 = Debug|x86\r
+ Release|x64 = Release|x64\r
+ Release|x86 = Release|x86\r
+ EndGlobalSection\r
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.ActiveCfg = Debug|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.Build.0 = Debug|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.Build.0 = Debug|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.ActiveCfg = Release|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.Build.0 = Release|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.ActiveCfg = Release|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.Build.0 = Release|Win32\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32\r
+ {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.ActiveCfg = Debug|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.Build.0 = Debug|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.Build.0 = Debug|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.ActiveCfg = Release|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.Build.0 = Release|x64\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.ActiveCfg = Release|Win32\r
+ {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.Build.0 = Release|Win32\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.ActiveCfg = Debug|x64\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.Build.0 = Debug|x64\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.Build.0 = Debug|Win32\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.ActiveCfg = Release|x64\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.Build.0 = Release|x64\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.ActiveCfg = Release|Win32\r
+ {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.Build.0 = Release|Win32\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32\r
+ {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32\r
+ {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64\r
+ {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64\r
+ {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32\r
+ {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64\r
+ {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64\r
+ {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32\r
+ {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32\r
+ EndGlobalSection\r
+ GlobalSection(SolutionProperties) = preSolution\r
+ HideSolutionNode = FALSE\r
+ EndGlobalSection\r
+ GlobalSection(ExtensibilityGlobals) = postSolution\r
+ SolutionGuid = {4297F93D-486A-4243-995F-7D32F59AE82A}\r
+ EndGlobalSection\r
+EndGlobal\r
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{ABB5EAE7-B3E6-432E-B636-333449892EA6}</ProjectGuid>
+ <RootNamespace>mimalloc</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ <ProjectName>mimalloc</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.lib</TargetExt>
+ <TargetName>mimalloc-static</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.lib</TargetExt>
+ <TargetName>mimalloc-static</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.lib</TargetExt>
+ <TargetName>mimalloc-static</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
+ <TargetExt>.lib</TargetExt>
+ <TargetName>mimalloc-static</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>MI_DEBUG=3;%(PreprocessorDefinitions);</PreprocessorDefinitions>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <SupportJustMyCode>false</SupportJustMyCode>
+ <LanguageStandard>Default</LanguageStandard>
+ </ClCompile>
+ <Lib>
+ <AdditionalLibraryDirectories>
+ </AdditionalLibraryDirectories>
+ <AdditionalDependencies>
+ </AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>MI_DEBUG=3;%(PreprocessorDefinitions);</PreprocessorDefinitions>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <SupportJustMyCode>false</SupportJustMyCode>
+ <LanguageStandard>Default</LanguageStandard>
+ </ClCompile>
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ <Link>
+ <EntryPointSymbol>
+ </EntryPointSymbol>
+ </Link>
+ <Lib>
+ <AdditionalLibraryDirectories>
+ </AdditionalLibraryDirectories>
+ <AdditionalDependencies>
+ </AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <LanguageStandard>Default</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ <Lib>
+ <AdditionalLibraryDirectories>
+ </AdditionalLibraryDirectories>
+ <AdditionalDependencies>
+ </AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
+ <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <LanguageStandard>Default</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EntryPointSymbol>
+ </EntryPointSymbol>
+ </Link>
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ <Lib>
+ <AdditionalLibraryDirectories>
+ </AdditionalLibraryDirectories>
+ <AdditionalDependencies>
+ </AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\src\alloc-aligned.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-override-osx.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-override.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-posix.c" />
+ <ClCompile Include="..\..\src\alloc.c" />
+ <ClCompile Include="..\..\src\arena.c" />
+ <ClCompile Include="..\..\src\bitmap.inc.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\heap.c" />
+ <ClCompile Include="..\..\src\init.c" />
+ <ClCompile Include="..\..\src\region.c" />
+ <ClCompile Include="..\..\src\options.c" />
+ <ClCompile Include="..\..\src\page-queue.c">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\src\page.c" />
+ <ClCompile Include="..\..\src\random.c" />
+ <ClCompile Include="..\..\src\segment.c" />
+ <ClCompile Include="..\..\src\os.c" />
+ <ClCompile Include="..\..\src\stats.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h" />
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc.h" />
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-atomic.h" />
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-override.h" />
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-types.h" />
+ <ClInclude Include="..\..\include\mimalloc-new-delete.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="..\..\src\alloc.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-aligned.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-override.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-override-osx.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\alloc-posix.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\heap.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\init.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\region.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\options.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\os.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\page.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\page-queue.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\segment.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\stats.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\arena.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\bitmap.inc.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\random.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-atomic.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\mimalloc-new-delete.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-override.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-types.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{2b556b10-f559-4b2d-896e-142652adbf0c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{852a14ae-6dde-4e95-8077-ca705e97e5af}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+</Project>
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#pragma once
+#ifndef MIMALLOC_ATOMIC_H
+#define MIMALLOC_ATOMIC_H
+
+// ------------------------------------------------------
+// Atomics
+// We need to be portable between C, C++, and MSVC.
+// ------------------------------------------------------
+
+#if defined(_MSC_VER)
+#define _Atomic(tp) tp
+#define ATOMIC_VAR_INIT(x) x
+#elif defined(__cplusplus)
+#include <atomic>
+#define _Atomic(tp) std::atomic<tp>
+#else
+#include <stdatomic.h>
+#endif
+
+// ------------------------------------------------------
+// Atomic operations specialized for mimalloc
+// ------------------------------------------------------
+
+// Atomically add a value; returns the previous value. Memory ordering is relaxed.
+static inline uintptr_t mi_atomic_add(volatile _Atomic(uintptr_t)* p, uintptr_t add);
+
+// Atomically "and" a value; returns the previous value. Memory ordering is relaxed.
+static inline uintptr_t mi_atomic_and(volatile _Atomic(uintptr_t)* p, uintptr_t x);
+
+// Atomically "or" a value; returns the previous value. Memory ordering is relaxed.
+static inline uintptr_t mi_atomic_or(volatile _Atomic(uintptr_t)* p, uintptr_t x);
+
+// Atomically compare and exchange a value; returns `true` if successful.
+// May fail spuriously. Memory ordering as release on success, and relaxed on failure.
+// (Note: expected and desired are in opposite order from atomic_compare_exchange)
+static inline bool mi_atomic_cas_weak(volatile _Atomic(uintptr_t)* p, uintptr_t desired, uintptr_t expected);
+
+// Atomically compare and exchange a value; returns `true` if successful.
+// Memory ordering is acquire-release
+// (Note: expected and desired are in opposite order from atomic_compare_exchange)
+static inline bool mi_atomic_cas_strong(volatile _Atomic(uintptr_t)* p, uintptr_t desired, uintptr_t expected);
+
+// Atomically exchange a value. Memory ordering is acquire-release.
+static inline uintptr_t mi_atomic_exchange(volatile _Atomic(uintptr_t)* p, uintptr_t exchange);
+
+// Atomically read a value. Memory ordering is relaxed.
+static inline uintptr_t mi_atomic_read_relaxed(const volatile _Atomic(uintptr_t)* p);
+
+// Atomically read a value. Memory ordering is acquire.
+static inline uintptr_t mi_atomic_read(const volatile _Atomic(uintptr_t)* p);
+
+// Atomically write a value. Memory ordering is release.
+static inline void mi_atomic_write(volatile _Atomic(uintptr_t)* p, uintptr_t x);
+
+// Yield
+static inline void mi_atomic_yield(void);
+
+// Atomically add a 64-bit value; returns the previous value.
+// Note: not using _Atomic(int64_t) as it is only used for statistics.
+static inline void mi_atomic_addi64(volatile int64_t* p, int64_t add);
+
+// Atomically update `*p` with the maximum of `*p` and `x` as a 64-bit value.
+// Returns the previous value. Note: not using _Atomic(int64_t) as it is only used for statistics.
+static inline void mi_atomic_maxi64(volatile int64_t* p, int64_t x);
+
+// Atomically read a 64-bit value
+// Note: not using _Atomic(int64_t) as it is only used for statistics.
+static inline int64_t mi_atomic_readi64(volatile int64_t* p);
+
+// Atomically subtract a value; returns the previous value.
+static inline uintptr_t mi_atomic_sub(volatile _Atomic(uintptr_t)* p, uintptr_t sub) {
+ return mi_atomic_add(p, (uintptr_t)(-((intptr_t)sub)));
+}
+
+// Atomically increment a value; returns the incremented result.
+static inline uintptr_t mi_atomic_increment(volatile _Atomic(uintptr_t)* p) {
+ return mi_atomic_add(p, 1);
+}
+
+// Atomically decrement a value; returns the decremented result.
+static inline uintptr_t mi_atomic_decrement(volatile _Atomic(uintptr_t)* p) {
+ return mi_atomic_sub(p, 1);
+}
+
+// Atomically add a signed value; returns the previous value.
+static inline intptr_t mi_atomic_addi(volatile _Atomic(intptr_t)* p, intptr_t add) {
+ return (intptr_t)mi_atomic_add((volatile _Atomic(uintptr_t)*)p, (uintptr_t)add);
+}
+
+// Atomically subtract a signed value; returns the previous value.
+static inline intptr_t mi_atomic_subi(volatile _Atomic(intptr_t)* p, intptr_t sub) {
+ return (intptr_t)mi_atomic_addi(p,-sub);
+}
+
+// Atomically read a pointer; Memory order is relaxed (i.e. no fence, only atomic).
+#define mi_atomic_read_ptr_relaxed(T,p) \
+ (T*)(mi_atomic_read_relaxed((const volatile _Atomic(uintptr_t)*)(p)))
+
+// Atomically read a pointer; Memory order is acquire.
+#define mi_atomic_read_ptr(T,p) \
+ (T*)(mi_atomic_read((const volatile _Atomic(uintptr_t)*)(p)))
+
+// Atomically write a pointer; Memory order is acquire.
+#define mi_atomic_write_ptr(T,p,x) \
+ mi_atomic_write((volatile _Atomic(uintptr_t)*)(p), (uintptr_t)((T*)x))
+
+// Atomically compare and exchange a pointer; returns `true` if successful. May fail spuriously.
+// Memory order is release. (like a write)
+// (Note: expected and desired are in opposite order from atomic_compare_exchange)
+#define mi_atomic_cas_ptr_weak(T,p,desired,expected) \
+ mi_atomic_cas_weak((volatile _Atomic(uintptr_t)*)(p), (uintptr_t)((T*)(desired)), (uintptr_t)((T*)(expected)))
+
+// Atomically compare and exchange a pointer; returns `true` if successful. Memory order is acquire_release.
+// (Note: expected and desired are in opposite order from atomic_compare_exchange)
+#define mi_atomic_cas_ptr_strong(T,p,desired,expected) \
+ mi_atomic_cas_strong((volatile _Atomic(uintptr_t)*)(p),(uintptr_t)((T*)(desired)), (uintptr_t)((T*)(expected)))
+
+// Atomically exchange a pointer value.
+#define mi_atomic_exchange_ptr(T,p,exchange) \
+ (T*)mi_atomic_exchange((volatile _Atomic(uintptr_t)*)(p), (uintptr_t)((T*)exchange))
+
+
+#ifdef _MSC_VER
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <intrin.h>
+#ifdef _WIN64
+typedef LONG64 msc_intptr_t;
+#define MI_64(f) f##64
+#else
+typedef LONG msc_intptr_t;
+#define MI_64(f) f
+#endif
+static inline uintptr_t mi_atomic_add(volatile _Atomic(uintptr_t)* p, uintptr_t add) {
+ return (uintptr_t)MI_64(_InterlockedExchangeAdd)((volatile msc_intptr_t*)p, (msc_intptr_t)add);
+}
+static inline uintptr_t mi_atomic_and(volatile _Atomic(uintptr_t)* p, uintptr_t x) {
+ return (uintptr_t)MI_64(_InterlockedAnd)((volatile msc_intptr_t*)p, (msc_intptr_t)x);
+}
+static inline uintptr_t mi_atomic_or(volatile _Atomic(uintptr_t)* p, uintptr_t x) {
+ return (uintptr_t)MI_64(_InterlockedOr)((volatile msc_intptr_t*)p, (msc_intptr_t)x);
+}
+static inline bool mi_atomic_cas_strong(volatile _Atomic(uintptr_t)* p, uintptr_t desired, uintptr_t expected) {
+ return (expected == (uintptr_t)MI_64(_InterlockedCompareExchange)((volatile msc_intptr_t*)p, (msc_intptr_t)desired, (msc_intptr_t)expected));
+}
+static inline bool mi_atomic_cas_weak(volatile _Atomic(uintptr_t)* p, uintptr_t desired, uintptr_t expected) {
+ return mi_atomic_cas_strong(p,desired,expected);
+}
+static inline uintptr_t mi_atomic_exchange(volatile _Atomic(uintptr_t)* p, uintptr_t exchange) {
+ return (uintptr_t)MI_64(_InterlockedExchange)((volatile msc_intptr_t*)p, (msc_intptr_t)exchange);
+}
+static inline uintptr_t mi_atomic_read(volatile _Atomic(uintptr_t) const* p) {
+ return *p;
+}
+static inline uintptr_t mi_atomic_read_relaxed(volatile _Atomic(uintptr_t) const* p) {
+ return *p;
+}
+static inline void mi_atomic_write(volatile _Atomic(uintptr_t)* p, uintptr_t x) {
+ #if defined(_M_IX86) || defined(_M_X64)
+ *p = x;
+ #else
+ mi_atomic_exchange(p,x);
+ #endif
+}
+static inline void mi_atomic_yield(void) {
+ YieldProcessor();
+}
+static inline void mi_atomic_addi64(volatile _Atomic(int64_t)* p, int64_t add) {
+ #ifdef _WIN64
+ mi_atomic_addi(p,add);
+ #else
+ int64_t current;
+ int64_t sum;
+ do {
+ current = *p;
+ sum = current + add;
+ } while (_InterlockedCompareExchange64(p, sum, current) != current);
+ #endif
+}
+
+static inline void mi_atomic_maxi64(volatile _Atomic(int64_t)*p, int64_t x) {
+ int64_t current;
+ do {
+ current = *p;
+ } while (current < x && _InterlockedCompareExchange64(p, x, current) != current);
+}
+
+static inline int64_t mi_atomic_readi64(volatile _Atomic(int64_t)*p) {
+ #ifdef _WIN64
+ return *p;
+ #else
+ int64_t current;
+ do {
+ current = *p;
+ } while (_InterlockedCompareExchange64(p, current, current) != current);
+ return current;
+ #endif
+}
+
+#else
+#ifdef __cplusplus
+#define MI_USING_STD using namespace std;
+#else
+#define MI_USING_STD
+#endif
+static inline uintptr_t mi_atomic_add(volatile _Atomic(uintptr_t)* p, uintptr_t add) {
+ MI_USING_STD
+ return atomic_fetch_add_explicit(p, add, memory_order_relaxed);
+}
+static inline uintptr_t mi_atomic_and(volatile _Atomic(uintptr_t)* p, uintptr_t x) {
+ MI_USING_STD
+ return atomic_fetch_and_explicit(p, x, memory_order_acq_rel);
+}
+static inline uintptr_t mi_atomic_or(volatile _Atomic(uintptr_t)* p, uintptr_t x) {
+ MI_USING_STD
+ return atomic_fetch_or_explicit(p, x, memory_order_acq_rel);
+}
+static inline bool mi_atomic_cas_weak(volatile _Atomic(uintptr_t)* p, uintptr_t desired, uintptr_t expected) {
+ MI_USING_STD
+ return atomic_compare_exchange_weak_explicit(p, &expected, desired, memory_order_acq_rel, memory_order_acquire);
+}
+static inline bool mi_atomic_cas_strong(volatile _Atomic(uintptr_t)* p, uintptr_t desired, uintptr_t expected) {
+ MI_USING_STD
+ return atomic_compare_exchange_strong_explicit(p, &expected, desired, memory_order_acq_rel, memory_order_acquire);
+}
+static inline uintptr_t mi_atomic_exchange(volatile _Atomic(uintptr_t)* p, uintptr_t exchange) {
+ MI_USING_STD
+ return atomic_exchange_explicit(p, exchange, memory_order_acq_rel);
+}
+static inline uintptr_t mi_atomic_read_relaxed(const volatile _Atomic(uintptr_t)* p) {
+ MI_USING_STD
+ return atomic_load_explicit((volatile _Atomic(uintptr_t)*) p, memory_order_relaxed);
+}
+static inline uintptr_t mi_atomic_read(const volatile _Atomic(uintptr_t)* p) {
+ MI_USING_STD
+ return atomic_load_explicit((volatile _Atomic(uintptr_t)*) p, memory_order_acquire);
+}
+static inline void mi_atomic_write(volatile _Atomic(uintptr_t)* p, uintptr_t x) {
+ MI_USING_STD
+ return atomic_store_explicit(p, x, memory_order_release);
+}
+static inline void mi_atomic_addi64(volatile int64_t* p, int64_t add) {
+ MI_USING_STD
+ atomic_fetch_add_explicit((volatile _Atomic(int64_t)*)p, add, memory_order_relaxed);
+}
+static inline int64_t mi_atomic_readi64(volatile int64_t* p) {
+ MI_USING_STD
+ return atomic_load_explicit((volatile _Atomic(int64_t)*) p, memory_order_relaxed);
+}
+static inline void mi_atomic_maxi64(volatile int64_t* p, int64_t x) {
+ MI_USING_STD
+ int64_t current;
+ do {
+ current = mi_atomic_readi64(p);
+ } while (current < x && !atomic_compare_exchange_weak_explicit((volatile _Atomic(int64_t)*)p, ¤t, x, memory_order_acq_rel, memory_order_relaxed));
+}
+
+#if defined(__cplusplus)
+ #include <thread>
+ static inline void mi_atomic_yield(void) {
+ std::this_thread::yield();
+ }
+#elif (defined(__GNUC__) || defined(__clang__)) && \
+ (defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__))
+#if defined(__x86_64__) || defined(__i386__)
+ static inline void mi_atomic_yield(void) {
+ asm volatile ("pause" ::: "memory");
+ }
+#elif defined(__arm__) || defined(__aarch64__)
+ static inline void mi_atomic_yield(void) {
+ asm volatile("yield");
+ }
+#endif
+#elif defined(__wasi__)
+ #include <sched.h>
+ static inline void mi_atomic_yield(void) {
+ sched_yield();
+ }
+#else
+ #include <unistd.h>
+ static inline void mi_atomic_yield(void) {
+ sleep(0);
+ }
+#endif
+
+#endif
+
+#endif // __MIMALLOC_ATOMIC_H
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#pragma once
+#ifndef MIMALLOC_INTERNAL_H
+#define MIMALLOC_INTERNAL_H
+
+#include "mimalloc-types.h"
+
+#if (MI_DEBUG>0)
+#define mi_trace_message(...) _mi_trace_message(__VA_ARGS__)
+#else
+#define mi_trace_message(...)
+#endif
+
+#define MI_CACHE_LINE 64
+#if defined(_MSC_VER)
+#pragma warning(disable:4127) // suppress constant conditional warning (due to MI_SECURE paths)
+#define mi_decl_noinline __declspec(noinline)
+#define mi_decl_thread __declspec(thread)
+#define mi_decl_cache_align __declspec(align(MI_CACHE_LINE))
+#elif (defined(__GNUC__) && (__GNUC__>=3)) // includes clang and icc
+#define mi_decl_noinline __attribute__((noinline))
+#define mi_decl_thread __thread
+#define mi_decl_cache_align __attribute__((aligned(MI_CACHE_LINE)))
+#else
+#define mi_decl_noinline
+#define mi_decl_thread __thread // hope for the best :-)
+#define mi_decl_cache_align
+#endif
+
+
+// "options.c"
+void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message);
+void _mi_fprintf(mi_output_fun* out, void* arg, const char* fmt, ...);
+void _mi_warning_message(const char* fmt, ...);
+void _mi_verbose_message(const char* fmt, ...);
+void _mi_trace_message(const char* fmt, ...);
+void _mi_options_init(void);
+void _mi_error_message(int err, const char* fmt, ...);
+
+// random.c
+void _mi_random_init(mi_random_ctx_t* ctx);
+void _mi_random_split(mi_random_ctx_t* ctx, mi_random_ctx_t* new_ctx);
+uintptr_t _mi_random_next(mi_random_ctx_t* ctx);
+uintptr_t _mi_heap_random_next(mi_heap_t* heap);
+uintptr_t _os_random_weak(uintptr_t extra_seed);
+static inline uintptr_t _mi_random_shuffle(uintptr_t x);
+
+// init.c
+extern mi_stats_t _mi_stats_main;
+extern const mi_page_t _mi_page_empty;
+bool _mi_is_main_thread(void);
+bool _mi_preloading(); // true while the C runtime is not ready
+
+// os.c
+size_t _mi_os_page_size(void);
+void _mi_os_init(void); // called from process init
+void* _mi_os_alloc(size_t size, mi_stats_t* stats); // to allocate thread local data
+void _mi_os_free(void* p, size_t size, mi_stats_t* stats); // to free thread local data
+size_t _mi_os_good_alloc_size(size_t size);
+
+// memory.c
+void* _mi_mem_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_zero, size_t* id, mi_os_tld_t* tld);
+void _mi_mem_free(void* p, size_t size, size_t id, bool fully_committed, bool any_reset, mi_os_tld_t* tld);
+
+bool _mi_mem_reset(void* p, size_t size, mi_os_tld_t* tld);
+bool _mi_mem_unreset(void* p, size_t size, bool* is_zero, mi_os_tld_t* tld);
+bool _mi_mem_commit(void* p, size_t size, bool* is_zero, mi_os_tld_t* tld);
+bool _mi_mem_protect(void* addr, size_t size);
+bool _mi_mem_unprotect(void* addr, size_t size);
+
+void _mi_mem_collect(mi_os_tld_t* tld);
+
+// "segment.c"
+mi_page_t* _mi_segment_page_alloc(mi_heap_t* heap, size_t block_wsize, mi_segments_tld_t* tld, mi_os_tld_t* os_tld);
+void _mi_segment_page_free(mi_page_t* page, bool force, mi_segments_tld_t* tld);
+void _mi_segment_page_abandon(mi_page_t* page, mi_segments_tld_t* tld);
+uint8_t* _mi_segment_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t block_size, size_t* page_size, size_t* pre_size); // page start for any page
+void _mi_segment_huge_page_free(mi_segment_t* segment, mi_page_t* page, mi_block_t* block);
+
+void _mi_segment_thread_collect(mi_segments_tld_t* tld);
+void _mi_abandoned_reclaim_all(mi_heap_t* heap, mi_segments_tld_t* tld);
+void _mi_abandoned_await_readers(void);
+
+
+
+// "page.c"
+void* _mi_malloc_generic(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_attr_malloc;
+
+void _mi_page_retire(mi_page_t* page); // free the page if there are no other pages with many free blocks
+void _mi_page_unfull(mi_page_t* page);
+void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force); // free the page
+void _mi_page_abandon(mi_page_t* page, mi_page_queue_t* pq); // abandon the page, to be picked up by another thread...
+void _mi_heap_delayed_free(mi_heap_t* heap);
+void _mi_heap_collect_retired(mi_heap_t* heap, bool force);
+
+void _mi_page_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never);
+size_t _mi_page_queue_append(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_queue_t* append);
+void _mi_deferred_free(mi_heap_t* heap, bool force);
+
+void _mi_page_free_collect(mi_page_t* page,bool force);
+void _mi_page_reclaim(mi_heap_t* heap, mi_page_t* page); // callback from segments
+
+size_t _mi_bin_size(uint8_t bin); // for stats
+uint8_t _mi_bin(size_t size); // for stats
+uint8_t _mi_bsr(uintptr_t x); // bit-scan-right, used on BSD in "os.c"
+
+// "heap.c"
+void _mi_heap_destroy_pages(mi_heap_t* heap);
+void _mi_heap_collect_abandon(mi_heap_t* heap);
+void _mi_heap_set_default_direct(mi_heap_t* heap);
+
+// "stats.c"
+void _mi_stats_done(mi_stats_t* stats);
+
+mi_msecs_t _mi_clock_now(void);
+mi_msecs_t _mi_clock_end(mi_msecs_t start);
+mi_msecs_t _mi_clock_start(void);
+
+// "alloc.c"
+void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept; // called from `_mi_malloc_generic`
+void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero);
+void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero);
+mi_block_t* _mi_page_ptr_unalign(const mi_segment_t* segment, const mi_page_t* page, const void* p);
+bool _mi_free_delayed_block(mi_block_t* block);
+void _mi_block_zero_init(const mi_page_t* page, void* p, size_t size);
+
+#if MI_DEBUG>1
+bool _mi_page_is_valid(mi_page_t* page);
+#endif
+
+
+// ------------------------------------------------------
+// Branches
+// ------------------------------------------------------
+
+#if defined(__GNUC__) || defined(__clang__)
+#define mi_unlikely(x) __builtin_expect((x),0)
+#define mi_likely(x) __builtin_expect((x),1)
+#else
+#define mi_unlikely(x) (x)
+#define mi_likely(x) (x)
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+
+/* -----------------------------------------------------------
+ Error codes passed to `_mi_fatal_error`
+ All are recoverable but EFAULT is a serious error and aborts by default in secure mode.
+ For portability define undefined error codes using common Unix codes:
+ <https://www-numi.fnal.gov/offline_software/srt_public_context/WebDocs/Errors/unix_system_errors.html>
+----------------------------------------------------------- */
+#include <errno.h>
+#ifndef EAGAIN // double free
+#define EAGAIN (11)
+#endif
+#ifndef ENOMEM // out of memory
+#define ENOMEM (12)
+#endif
+#ifndef EFAULT // corrupted free-list or meta-data
+#define EFAULT (14)
+#endif
+#ifndef EINVAL // trying to free an invalid pointer
+#define EINVAL (22)
+#endif
+#ifndef EOVERFLOW // count*size overflow
+#define EOVERFLOW (75)
+#endif
+
+
+/* -----------------------------------------------------------
+ Inlined definitions
+----------------------------------------------------------- */
+#define UNUSED(x) (void)(x)
+#if (MI_DEBUG>0)
+#define UNUSED_RELEASE(x)
+#else
+#define UNUSED_RELEASE(x) UNUSED(x)
+#endif
+
+#define MI_INIT4(x) x(),x(),x(),x()
+#define MI_INIT8(x) MI_INIT4(x),MI_INIT4(x)
+#define MI_INIT16(x) MI_INIT8(x),MI_INIT8(x)
+#define MI_INIT32(x) MI_INIT16(x),MI_INIT16(x)
+#define MI_INIT64(x) MI_INIT32(x),MI_INIT32(x)
+#define MI_INIT128(x) MI_INIT64(x),MI_INIT64(x)
+#define MI_INIT256(x) MI_INIT128(x),MI_INIT128(x)
+
+
+// Is `x` a power of two? (0 is considered a power of two)
+static inline bool _mi_is_power_of_two(uintptr_t x) {
+ return ((x & (x - 1)) == 0);
+}
+
+// Align upwards
+static inline uintptr_t _mi_align_up(uintptr_t sz, size_t alignment) {
+ mi_assert_internal(alignment != 0);
+ uintptr_t mask = alignment - 1;
+ if ((alignment & mask) == 0) { // power of two?
+ return ((sz + mask) & ~mask);
+ }
+ else {
+ return (((sz + mask)/alignment)*alignment);
+ }
+}
+
+// Divide upwards: `s <= _mi_divide_up(s,d)*d < s+d`.
+static inline uintptr_t _mi_divide_up(uintptr_t size, size_t divider) {
+ mi_assert_internal(divider != 0);
+ return (divider == 0 ? size : ((size + divider - 1) / divider));
+}
+
+// Is memory zero initialized?
+static inline bool mi_mem_is_zero(void* p, size_t size) {
+ for (size_t i = 0; i < size; i++) {
+ if (((uint8_t*)p)[i] != 0) return false;
+ }
+ return true;
+}
+
+// Align a byte size to a size in _machine words_,
+// i.e. byte size == `wsize*sizeof(void*)`.
+static inline size_t _mi_wsize_from_size(size_t size) {
+ mi_assert_internal(size <= SIZE_MAX - sizeof(uintptr_t));
+ return (size + sizeof(uintptr_t) - 1) / sizeof(uintptr_t);
+}
+
+// Does malloc satisfy the alignment constraints already?
+static inline bool mi_malloc_satisfies_alignment(size_t alignment, size_t size) {
+ return (alignment == sizeof(void*) || (alignment == MI_MAX_ALIGN_SIZE && size > (MI_MAX_ALIGN_SIZE/2)));
+}
+
+// Overflow detecting multiply
+#if __has_builtin(__builtin_umul_overflow) || __GNUC__ >= 5
+#include <limits.h> // UINT_MAX, ULONG_MAX
+#if defined(_CLOCK_T) // for Illumos
+#undef _CLOCK_T
+#endif
+static inline bool mi_mul_overflow(size_t count, size_t size, size_t* total) {
+ #if (SIZE_MAX == UINT_MAX)
+ return __builtin_umul_overflow(count, size, total);
+ #elif (SIZE_MAX == ULONG_MAX)
+ return __builtin_umull_overflow(count, size, total);
+ #else
+ return __builtin_umulll_overflow(count, size, total);
+ #endif
+}
+#else /* __builtin_umul_overflow is unavailable */
+static inline bool mi_mul_overflow(size_t count, size_t size, size_t* total) {
+ #define MI_MUL_NO_OVERFLOW ((size_t)1 << (4*sizeof(size_t))) // sqrt(SIZE_MAX)
+ *total = count * size;
+ return ((size >= MI_MUL_NO_OVERFLOW || count >= MI_MUL_NO_OVERFLOW)
+ && size > 0 && (SIZE_MAX / size) < count);
+}
+#endif
+
+// Safe multiply `count*size` into `total`; return `true` on overflow.
+static inline bool mi_count_size_overflow(size_t count, size_t size, size_t* total) {
+ if (count==1) { // quick check for the case where count is one (common for C++ allocators)
+ *total = size;
+ return false;
+ }
+ else if (mi_unlikely(mi_mul_overflow(count, size, total))) {
+ _mi_error_message(EOVERFLOW, "allocation request is too large (%zu * %zu bytes)\n", count, size);
+ *total = SIZE_MAX;
+ return true;
+ }
+ else return false;
+}
+
+
+/* ----------------------------------------------------------------------------------------
+The thread local default heap: `_mi_get_default_heap` returns the thread local heap.
+On most platforms (Windows, Linux, FreeBSD, NetBSD, etc), this just returns a
+__thread local variable (`_mi_heap_default`). With the initial-exec TLS model this ensures
+that the storage will always be available (allocated on the thread stacks).
+On some platforms though we cannot use that when overriding `malloc` since the underlying
+TLS implementation (or the loader) will call itself `malloc` on a first access and recurse.
+We try to circumvent this in an efficient way:
+- macOSX : we use an unused TLS slot from the OS allocated slots (MI_TLS_SLOT). On OSX, the
+ loader itself calls `malloc` even before the modules are initialized.
+- OpenBSD: we use an unused slot from the pthread block (MI_TLS_PTHREAD_SLOT_OFS).
+- DragonFly: not yet working.
+------------------------------------------------------------------------------------------- */
+
+extern const mi_heap_t _mi_heap_empty; // read-only empty heap, initial value of the thread local default heap
+extern bool _mi_process_is_initialized;
+mi_heap_t* _mi_heap_main_get(void); // statically allocated main backing heap
+
+#if defined(MI_MALLOC_OVERRIDE)
+#if defined(__MACH__) // OSX
+#define MI_TLS_SLOT 89 // seems unused?
+// other possible unused ones are 9, 29, __PTK_FRAMEWORK_JAVASCRIPTCORE_KEY4 (94), __PTK_FRAMEWORK_GC_KEY9 (112) and __PTK_FRAMEWORK_OLDGC_KEY9 (89)
+// see <https://github.com/rweichler/substrate/blob/master/include/pthread_machdep.h>
+#elif defined(__OpenBSD__)
+// use end bytes of a name; goes wrong if anyone uses names > 23 characters (ptrhread specifies 16)
+// see <https://github.com/openbsd/src/blob/master/lib/libc/include/thread_private.h#L371>
+#define MI_TLS_PTHREAD_SLOT_OFS (6*sizeof(int) + 4*sizeof(void*) + 24)
+#elif defined(__DragonFly__)
+#warning "mimalloc is not working correctly on DragonFly yet."
+#define MI_TLS_PTHREAD_SLOT_OFS (4 + 1*sizeof(void*)) // offset `uniqueid` (also used by gdb?) <https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/lib/libthread_xu/thread/thr_private.h#L458>
+#endif
+#endif
+
+#if defined(MI_TLS_SLOT)
+static inline void* mi_tls_slot(size_t slot) mi_attr_noexcept; // forward declaration
+#elif defined(MI_TLS_PTHREAD_SLOT_OFS)
+#include <pthread.h>
+static inline mi_heap_t** mi_tls_pthread_heap_slot(void) {
+ pthread_t self = pthread_self();
+ #if defined(__DragonFly__)
+ if (self==NULL) {
+ static mi_heap_t* pheap_main = _mi_heap_main_get();
+ return &pheap_main;
+ }
+ #endif
+ return (mi_heap_t**)((uint8_t*)self + MI_TLS_PTHREAD_SLOT_OFS);
+}
+#elif defined(MI_TLS_PTHREAD)
+#include <pthread.h>
+extern pthread_key_t _mi_heap_default_key;
+#else
+extern mi_decl_thread mi_heap_t* _mi_heap_default; // default heap to allocate from
+#endif
+
+static inline mi_heap_t* mi_get_default_heap(void) {
+#if defined(MI_TLS_SLOT)
+ mi_heap_t* heap = (mi_heap_t*)mi_tls_slot(MI_TLS_SLOT);
+ return (mi_unlikely(heap == NULL) ? (mi_heap_t*)&_mi_heap_empty : heap);
+#elif defined(MI_TLS_PTHREAD_SLOT_OFS)
+ mi_heap_t* heap = *mi_tls_pthread_heap_slot();
+ return (mi_unlikely(heap == NULL) ? (mi_heap_t*)&_mi_heap_empty : heap);
+#elif defined(MI_TLS_PTHREAD)
+ mi_heap_t* heap = (mi_unlikely(_mi_heap_default_key == (pthread_key_t)(-1)) ? _mi_heap_main_get() : (mi_heap_t*)pthread_getspecific(_mi_heap_default_key));
+ return (mi_unlikely(heap == NULL) ? (mi_heap_t*)&_mi_heap_empty : heap);
+#else
+ #if defined(MI_TLS_RECURSE_GUARD)
+ if (mi_unlikely(!_mi_process_is_initialized)) return _mi_heap_main_get();
+ #endif
+ return _mi_heap_default;
+#endif
+}
+
+static inline bool mi_heap_is_default(const mi_heap_t* heap) {
+ return (heap == mi_get_default_heap());
+}
+
+static inline bool mi_heap_is_backing(const mi_heap_t* heap) {
+ return (heap->tld->heap_backing == heap);
+}
+
+static inline bool mi_heap_is_initialized(mi_heap_t* heap) {
+ mi_assert_internal(heap != NULL);
+ return (heap != &_mi_heap_empty);
+}
+
+static inline uintptr_t _mi_ptr_cookie(const void* p) {
+ extern mi_heap_t _mi_heap_main;
+ mi_assert_internal(_mi_heap_main.cookie != 0);
+ return ((uintptr_t)p ^ _mi_heap_main.cookie);
+}
+
+/* -----------------------------------------------------------
+ Pages
+----------------------------------------------------------- */
+
+static inline mi_page_t* _mi_heap_get_free_small_page(mi_heap_t* heap, size_t size) {
+ mi_assert_internal(size <= (MI_SMALL_SIZE_MAX + MI_PADDING_SIZE));
+ const size_t idx = _mi_wsize_from_size(size);
+ mi_assert_internal(idx < MI_PAGES_DIRECT);
+ return heap->pages_free_direct[idx];
+}
+
+// Get the page belonging to a certain size class
+static inline mi_page_t* _mi_get_free_small_page(size_t size) {
+ return _mi_heap_get_free_small_page(mi_get_default_heap(), size);
+}
+
+// Segment that contains the pointer
+static inline mi_segment_t* _mi_ptr_segment(const void* p) {
+ // mi_assert_internal(p != NULL);
+ return (mi_segment_t*)((uintptr_t)p & ~MI_SEGMENT_MASK);
+}
+
+// Segment belonging to a page
+static inline mi_segment_t* _mi_page_segment(const mi_page_t* page) {
+ mi_segment_t* segment = _mi_ptr_segment(page);
+ mi_assert_internal(segment == NULL || page == &segment->pages[page->segment_idx]);
+ return segment;
+}
+
+// used internally
+static inline uintptr_t _mi_segment_page_idx_of(const mi_segment_t* segment, const void* p) {
+ // if (segment->page_size > MI_SEGMENT_SIZE) return &segment->pages[0]; // huge pages
+ ptrdiff_t diff = (uint8_t*)p - (uint8_t*)segment;
+ mi_assert_internal(diff >= 0 && (size_t)diff < MI_SEGMENT_SIZE);
+ uintptr_t idx = (uintptr_t)diff >> segment->page_shift;
+ mi_assert_internal(idx < segment->capacity);
+ mi_assert_internal(segment->page_kind <= MI_PAGE_MEDIUM || idx == 0);
+ return idx;
+}
+
+// Get the page containing the pointer
+static inline mi_page_t* _mi_segment_page_of(const mi_segment_t* segment, const void* p) {
+ uintptr_t idx = _mi_segment_page_idx_of(segment, p);
+ return &((mi_segment_t*)segment)->pages[idx];
+}
+
+// Quick page start for initialized pages
+static inline uint8_t* _mi_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size) {
+ const size_t bsize = page->xblock_size;
+ mi_assert_internal(bsize > 0 && (bsize%sizeof(void*)) == 0);
+ return _mi_segment_page_start(segment, page, bsize, page_size, NULL);
+}
+
+// Get the page containing the pointer
+static inline mi_page_t* _mi_ptr_page(void* p) {
+ return _mi_segment_page_of(_mi_ptr_segment(p), p);
+}
+
+// Get the block size of a page (special cased for huge objects)
+static inline size_t mi_page_block_size(const mi_page_t* page) {
+ const size_t bsize = page->xblock_size;
+ mi_assert_internal(bsize > 0);
+ if (mi_likely(bsize < MI_HUGE_BLOCK_SIZE)) {
+ return bsize;
+ }
+ else {
+ size_t psize;
+ _mi_segment_page_start(_mi_page_segment(page), page, bsize, &psize, NULL);
+ return psize;
+ }
+}
+
+// Get the usable block size of a page without fixed padding.
+// This may still include internal padding due to alignment and rounding up size classes.
+static inline size_t mi_page_usable_block_size(const mi_page_t* page) {
+ return mi_page_block_size(page) - MI_PADDING_SIZE;
+}
+
+
+// Thread free access
+static inline mi_block_t* mi_page_thread_free(const mi_page_t* page) {
+ return (mi_block_t*)(mi_atomic_read_relaxed(&page->xthread_free) & ~3);
+}
+
+static inline mi_delayed_t mi_page_thread_free_flag(const mi_page_t* page) {
+ return (mi_delayed_t)(mi_atomic_read_relaxed(&page->xthread_free) & 3);
+}
+
+// Heap access
+static inline mi_heap_t* mi_page_heap(const mi_page_t* page) {
+ return (mi_heap_t*)(mi_atomic_read_relaxed(&page->xheap));
+}
+
+static inline void mi_page_set_heap(mi_page_t* page, mi_heap_t* heap) {
+ mi_assert_internal(mi_page_thread_free_flag(page) != MI_DELAYED_FREEING);
+ mi_atomic_write(&page->xheap,(uintptr_t)heap);
+}
+
+// Thread free flag helpers
+static inline mi_block_t* mi_tf_block(mi_thread_free_t tf) {
+ return (mi_block_t*)(tf & ~0x03);
+}
+static inline mi_delayed_t mi_tf_delayed(mi_thread_free_t tf) {
+ return (mi_delayed_t)(tf & 0x03);
+}
+static inline mi_thread_free_t mi_tf_make(mi_block_t* block, mi_delayed_t delayed) {
+ return (mi_thread_free_t)((uintptr_t)block | (uintptr_t)delayed);
+}
+static inline mi_thread_free_t mi_tf_set_delayed(mi_thread_free_t tf, mi_delayed_t delayed) {
+ return mi_tf_make(mi_tf_block(tf),delayed);
+}
+static inline mi_thread_free_t mi_tf_set_block(mi_thread_free_t tf, mi_block_t* block) {
+ return mi_tf_make(block, mi_tf_delayed(tf));
+}
+
+// are all blocks in a page freed?
+// note: needs up-to-date used count, (as the `xthread_free` list may not be empty). see `_mi_page_collect_free`.
+static inline bool mi_page_all_free(const mi_page_t* page) {
+ mi_assert_internal(page != NULL);
+ return (page->used == 0);
+}
+
+// are there any available blocks?
+static inline bool mi_page_has_any_available(const mi_page_t* page) {
+ mi_assert_internal(page != NULL && page->reserved > 0);
+ return (page->used < page->reserved || (mi_page_thread_free(page) != NULL));
+}
+
+// are there immediately available blocks, i.e. blocks available on the free list.
+static inline bool mi_page_immediate_available(const mi_page_t* page) {
+ mi_assert_internal(page != NULL);
+ return (page->free != NULL);
+}
+
+// is more than 7/8th of a page in use?
+static inline bool mi_page_mostly_used(const mi_page_t* page) {
+ if (page==NULL) return true;
+ uint16_t frac = page->reserved / 8U;
+ return (page->reserved - page->used <= frac);
+}
+
+static inline mi_page_queue_t* mi_page_queue(const mi_heap_t* heap, size_t size) {
+ return &((mi_heap_t*)heap)->pages[_mi_bin(size)];
+}
+
+
+
+//-----------------------------------------------------------
+// Page flags
+//-----------------------------------------------------------
+static inline bool mi_page_is_in_full(const mi_page_t* page) {
+ return page->flags.x.in_full;
+}
+
+static inline void mi_page_set_in_full(mi_page_t* page, bool in_full) {
+ page->flags.x.in_full = in_full;
+}
+
+static inline bool mi_page_has_aligned(const mi_page_t* page) {
+ return page->flags.x.has_aligned;
+}
+
+static inline void mi_page_set_has_aligned(mi_page_t* page, bool has_aligned) {
+ page->flags.x.has_aligned = has_aligned;
+}
+
+
+/* -------------------------------------------------------------------
+Encoding/Decoding the free list next pointers
+
+This is to protect against buffer overflow exploits where the
+free list is mutated. Many hardened allocators xor the next pointer `p`
+with a secret key `k1`, as `p^k1`. This prevents overwriting with known
+values but might be still too weak: if the attacker can guess
+the pointer `p` this can reveal `k1` (since `p^k1^p == k1`).
+Moreover, if multiple blocks can be read as well, the attacker can
+xor both as `(p1^k1) ^ (p2^k1) == p1^p2` which may reveal a lot
+about the pointers (and subsequently `k1`).
+
+Instead mimalloc uses an extra key `k2` and encodes as `((p^k2)<<<k1)+k1`.
+Since these operations are not associative, the above approaches do not
+work so well any more even if the `p` can be guesstimated. For example,
+for the read case we can subtract two entries to discard the `+k1` term,
+but that leads to `((p1^k2)<<<k1) - ((p2^k2)<<<k1)` at best.
+We include the left-rotation since xor and addition are otherwise linear
+in the lowest bit. Finally, both keys are unique per page which reduces
+the re-use of keys by a large factor.
+
+We also pass a separate `null` value to be used as `NULL` or otherwise
+`(k2<<<k1)+k1` would appear (too) often as a sentinel value.
+------------------------------------------------------------------- */
+
+static inline bool mi_is_in_same_segment(const void* p, const void* q) {
+ return (_mi_ptr_segment(p) == _mi_ptr_segment(q));
+}
+
+static inline bool mi_is_in_same_page(const void* p, const void* q) {
+ mi_segment_t* segmentp = _mi_ptr_segment(p);
+ mi_segment_t* segmentq = _mi_ptr_segment(q);
+ if (segmentp != segmentq) return false;
+ uintptr_t idxp = _mi_segment_page_idx_of(segmentp, p);
+ uintptr_t idxq = _mi_segment_page_idx_of(segmentq, q);
+ return (idxp == idxq);
+}
+
+static inline uintptr_t mi_rotl(uintptr_t x, uintptr_t shift) {
+ shift %= MI_INTPTR_BITS;
+ return (shift==0 ? x : ((x << shift) | (x >> (MI_INTPTR_BITS - shift))));
+}
+static inline uintptr_t mi_rotr(uintptr_t x, uintptr_t shift) {
+ shift %= MI_INTPTR_BITS;
+ return (shift==0 ? x : ((x >> shift) | (x << (MI_INTPTR_BITS - shift))));
+}
+
+static inline void* mi_ptr_decode(const void* null, const mi_encoded_t x, const uintptr_t* keys) {
+ void* p = (void*)(mi_rotr(x - keys[0], keys[0]) ^ keys[1]);
+ return (mi_unlikely(p==null) ? NULL : p);
+}
+
+static inline mi_encoded_t mi_ptr_encode(const void* null, const void* p, const uintptr_t* keys) {
+ uintptr_t x = (uintptr_t)(mi_unlikely(p==NULL) ? null : p);
+ return mi_rotl(x ^ keys[1], keys[0]) + keys[0];
+}
+
+static inline mi_block_t* mi_block_nextx( const void* null, const mi_block_t* block, const uintptr_t* keys ) {
+ #ifdef MI_ENCODE_FREELIST
+ return (mi_block_t*)mi_ptr_decode(null, block->next, keys);
+ #else
+ UNUSED(keys); UNUSED(null);
+ return (mi_block_t*)block->next;
+ #endif
+}
+
+static inline void mi_block_set_nextx(const void* null, mi_block_t* block, const mi_block_t* next, const uintptr_t* keys) {
+ #ifdef MI_ENCODE_FREELIST
+ block->next = mi_ptr_encode(null, next, keys);
+ #else
+ UNUSED(keys); UNUSED(null);
+ block->next = (mi_encoded_t)next;
+ #endif
+}
+
+static inline mi_block_t* mi_block_next(const mi_page_t* page, const mi_block_t* block) {
+ #ifdef MI_ENCODE_FREELIST
+ mi_block_t* next = mi_block_nextx(page,block,page->keys);
+ // check for free list corruption: is `next` at least in the same page?
+ // TODO: check if `next` is `page->block_size` aligned?
+ if (mi_unlikely(next!=NULL && !mi_is_in_same_page(block, next))) {
+ _mi_error_message(EFAULT, "corrupted free list entry of size %zub at %p: value 0x%zx\n", mi_page_block_size(page), block, (uintptr_t)next);
+ next = NULL;
+ }
+ return next;
+ #else
+ UNUSED(page);
+ return mi_block_nextx(page,block,NULL);
+ #endif
+}
+
+static inline void mi_block_set_next(const mi_page_t* page, mi_block_t* block, const mi_block_t* next) {
+ #ifdef MI_ENCODE_FREELIST
+ mi_block_set_nextx(page,block,next, page->keys);
+ #else
+ UNUSED(page);
+ mi_block_set_nextx(page,block,next,NULL);
+ #endif
+}
+
+// -------------------------------------------------------------------
+// Fast "random" shuffle
+// -------------------------------------------------------------------
+
+static inline uintptr_t _mi_random_shuffle(uintptr_t x) {
+ if (x==0) { x = 17; } // ensure we don't get stuck in generating zeros
+#if (MI_INTPTR_SIZE==8)
+ // by Sebastiano Vigna, see: <http://xoshiro.di.unimi.it/splitmix64.c>
+ x ^= x >> 30;
+ x *= 0xbf58476d1ce4e5b9UL;
+ x ^= x >> 27;
+ x *= 0x94d049bb133111ebUL;
+ x ^= x >> 31;
+#elif (MI_INTPTR_SIZE==4)
+ // by Chris Wellons, see: <https://nullprogram.com/blog/2018/07/31/>
+ x ^= x >> 16;
+ x *= 0x7feb352dUL;
+ x ^= x >> 15;
+ x *= 0x846ca68bUL;
+ x ^= x >> 16;
+#endif
+ return x;
+}
+
+// -------------------------------------------------------------------
+// Optimize numa node access for the common case (= one node)
+// -------------------------------------------------------------------
+
+int _mi_os_numa_node_get(mi_os_tld_t* tld);
+size_t _mi_os_numa_node_count_get(void);
+
+extern size_t _mi_numa_node_count;
+static inline int _mi_os_numa_node(mi_os_tld_t* tld) {
+ if (mi_likely(_mi_numa_node_count == 1)) return 0;
+ else return _mi_os_numa_node_get(tld);
+}
+static inline size_t _mi_os_numa_node_count(void) {
+ if (mi_likely(_mi_numa_node_count>0)) return _mi_numa_node_count;
+ else return _mi_os_numa_node_count_get();
+}
+
+
+// -------------------------------------------------------------------
+// Getting the thread id should be performant as it is called in the
+// fast path of `_mi_free` and we specialize for various platforms.
+// -------------------------------------------------------------------
+#if defined(_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+static inline uintptr_t _mi_thread_id(void) mi_attr_noexcept {
+ // Windows: works on Intel and ARM in both 32- and 64-bit
+ return (uintptr_t)NtCurrentTeb();
+}
+
+#elif defined(__GNUC__) && \
+ (defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__))
+
+// TLS register on x86 is in the FS or GS register, see: https://akkadia.org/drepper/tls.pdf
+static inline void* mi_tls_slot(size_t slot) mi_attr_noexcept {
+ void* res;
+ const size_t ofs = (slot*sizeof(void*));
+#if defined(__i386__)
+ __asm__("movl %%gs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); // 32-bit always uses GS
+#elif defined(__MACH__) && defined(__x86_64__)
+ __asm__("movq %%gs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); // x86_64 macOSX uses GS
+#elif defined(__x86_64__)
+ __asm__("movq %%fs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); // x86_64 Linux, BSD uses FS
+#elif defined(__arm__)
+ void** tcb; UNUSED(ofs);
+ asm volatile ("mrc p15, 0, %0, c13, c0, 3\nbic %0, %0, #3" : "=r" (tcb));
+ res = tcb[slot];
+#elif defined(__aarch64__)
+ void** tcb; UNUSED(ofs);
+ asm volatile ("mrs %0, tpidr_el0" : "=r" (tcb));
+ res = tcb[slot];
+#endif
+ return res;
+}
+
+// setting is only used on macOSX for now
+static inline void mi_tls_slot_set(size_t slot, void* value) mi_attr_noexcept {
+ const size_t ofs = (slot*sizeof(void*));
+#if defined(__i386__)
+ __asm__("movl %1,%%gs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); // 32-bit always uses GS
+#elif defined(__MACH__) && defined(__x86_64__)
+ __asm__("movq %1,%%gs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); // x86_64 macOSX uses GS
+#elif defined(__x86_64__)
+ __asm__("movq %1,%%fs:%1" : "=m" (*((void**)ofs)) : "rn" (value) : ); // x86_64 Linux, BSD uses FS
+#elif defined(__arm__)
+ void** tcb; UNUSED(ofs);
+ asm volatile ("mrc p15, 0, %0, c13, c0, 3\nbic %0, %0, #3" : "=r" (tcb));
+ tcb[slot] = value;
+#elif defined(__aarch64__)
+ void** tcb; UNUSED(ofs);
+ asm volatile ("mrs %0, tpidr_el0" : "=r" (tcb));
+ tcb[slot] = value;
+#endif
+}
+
+static inline uintptr_t _mi_thread_id(void) mi_attr_noexcept {
+ // in all our targets, slot 0 is the pointer to the thread control block
+ return (uintptr_t)mi_tls_slot(0);
+}
+#else
+// otherwise use standard C
+static inline uintptr_t _mi_thread_id(void) mi_attr_noexcept {
+ return (uintptr_t)&_mi_heap_default;
+}
+#endif
+
+
+#endif
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018,2019 Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#pragma once
+#ifndef MIMALLOC_NEW_DELETE_H
+#define MIMALLOC_NEW_DELETE_H
+
+// ----------------------------------------------------------------------------
+// This header provides convenient overrides for the new and
+// delete operations in C++.
+//
+// This header should be included in only one source file!
+//
+// On Windows, or when linking dynamically with mimalloc, these
+// can be more performant than the standard new-delete operations.
+// See <https://en.cppreference.com/w/cpp/memory/new/operator_new>
+// ---------------------------------------------------------------------------
+#if defined(__cplusplus)
+ #include <new>
+ #include <mimalloc.h>
+
+ void operator delete(void* p) noexcept { mi_free(p); };
+ void operator delete[](void* p) noexcept { mi_free(p); };
+
+ void* operator new(std::size_t n) noexcept(false) { return mi_new(n); }
+ void* operator new[](std::size_t n) noexcept(false) { return mi_new(n); }
+
+ void* operator new (std::size_t n, const std::nothrow_t& tag) noexcept { (void)(tag); return mi_new_nothrow(n); }
+ void* operator new[](std::size_t n, const std::nothrow_t& tag) noexcept { (void)(tag); return mi_new_nothrow(n); }
+
+ #if (__cplusplus >= 201402L || _MSC_VER >= 1916)
+ void operator delete (void* p, std::size_t n) noexcept { mi_free_size(p,n); };
+ void operator delete[](void* p, std::size_t n) noexcept { mi_free_size(p,n); };
+ #endif
+
+ #if (__cplusplus > 201402L || defined(__cpp_aligned_new))
+ void operator delete (void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
+ void operator delete[](void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
+ void operator delete (void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast<size_t>(al)); };
+ void operator delete[](void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast<size_t>(al)); };
+
+ void* operator new( std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast<size_t>(al)); }
+ void* operator new[]( std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast<size_t>(al)); }
+ void* operator new (std::size_t n, std::align_val_t al, const std::nothrow_t&) noexcept { return mi_new_aligned_nothrow(n, static_cast<size_t>(al)); }
+ void* operator new[](std::size_t n, std::align_val_t al, const std::nothrow_t&) noexcept { return mi_new_aligned_nothrow(n, static_cast<size_t>(al)); }
+ #endif
+#endif
+
+#endif // MIMALLOC_NEW_DELETE_H
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018,2019 Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#pragma once
+#ifndef MIMALLOC_OVERRIDE_H
+#define MIMALLOC_OVERRIDE_H
+
+/* ----------------------------------------------------------------------------
+This header can be used to statically redirect malloc/free and new/delete
+to the mimalloc variants. This can be useful if one can include this file on
+each source file in a project (but be careful when using external code to
+not accidentally mix pointers from different allocators).
+-----------------------------------------------------------------------------*/
+
+#include <mimalloc.h>
+
+// Standard C allocation
+#define malloc(n) mi_malloc(n)
+#define calloc(n,c) mi_calloc(n,c)
+#define realloc(p,n) mi_realloc(p,n)
+#define free(p) mi_free(p)
+
+#define strdup(s) mi_strdup(s)
+#define strndup(s) mi_strndup(s)
+#define realpath(f,n) mi_realpath(f,n)
+
+// Microsoft extensions
+#define _expand(p,n) mi_expand(p,n)
+#define _msize(p) mi_usable_size(p)
+#define _recalloc(p,n,c) mi_recalloc(p,n,c)
+
+#define _strdup(s) mi_strdup(s)
+#define _strndup(s) mi_strndup(s)
+#define _wcsdup(s) (wchar_t*)mi_wcsdup((const unsigned short*)(s))
+#define _mbsdup(s) mi_mbsdup(s)
+#define _dupenv_s(b,n,v) mi_dupenv_s(b,n,v)
+#define _wdupenv_s(b,n,v) mi_wdupenv_s((unsigned short*)(b),n,(const unsigned short*)(v))
+
+// Various Posix and Unix variants
+#define reallocf(p,n) mi_reallocf(p,n)
+#define malloc_size(p) mi_usable_size(p)
+#define malloc_usable_size(p) mi_usable_size(p)
+#define cfree(p) mi_free(p)
+
+#define valloc(n) mi_valloc(n)
+#define pvalloc(n) mi_pvalloc(n)
+#define reallocarray(p,s,n) mi_reallocarray(p,s,n)
+#define memalign(a,n) mi_memalign(a,n)
+#define aligned_alloc(a,n) mi_aligned_alloc(a,n)
+#define posix_memalign(p,a,n) mi_posix_memalign(p,a,n)
+#define _posix_memalign(p,a,n) mi_posix_memalign(p,a,n)
+
+// Microsoft aligned variants
+#define _aligned_malloc(n,a) mi_malloc_aligned(n,a)
+#define _aligned_realloc(p,n,a) mi_realloc_aligned(p,n,a)
+#define _aligned_recalloc(p,s,n,a) mi_aligned_recalloc(p,s,n,a)
+#define _aligned_msize(p,a,o) mi_usable_size(p)
+#define _aligned_free(p) mi_free(p)
+#define _aligned_offset_malloc(n,a,o) mi_malloc_aligned_at(n,a,o)
+#define _aligned_offset_realloc(p,n,a,o) mi_realloc_aligned_at(p,n,a,o)
+#define _aligned_offset_recalloc(p,s,n,a,o) mi_recalloc_aligned_at(p,s,n,a,o)
+
+#endif // MIMALLOC_OVERRIDE_H
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#pragma once
+#ifndef MIMALLOC_TYPES_H
+#define MIMALLOC_TYPES_H
+
+#include <stddef.h> // ptrdiff_t
+#include <stdint.h> // uintptr_t, uint16_t, etc
+#include <mimalloc-atomic.h> // _Atomic
+
+// Minimal alignment necessary. On most platforms 16 bytes are needed
+// due to SSE registers for example. This must be at least `MI_INTPTR_SIZE`
+#define MI_MAX_ALIGN_SIZE 16 // sizeof(max_align_t)
+
+// ------------------------------------------------------
+// Variants
+// ------------------------------------------------------
+
+// Define NDEBUG in the release version to disable assertions.
+// #define NDEBUG
+
+// Define MI_STAT as 1 to maintain statistics; set it to 2 to have detailed statistics (but costs some performance).
+// #define MI_STAT 1
+
+// Define MI_SECURE to enable security mitigations
+// #define MI_SECURE 1 // guard page around metadata
+// #define MI_SECURE 2 // guard page around each mimalloc page
+// #define MI_SECURE 3 // encode free lists (detect corrupted free list (buffer overflow), and invalid pointer free)
+// #define MI_SECURE 4 // checks for double free. (may be more expensive)
+
+#if !defined(MI_SECURE)
+#define MI_SECURE 0
+#endif
+
+// Define MI_DEBUG for debug mode
+// #define MI_DEBUG 1 // basic assertion checks and statistics, check double free, corrupted free list, and invalid pointer free.
+// #define MI_DEBUG 2 // + internal assertion checks
+// #define MI_DEBUG 3 // + extensive internal invariant checking (cmake -DMI_DEBUG_FULL=ON)
+#if !defined(MI_DEBUG)
+#if !defined(NDEBUG) || defined(_DEBUG)
+#define MI_DEBUG 2
+#else
+#define MI_DEBUG 0
+#endif
+#endif
+
+// Reserve extra padding at the end of each block to be more resilient against heap block overflows.
+// The padding can detect byte-precise buffer overflow on free.
+#if !defined(MI_PADDING) && (MI_DEBUG>=1)
+#define MI_PADDING 1
+#endif
+
+
+// Encoded free lists allow detection of corrupted free lists
+// and can detect buffer overflows, modify after free, and double `free`s.
+#if (MI_SECURE>=3 || MI_DEBUG>=1 || MI_PADDING > 0)
+#define MI_ENCODE_FREELIST 1
+#endif
+
+// ------------------------------------------------------
+// Platform specific values
+// ------------------------------------------------------
+
+// ------------------------------------------------------
+// Size of a pointer.
+// We assume that `sizeof(void*)==sizeof(intptr_t)`
+// and it holds for all platforms we know of.
+//
+// However, the C standard only requires that:
+// p == (void*)((intptr_t)p))
+// but we also need:
+// i == (intptr_t)((void*)i)
+// or otherwise one might define an intptr_t type that is larger than a pointer...
+// ------------------------------------------------------
+
+#if INTPTR_MAX == 9223372036854775807LL
+# define MI_INTPTR_SHIFT (3)
+#elif INTPTR_MAX == 2147483647LL
+# define MI_INTPTR_SHIFT (2)
+#else
+#error platform must be 32 or 64 bits
+#endif
+
+#define MI_INTPTR_SIZE (1<<MI_INTPTR_SHIFT)
+#define MI_INTPTR_BITS (MI_INTPTR_SIZE*8)
+
+#define KiB ((size_t)1024)
+#define MiB (KiB*KiB)
+#define GiB (MiB*KiB)
+
+
+// ------------------------------------------------------
+// Main internal data-structures
+// ------------------------------------------------------
+
+// Main tuning parameters for segment and page sizes
+// Sizes for 64-bit, divide by two for 32-bit
+#define MI_SMALL_PAGE_SHIFT (13 + MI_INTPTR_SHIFT) // 64kb
+#define MI_MEDIUM_PAGE_SHIFT ( 3 + MI_SMALL_PAGE_SHIFT) // 512kb
+#define MI_LARGE_PAGE_SHIFT ( 3 + MI_MEDIUM_PAGE_SHIFT) // 4mb
+#define MI_SEGMENT_SHIFT ( MI_LARGE_PAGE_SHIFT) // 4mb
+
+// Derived constants
+#define MI_SEGMENT_SIZE (1UL<<MI_SEGMENT_SHIFT)
+#define MI_SEGMENT_MASK ((uintptr_t)MI_SEGMENT_SIZE - 1)
+
+#define MI_SMALL_PAGE_SIZE (1UL<<MI_SMALL_PAGE_SHIFT)
+#define MI_MEDIUM_PAGE_SIZE (1UL<<MI_MEDIUM_PAGE_SHIFT)
+#define MI_LARGE_PAGE_SIZE (1UL<<MI_LARGE_PAGE_SHIFT)
+
+#define MI_SMALL_PAGES_PER_SEGMENT (MI_SEGMENT_SIZE/MI_SMALL_PAGE_SIZE)
+#define MI_MEDIUM_PAGES_PER_SEGMENT (MI_SEGMENT_SIZE/MI_MEDIUM_PAGE_SIZE)
+#define MI_LARGE_PAGES_PER_SEGMENT (MI_SEGMENT_SIZE/MI_LARGE_PAGE_SIZE)
+
+// The max object size are checked to not waste more than 12.5% internally over the page sizes.
+// (Except for large pages since huge objects are allocated in 4MiB chunks)
+#define MI_SMALL_OBJ_SIZE_MAX (MI_SMALL_PAGE_SIZE/4) // 16kb
+#define MI_MEDIUM_OBJ_SIZE_MAX (MI_MEDIUM_PAGE_SIZE/4) // 128kb
+#define MI_LARGE_OBJ_SIZE_MAX (MI_LARGE_PAGE_SIZE/2) // 2mb
+#define MI_LARGE_OBJ_WSIZE_MAX (MI_LARGE_OBJ_SIZE_MAX/MI_INTPTR_SIZE)
+#define MI_HUGE_OBJ_SIZE_MAX (2*MI_INTPTR_SIZE*MI_SEGMENT_SIZE) // (must match MI_REGION_MAX_ALLOC_SIZE in memory.c)
+
+// Maximum number of size classes. (spaced exponentially in 12.5% increments)
+#define MI_BIN_HUGE (73U)
+
+#if (MI_LARGE_OBJ_WSIZE_MAX >= 655360)
+#error "define more bins"
+#endif
+
+// Used as a special value to encode block sizes in 32 bits.
+#define MI_HUGE_BLOCK_SIZE ((uint32_t)MI_HUGE_OBJ_SIZE_MAX)
+
+// The free lists use encoded next fields
+// (Only actually encodes when MI_ENCODED_FREELIST is defined.)
+typedef uintptr_t mi_encoded_t;
+
+// free lists contain blocks
+typedef struct mi_block_s {
+ mi_encoded_t next;
+} mi_block_t;
+
+
+// The delayed flags are used for efficient multi-threaded free-ing
+typedef enum mi_delayed_e {
+ MI_USE_DELAYED_FREE = 0, // push on the owning heap thread delayed list
+ MI_DELAYED_FREEING = 1, // temporary: another thread is accessing the owning heap
+ MI_NO_DELAYED_FREE = 2, // optimize: push on page local thread free queue if another block is already in the heap thread delayed free list
+ MI_NEVER_DELAYED_FREE = 3 // sticky, only resets on page reclaim
+} mi_delayed_t;
+
+
+// The `in_full` and `has_aligned` page flags are put in a union to efficiently
+// test if both are false (`full_aligned == 0`) in the `mi_free` routine.
+typedef union mi_page_flags_s {
+ uint8_t full_aligned;
+ struct {
+ uint8_t in_full : 1;
+ uint8_t has_aligned : 1;
+ } x;
+} mi_page_flags_t;
+
+// Thread free list.
+// We use the bottom 2 bits of the pointer for mi_delayed_t flags
+typedef uintptr_t mi_thread_free_t;
+
+// A page contains blocks of one specific size (`block_size`).
+// Each page has three list of free blocks:
+// `free` for blocks that can be allocated,
+// `local_free` for freed blocks that are not yet available to `mi_malloc`
+// `thread_free` for freed blocks by other threads
+// The `local_free` and `thread_free` lists are migrated to the `free` list
+// when it is exhausted. The separate `local_free` list is necessary to
+// implement a monotonic heartbeat. The `thread_free` list is needed for
+// avoiding atomic operations in the common case.
+//
+//
+// `used - |thread_free|` == actual blocks that are in use (alive)
+// `used - |thread_free| + |free| + |local_free| == capacity`
+//
+// We don't count `freed` (as |free|) but use `used` to reduce
+// the number of memory accesses in the `mi_page_all_free` function(s).
+//
+// Notes:
+// - Access is optimized for `mi_free` and `mi_page_alloc` (in `alloc.c`)
+// - Using `uint16_t` does not seem to slow things down
+// - The size is 8 words on 64-bit which helps the page index calculations
+// (and 10 words on 32-bit, and encoded free lists add 2 words. Sizes 10
+// and 12 are still good for address calculation)
+// - To limit the structure size, the `xblock_size` is 32-bits only; for
+// blocks > MI_HUGE_BLOCK_SIZE the size is determined from the segment page size
+// - `thread_free` uses the bottom bits as a delayed-free flags to optimize
+// concurrent frees where only the first concurrent free adds to the owning
+// heap `thread_delayed_free` list (see `alloc.c:mi_free_block_mt`).
+// The invariant is that no-delayed-free is only set if there is
+// at least one block that will be added, or as already been added, to
+// the owning heap `thread_delayed_free` list. This guarantees that pages
+// will be freed correctly even if only other threads free blocks.
+typedef struct mi_page_s {
+ // "owned" by the segment
+ uint8_t segment_idx; // index in the segment `pages` array, `page == &segment->pages[page->segment_idx]`
+ uint8_t segment_in_use:1; // `true` if the segment allocated this page
+ uint8_t is_reset:1; // `true` if the page memory was reset
+ uint8_t is_committed:1; // `true` if the page virtual memory is committed
+ uint8_t is_zero_init:1; // `true` if the page was zero initialized
+
+ // layout like this to optimize access in `mi_malloc` and `mi_free`
+ uint16_t capacity; // number of blocks committed, must be the first field, see `segment.c:page_clear`
+ uint16_t reserved; // number of blocks reserved in memory
+ mi_page_flags_t flags; // `in_full` and `has_aligned` flags (8 bits)
+ uint8_t is_zero:1; // `true` if the blocks in the free list are zero initialized
+ uint8_t retire_expire:7; // expiration count for retired blocks
+
+ mi_block_t* free; // list of available free blocks (`malloc` allocates from this list)
+ #ifdef MI_ENCODE_FREELIST
+ uintptr_t keys[2]; // two random keys to encode the free lists (see `_mi_block_next`)
+ #endif
+ uint32_t used; // number of blocks in use (including blocks in `local_free` and `thread_free`)
+ uint32_t xblock_size; // size available in each block (always `>0`)
+
+ mi_block_t* local_free; // list of deferred free blocks by this thread (migrates to `free`)
+ volatile _Atomic(mi_thread_free_t) xthread_free; // list of deferred free blocks freed by other threads
+ volatile _Atomic(uintptr_t) xheap;
+
+ struct mi_page_s* next; // next page owned by this thread with the same `block_size`
+ struct mi_page_s* prev; // previous page owned by this thread with the same `block_size`
+} mi_page_t;
+
+
+
+typedef enum mi_page_kind_e {
+ MI_PAGE_SMALL, // small blocks go into 64kb pages inside a segment
+ MI_PAGE_MEDIUM, // medium blocks go into 512kb pages inside a segment
+ MI_PAGE_LARGE, // larger blocks go into a single page spanning a whole segment
+ MI_PAGE_HUGE // huge blocks (>512kb) are put into a single page in a segment of the exact size (but still 2mb aligned)
+} mi_page_kind_t;
+
+// Segments are large allocated memory blocks (2mb on 64 bit) from
+// the OS. Inside segments we allocated fixed size _pages_ that
+// contain blocks.
+typedef struct mi_segment_s {
+ // memory fields
+ size_t memid; // id for the os-level memory manager
+ bool mem_is_fixed; // `true` if we cannot decommit/reset/protect in this memory (i.e. when allocated using large OS pages)
+ bool mem_is_committed; // `true` if the whole segment is eagerly committed
+
+ // segment fields
+ struct mi_segment_s* next; // must be the first segment field -- see `segment.c:segment_alloc`
+ struct mi_segment_s* prev;
+ struct mi_segment_s* abandoned_next;
+ size_t abandoned; // abandoned pages (i.e. the original owning thread stopped) (`abandoned <= used`)
+ size_t abandoned_visits; // count how often this segment is visited in the abandoned list (to force reclaim it it is too long)
+
+ size_t used; // count of pages in use (`used <= capacity`)
+ size_t capacity; // count of available pages (`#free + used`)
+ size_t segment_size;// for huge pages this may be different from `MI_SEGMENT_SIZE`
+ size_t segment_info_size; // space we are using from the first page for segment meta-data and possible guard pages.
+ uintptr_t cookie; // verify addresses in secure mode: `_mi_ptr_cookie(segment) == segment->cookie`
+
+ // layout like this to optimize access in `mi_free`
+ size_t page_shift; // `1 << page_shift` == the page sizes == `page->block_size * page->reserved` (unless the first page, then `-segment_info_size`).
+ volatile _Atomic(uintptr_t) thread_id; // unique id of the thread owning this segment
+ mi_page_kind_t page_kind; // kind of pages: small, large, or huge
+ mi_page_t pages[1]; // up to `MI_SMALL_PAGES_PER_SEGMENT` pages
+} mi_segment_t;
+
+
+// ------------------------------------------------------
+// Heaps
+// Provide first-class heaps to allocate from.
+// A heap just owns a set of pages for allocation and
+// can only be allocate/reallocate from the thread that created it.
+// Freeing blocks can be done from any thread though.
+// Per thread, the segments are shared among its heaps.
+// Per thread, there is always a default heap that is
+// used for allocation; it is initialized to statically
+// point to an empty heap to avoid initialization checks
+// in the fast path.
+// ------------------------------------------------------
+
+// Thread local data
+typedef struct mi_tld_s mi_tld_t;
+
+// Pages of a certain block size are held in a queue.
+typedef struct mi_page_queue_s {
+ mi_page_t* first;
+ mi_page_t* last;
+ size_t block_size;
+} mi_page_queue_t;
+
+#define MI_BIN_FULL (MI_BIN_HUGE+1)
+
+// Random context
+typedef struct mi_random_cxt_s {
+ uint32_t input[16];
+ uint32_t output[16];
+ int output_available;
+} mi_random_ctx_t;
+
+
+// In debug mode there is a padding stucture at the end of the blocks to check for buffer overflows
+#if (MI_PADDING)
+typedef struct mi_padding_s {
+ uint32_t canary; // encoded block value to check validity of the padding (in case of overflow)
+ uint32_t delta; // padding bytes before the block. (mi_usable_size(p) - delta == exact allocated bytes)
+} mi_padding_t;
+#define MI_PADDING_SIZE (sizeof(mi_padding_t))
+#define MI_PADDING_WSIZE ((MI_PADDING_SIZE + MI_INTPTR_SIZE - 1) / MI_INTPTR_SIZE)
+#else
+#define MI_PADDING_SIZE 0
+#define MI_PADDING_WSIZE 0
+#endif
+
+#define MI_PAGES_DIRECT (MI_SMALL_WSIZE_MAX + MI_PADDING_WSIZE + 1)
+
+
+// A heap owns a set of pages.
+struct mi_heap_s {
+ mi_tld_t* tld;
+ mi_page_t* pages_free_direct[MI_PAGES_DIRECT]; // optimize: array where every entry points a page with possibly free blocks in the corresponding queue for that size.
+ mi_page_queue_t pages[MI_BIN_FULL + 1]; // queue of pages for each size class (or "bin")
+ volatile _Atomic(mi_block_t*) thread_delayed_free;
+ uintptr_t thread_id; // thread this heap belongs too
+ uintptr_t cookie; // random cookie to verify pointers (see `_mi_ptr_cookie`)
+ uintptr_t keys[2]; // two random keys used to encode the `thread_delayed_free` list
+ mi_random_ctx_t random; // random number context used for secure allocation
+ size_t page_count; // total number of pages in the `pages` queues.
+ size_t page_retired_min; // smallest retired index (retired pages are fully free, but still in the page queues)
+ size_t page_retired_max; // largest retired index into the `pages` array.
+ mi_heap_t* next; // list of heaps per thread
+ bool no_reclaim; // `true` if this heap should not reclaim abandoned pages
+};
+
+
+
+// ------------------------------------------------------
+// Debug
+// ------------------------------------------------------
+
+#define MI_DEBUG_UNINIT (0xD0)
+#define MI_DEBUG_FREED (0xDF)
+#define MI_DEBUG_PADDING (0xDE)
+
+#if (MI_DEBUG)
+// use our own assertion to print without memory allocation
+void _mi_assert_fail(const char* assertion, const char* fname, unsigned int line, const char* func );
+#define mi_assert(expr) ((expr) ? (void)0 : _mi_assert_fail(#expr,__FILE__,__LINE__,__func__))
+#else
+#define mi_assert(x)
+#endif
+
+#if (MI_DEBUG>1)
+#define mi_assert_internal mi_assert
+#else
+#define mi_assert_internal(x)
+#endif
+
+#if (MI_DEBUG>2)
+#define mi_assert_expensive mi_assert
+#else
+#define mi_assert_expensive(x)
+#endif
+
+// ------------------------------------------------------
+// Statistics
+// ------------------------------------------------------
+
+#ifndef MI_STAT
+#if (MI_DEBUG>0)
+#define MI_STAT 2
+#else
+#define MI_STAT 0
+#endif
+#endif
+
+typedef struct mi_stat_count_s {
+ int64_t allocated;
+ int64_t freed;
+ int64_t peak;
+ int64_t current;
+} mi_stat_count_t;
+
+typedef struct mi_stat_counter_s {
+ int64_t total;
+ int64_t count;
+} mi_stat_counter_t;
+
+typedef struct mi_stats_s {
+ mi_stat_count_t segments;
+ mi_stat_count_t pages;
+ mi_stat_count_t reserved;
+ mi_stat_count_t committed;
+ mi_stat_count_t reset;
+ mi_stat_count_t page_committed;
+ mi_stat_count_t segments_abandoned;
+ mi_stat_count_t pages_abandoned;
+ mi_stat_count_t threads;
+ mi_stat_count_t huge;
+ mi_stat_count_t giant;
+ mi_stat_count_t malloc;
+ mi_stat_count_t segments_cache;
+ mi_stat_counter_t pages_extended;
+ mi_stat_counter_t mmap_calls;
+ mi_stat_counter_t commit_calls;
+ mi_stat_counter_t page_no_retire;
+ mi_stat_counter_t searches;
+ mi_stat_counter_t huge_count;
+ mi_stat_counter_t giant_count;
+#if MI_STAT>1
+ mi_stat_count_t normal[MI_BIN_HUGE+1];
+#endif
+} mi_stats_t;
+
+
+void _mi_stat_increase(mi_stat_count_t* stat, size_t amount);
+void _mi_stat_decrease(mi_stat_count_t* stat, size_t amount);
+void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount);
+
+#if (MI_STAT)
+#define mi_stat_increase(stat,amount) _mi_stat_increase( &(stat), amount)
+#define mi_stat_decrease(stat,amount) _mi_stat_decrease( &(stat), amount)
+#define mi_stat_counter_increase(stat,amount) _mi_stat_counter_increase( &(stat), amount)
+#else
+#define mi_stat_increase(stat,amount) (void)0
+#define mi_stat_decrease(stat,amount) (void)0
+#define mi_stat_counter_increase(stat,amount) (void)0
+#endif
+
+#define mi_heap_stat_increase(heap,stat,amount) mi_stat_increase( (heap)->tld->stats.stat, amount)
+#define mi_heap_stat_decrease(heap,stat,amount) mi_stat_decrease( (heap)->tld->stats.stat, amount)
+
+// ------------------------------------------------------
+// Thread Local data
+// ------------------------------------------------------
+
+typedef int64_t mi_msecs_t;
+
+// Queue of segments
+typedef struct mi_segment_queue_s {
+ mi_segment_t* first;
+ mi_segment_t* last;
+} mi_segment_queue_t;
+
+// OS thread local data
+typedef struct mi_os_tld_s {
+ size_t region_idx; // start point for next allocation
+ mi_stats_t* stats; // points to tld stats
+} mi_os_tld_t;
+
+// Segments thread local data
+typedef struct mi_segments_tld_s {
+ mi_segment_queue_t small_free; // queue of segments with free small pages
+ mi_segment_queue_t medium_free; // queue of segments with free medium pages
+ mi_page_queue_t pages_reset; // queue of freed pages that can be reset
+ size_t count; // current number of segments;
+ size_t peak_count; // peak number of segments
+ size_t current_size; // current size of all segments
+ size_t peak_size; // peak size of all segments
+ size_t cache_count; // number of segments in the cache
+ size_t cache_size; // total size of all segments in the cache
+ mi_segment_t* cache; // (small) cache of segments
+ mi_stats_t* stats; // points to tld stats
+ mi_os_tld_t* os; // points to os stats
+} mi_segments_tld_t;
+
+// Thread local data
+struct mi_tld_s {
+ unsigned long long heartbeat; // monotonic heartbeat count
+ bool recurse; // true if deferred was called; used to prevent infinite recursion.
+ mi_heap_t* heap_backing; // backing heap of this thread (cannot be deleted)
+ mi_heap_t* heaps; // list of heaps in this thread (so we can abandon all when the thread terminates)
+ mi_segments_tld_t segments; // segment tld
+ mi_os_tld_t os; // os tld
+ mi_stats_t stats; // statistics
+};
+
+#endif
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2020, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#pragma once
+#ifndef MIMALLOC_H
+#define MIMALLOC_H
+
+#define MI_MALLOC_VERSION 164 // major + 2 digits minor
+
+// ------------------------------------------------------
+// Compiler specific attributes
+// ------------------------------------------------------
+
+#ifdef __cplusplus
+ #if (__cplusplus >= 201103L) || (_MSC_VER > 1900) // C++11
+ #define mi_attr_noexcept noexcept
+ #else
+ #define mi_attr_noexcept throw()
+ #endif
+#else
+ #define mi_attr_noexcept
+#endif
+
+#if (__cplusplus >= 201703)
+ #define mi_decl_nodiscard [[nodiscard]]
+#elif (__GNUC__ >= 4) || defined(__clang__) // includes clang, icc, and clang-cl
+ #define mi_decl_nodiscard __attribute__((warn_unused_result))
+#elif (_MSC_VER >= 1700)
+ #define mi_decl_nodiscard _Check_return_
+#else
+ #define mi_decl_nodiscard
+#endif
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+ #if !defined(MI_SHARED_LIB)
+ #define mi_decl_export
+ #elif defined(MI_SHARED_LIB_EXPORT)
+ #define mi_decl_export __declspec(dllexport)
+ #else
+ #define mi_decl_export __declspec(dllimport)
+ #endif
+ #if defined(__MINGW32__)
+ #define mi_decl_restrict
+ #define mi_attr_malloc __attribute__((malloc))
+ #else
+ #if (_MSC_VER >= 1900) && !defined(__EDG__)
+ #define mi_decl_restrict __declspec(allocator) __declspec(restrict)
+ #else
+ #define mi_decl_restrict __declspec(restrict)
+ #endif
+ #define mi_attr_malloc
+ #endif
+ #define mi_cdecl __cdecl
+ #define mi_attr_alloc_size(s)
+ #define mi_attr_alloc_size2(s1,s2)
+ #define mi_attr_alloc_align(p)
+#elif defined(__GNUC__) // includes clang and icc
+ #define mi_cdecl // leads to warnings... __attribute__((cdecl))
+ #define mi_decl_export __attribute__((visibility("default")))
+ #define mi_decl_restrict
+ #define mi_attr_malloc __attribute__((malloc))
+ #if (defined(__clang_major__) && (__clang_major__ < 4)) || (__GNUC__ < 5)
+ #define mi_attr_alloc_size(s)
+ #define mi_attr_alloc_size2(s1,s2)
+ #define mi_attr_alloc_align(p)
+ #elif defined(__INTEL_COMPILER)
+ #define mi_attr_alloc_size(s) __attribute__((alloc_size(s)))
+ #define mi_attr_alloc_size2(s1,s2) __attribute__((alloc_size(s1,s2)))
+ #define mi_attr_alloc_align(p)
+ #else
+ #define mi_attr_alloc_size(s) __attribute__((alloc_size(s)))
+ #define mi_attr_alloc_size2(s1,s2) __attribute__((alloc_size(s1,s2)))
+ #define mi_attr_alloc_align(p) __attribute__((alloc_align(p)))
+ #endif
+#else
+ #define mi_cdecl
+ #define mi_decl_export
+ #define mi_decl_restrict
+ #define mi_attr_malloc
+ #define mi_attr_alloc_size(s)
+ #define mi_attr_alloc_size2(s1,s2)
+ #define mi_attr_alloc_align(p)
+#endif
+
+// ------------------------------------------------------
+// Includes
+// ------------------------------------------------------
+
+#include <stddef.h> // size_t
+#include <stdbool.h> // bool
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ------------------------------------------------------
+// Standard malloc interface
+// ------------------------------------------------------
+
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_malloc(size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_calloc(size_t count, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(1,2);
+mi_decl_nodiscard mi_decl_export void* mi_realloc(void* p, size_t newsize) mi_attr_noexcept mi_attr_alloc_size(2);
+mi_decl_export void* mi_expand(void* p, size_t newsize) mi_attr_noexcept mi_attr_alloc_size(2);
+
+mi_decl_export void mi_free(void* p) mi_attr_noexcept;
+mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_strdup(const char* s) mi_attr_noexcept mi_attr_malloc;
+mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_attr_noexcept mi_attr_malloc;
+mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_realpath(const char* fname, char* resolved_name) mi_attr_noexcept mi_attr_malloc;
+
+// ------------------------------------------------------
+// Extended functionality
+// ------------------------------------------------------
+#define MI_SMALL_WSIZE_MAX (128)
+#define MI_SMALL_SIZE_MAX (MI_SMALL_WSIZE_MAX*sizeof(void*))
+
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_malloc_small(size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_zalloc_small(size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_zalloc(size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1);
+
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_mallocn(size_t count, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(1,2);
+mi_decl_nodiscard mi_decl_export void* mi_reallocn(void* p, size_t count, size_t size) mi_attr_noexcept mi_attr_alloc_size2(2,3);
+mi_decl_nodiscard mi_decl_export void* mi_reallocf(void* p, size_t newsize) mi_attr_noexcept mi_attr_alloc_size(2);
+
+mi_decl_nodiscard mi_decl_export size_t mi_usable_size(const void* p) mi_attr_noexcept;
+mi_decl_nodiscard mi_decl_export size_t mi_good_size(size_t size) mi_attr_noexcept;
+
+
+// ------------------------------------------------------
+// Internals
+// ------------------------------------------------------
+
+typedef void (mi_cdecl mi_deferred_free_fun)(bool force, unsigned long long heartbeat, void* arg);
+mi_decl_export void mi_register_deferred_free(mi_deferred_free_fun* deferred_free, void* arg) mi_attr_noexcept;
+
+typedef void (mi_cdecl mi_output_fun)(const char* msg, void* arg);
+mi_decl_export void mi_register_output(mi_output_fun* out, void* arg) mi_attr_noexcept;
+
+typedef void (mi_cdecl mi_error_fun)(int err, void* arg);
+mi_decl_export void mi_register_error(mi_error_fun* fun, void* arg);
+
+mi_decl_export void mi_collect(bool force) mi_attr_noexcept;
+mi_decl_export int mi_version(void) mi_attr_noexcept;
+mi_decl_export void mi_stats_reset(void) mi_attr_noexcept;
+mi_decl_export void mi_stats_merge(void) mi_attr_noexcept;
+mi_decl_export void mi_stats_print(void* out) mi_attr_noexcept; // backward compatibility: `out` is ignored and should be NULL
+mi_decl_export void mi_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept;
+
+mi_decl_export void mi_process_init(void) mi_attr_noexcept;
+mi_decl_export void mi_thread_init(void) mi_attr_noexcept;
+mi_decl_export void mi_thread_done(void) mi_attr_noexcept;
+mi_decl_export void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept;
+
+
+// -------------------------------------------------------------------------------------
+// Aligned allocation
+// Note that `alignment` always follows `size` for consistency with unaligned
+// allocation, but unfortunately this differs from `posix_memalign` and `aligned_alloc`.
+// -------------------------------------------------------------------------------------
+
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_malloc_aligned(size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1) mi_attr_alloc_align(2);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_zalloc_aligned(size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1) mi_attr_alloc_align(2);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_calloc_aligned(size_t count, size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(1,2) mi_attr_alloc_align(3);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(1,2);
+mi_decl_nodiscard mi_decl_export void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept mi_attr_alloc_size(2) mi_attr_alloc_align(3);
+mi_decl_nodiscard mi_decl_export void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_alloc_size(2);
+
+
+// -------------------------------------------------------------------------------------
+// Heaps: first-class, but can only allocate from the same thread that created it.
+// -------------------------------------------------------------------------------------
+
+struct mi_heap_s;
+typedef struct mi_heap_s mi_heap_t;
+
+mi_decl_nodiscard mi_decl_export mi_heap_t* mi_heap_new(void);
+mi_decl_export void mi_heap_delete(mi_heap_t* heap);
+mi_decl_export void mi_heap_destroy(mi_heap_t* heap);
+mi_decl_export mi_heap_t* mi_heap_set_default(mi_heap_t* heap);
+mi_decl_export mi_heap_t* mi_heap_get_default(void);
+mi_decl_export mi_heap_t* mi_heap_get_backing(void);
+mi_decl_export void mi_heap_collect(mi_heap_t* heap, bool force) mi_attr_noexcept;
+
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_zalloc(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(2, 3);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_mallocn(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(2, 3);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2);
+
+mi_decl_nodiscard mi_decl_export void* mi_heap_realloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept mi_attr_alloc_size(3);
+mi_decl_nodiscard mi_decl_export void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept mi_attr_alloc_size2(3,4);;
+mi_decl_nodiscard mi_decl_export void* mi_heap_reallocf(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept mi_attr_alloc_size(3);
+
+mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_heap_strdup(mi_heap_t* heap, const char* s) mi_attr_noexcept mi_attr_malloc;
+mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n) mi_attr_noexcept mi_attr_malloc;
+mi_decl_nodiscard mi_decl_export mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept mi_attr_malloc;
+
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_malloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2) mi_attr_alloc_align(3);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_malloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_zalloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2) mi_attr_alloc_align(3);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_zalloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_calloc_aligned(mi_heap_t* heap, size_t count, size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(2, 3) mi_attr_alloc_align(4);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_calloc_aligned_at(mi_heap_t* heap, size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(2, 3);
+mi_decl_nodiscard mi_decl_export void* mi_heap_realloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept mi_attr_alloc_size(3) mi_attr_alloc_align(4);
+mi_decl_nodiscard mi_decl_export void* mi_heap_realloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_alloc_size(3);
+
+
+// --------------------------------------------------------------------------------
+// Zero initialized re-allocation.
+// Only valid on memory that was originally allocated with zero initialization too.
+// e.g. `mi_calloc`, `mi_zalloc`, `mi_zalloc_aligned` etc.
+// see <https://github.com/microsoft/mimalloc/issues/63#issuecomment-508272992>
+// --------------------------------------------------------------------------------
+
+mi_decl_nodiscard mi_decl_export void* mi_rezalloc(void* p, size_t newsize) mi_attr_noexcept mi_attr_alloc_size(2);
+mi_decl_nodiscard mi_decl_export void* mi_recalloc(void* p, size_t newcount, size_t size) mi_attr_noexcept mi_attr_alloc_size2(2,3);
+
+mi_decl_nodiscard mi_decl_export void* mi_rezalloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept mi_attr_alloc_size(2) mi_attr_alloc_align(3);
+mi_decl_nodiscard mi_decl_export void* mi_rezalloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_alloc_size(2);
+mi_decl_nodiscard mi_decl_export void* mi_recalloc_aligned(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept mi_attr_alloc_size2(2,3) mi_attr_alloc_align(4);
+mi_decl_nodiscard mi_decl_export void* mi_recalloc_aligned_at(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_alloc_size2(2,3);
+
+mi_decl_nodiscard mi_decl_export void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept mi_attr_alloc_size(3);
+mi_decl_nodiscard mi_decl_export void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t newcount, size_t size) mi_attr_noexcept mi_attr_alloc_size2(3,4);
+
+mi_decl_nodiscard mi_decl_export void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept mi_attr_alloc_size(3) mi_attr_alloc_align(4);
+mi_decl_nodiscard mi_decl_export void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_alloc_size(3);
+mi_decl_nodiscard mi_decl_export void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept mi_attr_alloc_size2(3,4) mi_attr_alloc_align(5);
+mi_decl_nodiscard mi_decl_export void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_alloc_size2(3,4);
+
+
+// ------------------------------------------------------
+// Analysis
+// ------------------------------------------------------
+
+mi_decl_export bool mi_heap_contains_block(mi_heap_t* heap, const void* p);
+mi_decl_export bool mi_heap_check_owned(mi_heap_t* heap, const void* p);
+mi_decl_export bool mi_check_owned(const void* p);
+
+// An area of heap space contains blocks of a single size.
+typedef struct mi_heap_area_s {
+ void* blocks; // start of the area containing heap blocks
+ size_t reserved; // bytes reserved for this area (virtual)
+ size_t committed; // current available bytes for this area
+ size_t used; // bytes in use by allocated blocks
+ size_t block_size; // size in bytes of each block
+} mi_heap_area_t;
+
+typedef bool (mi_cdecl mi_block_visit_fun)(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg);
+
+mi_decl_export bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_all_blocks, mi_block_visit_fun* visitor, void* arg);
+
+// Experimental
+mi_decl_nodiscard mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept;
+mi_decl_nodiscard mi_decl_export bool mi_is_redirected() mi_attr_noexcept;
+
+mi_decl_export int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs) mi_attr_noexcept;
+mi_decl_export int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs) mi_attr_noexcept;
+
+// deprecated
+mi_decl_export int mi_reserve_huge_os_pages(size_t pages, double max_secs, size_t* pages_reserved) mi_attr_noexcept;
+
+
+// ------------------------------------------------------
+// Convenience
+// ------------------------------------------------------
+
+#define mi_malloc_tp(tp) ((tp*)mi_malloc(sizeof(tp)))
+#define mi_zalloc_tp(tp) ((tp*)mi_zalloc(sizeof(tp)))
+#define mi_calloc_tp(tp,n) ((tp*)mi_calloc(n,sizeof(tp)))
+#define mi_mallocn_tp(tp,n) ((tp*)mi_mallocn(n,sizeof(tp)))
+#define mi_reallocn_tp(p,tp,n) ((tp*)mi_reallocn(p,n,sizeof(tp)))
+#define mi_recalloc_tp(p,tp,n) ((tp*)mi_recalloc(p,n,sizeof(tp)))
+
+#define mi_heap_malloc_tp(hp,tp) ((tp*)mi_heap_malloc(hp,sizeof(tp)))
+#define mi_heap_zalloc_tp(hp,tp) ((tp*)mi_heap_zalloc(hp,sizeof(tp)))
+#define mi_heap_calloc_tp(hp,tp,n) ((tp*)mi_heap_calloc(hp,n,sizeof(tp)))
+#define mi_heap_mallocn_tp(hp,tp,n) ((tp*)mi_heap_mallocn(hp,n,sizeof(tp)))
+#define mi_heap_reallocn_tp(hp,p,tp,n) ((tp*)mi_heap_reallocn(hp,p,n,sizeof(tp)))
+#define mi_heap_recalloc_tp(hp,p,tp,n) ((tp*)mi_heap_recalloc(hp,p,n,sizeof(tp)))
+
+
+// ------------------------------------------------------
+// Options, all `false` by default
+// ------------------------------------------------------
+
+typedef enum mi_option_e {
+ // stable options
+ mi_option_show_errors,
+ mi_option_show_stats,
+ mi_option_verbose,
+ // the following options are experimental
+ mi_option_eager_commit,
+ mi_option_eager_region_commit,
+ mi_option_reset_decommits,
+ mi_option_large_os_pages, // implies eager commit
+ mi_option_reserve_huge_os_pages,
+ mi_option_segment_cache,
+ mi_option_page_reset,
+ mi_option_abandoned_page_reset,
+ mi_option_segment_reset,
+ mi_option_eager_commit_delay,
+ mi_option_reset_delay,
+ mi_option_use_numa_nodes,
+ mi_option_os_tag,
+ mi_option_max_errors,
+ _mi_option_last
+} mi_option_t;
+
+
+mi_decl_nodiscard mi_decl_export bool mi_option_is_enabled(mi_option_t option);
+mi_decl_export void mi_option_enable(mi_option_t option);
+mi_decl_export void mi_option_disable(mi_option_t option);
+mi_decl_export void mi_option_set_enabled(mi_option_t option, bool enable);
+mi_decl_export void mi_option_set_enabled_default(mi_option_t option, bool enable);
+
+mi_decl_nodiscard mi_decl_export long mi_option_get(mi_option_t option);
+mi_decl_export void mi_option_set(mi_option_t option, long value);
+mi_decl_export void mi_option_set_default(mi_option_t option, long value);
+
+
+// -------------------------------------------------------------------------------------------------------
+// "mi" prefixed implementations of various posix, Unix, Windows, and C++ allocation functions.
+// (This can be convenient when providing overrides of these functions as done in `mimalloc-override.h`.)
+// note: we use `mi_cfree` as "checked free" and it checks if the pointer is in our heap before free-ing.
+// -------------------------------------------------------------------------------------------------------
+
+mi_decl_export void mi_cfree(void* p) mi_attr_noexcept;
+mi_decl_export void* mi__expand(void* p, size_t newsize) mi_attr_noexcept;
+mi_decl_nodiscard mi_decl_export size_t mi_malloc_size(const void* p) mi_attr_noexcept;
+mi_decl_nodiscard mi_decl_export size_t mi_malloc_usable_size(const void *p) mi_attr_noexcept;
+
+mi_decl_export int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept;
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_memalign(size_t alignment, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2) mi_attr_alloc_align(1);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_valloc(size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_pvalloc(size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_aligned_alloc(size_t alignment, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2) mi_attr_alloc_align(1);
+
+mi_decl_nodiscard mi_decl_export void* mi_reallocarray(void* p, size_t count, size_t size) mi_attr_noexcept mi_attr_alloc_size2(2,3);
+mi_decl_nodiscard mi_decl_export void* mi_aligned_recalloc(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept;
+mi_decl_nodiscard mi_decl_export void* mi_aligned_offset_recalloc(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept;
+
+mi_decl_nodiscard mi_decl_export mi_decl_restrict unsigned short* mi_wcsdup(const unsigned short* s) mi_attr_noexcept mi_attr_malloc;
+mi_decl_nodiscard mi_decl_export mi_decl_restrict unsigned char* mi_mbsdup(const unsigned char* s) mi_attr_noexcept mi_attr_malloc;
+mi_decl_export int mi_dupenv_s(char** buf, size_t* size, const char* name) mi_attr_noexcept;
+mi_decl_export int mi_wdupenv_s(unsigned short** buf, size_t* size, const unsigned short* name) mi_attr_noexcept;
+
+mi_decl_export void mi_free_size(void* p, size_t size) mi_attr_noexcept;
+mi_decl_export void mi_free_size_aligned(void* p, size_t size, size_t alignment) mi_attr_noexcept;
+mi_decl_export void mi_free_aligned(void* p, size_t alignment) mi_attr_noexcept;
+
+// The `mi_new` wrappers implement C++ semantics on out-of-memory instead of directly returning `NULL`.
+// (and call `std::get_new_handler` and potentially raise a `std::bad_alloc` exception).
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_new(size_t size) mi_attr_malloc mi_attr_alloc_size(1);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_new_aligned(size_t size, size_t alignment) mi_attr_malloc mi_attr_alloc_size(1) mi_attr_alloc_align(2);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_new_nothrow(size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_new_aligned_nothrow(size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1) mi_attr_alloc_align(2);
+mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_new_n(size_t count, size_t size) mi_attr_malloc mi_attr_alloc_size2(1, 2);
+mi_decl_nodiscard mi_decl_export void* mi_new_realloc(void* p, size_t newsize) mi_attr_alloc_size(2);
+mi_decl_nodiscard mi_decl_export void* mi_new_reallocn(void* p, size_t newcount, size_t size) mi_attr_alloc_size2(2, 3);
+
+#ifdef __cplusplus
+}
+#endif
+
+// ---------------------------------------------------------------------------------------------
+// Implement the C++ std::allocator interface for use in STL containers.
+// (note: see `mimalloc-new-delete.h` for overriding the new/delete operators globally)
+// ---------------------------------------------------------------------------------------------
+#ifdef __cplusplus
+
+#include <cstdint> // PTRDIFF_MAX
+#if (__cplusplus >= 201103L) || (_MSC_VER > 1900) // C++11
+#include <type_traits> // std::true_type
+#include <utility> // std::forward
+#endif
+
+template<class T> struct mi_stl_allocator {
+ typedef T value_type;
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef value_type& reference;
+ typedef value_type const& const_reference;
+ typedef value_type* pointer;
+ typedef value_type const* const_pointer;
+ template <class U> struct rebind { typedef mi_stl_allocator<U> other; };
+
+ mi_stl_allocator() mi_attr_noexcept = default;
+ mi_stl_allocator(const mi_stl_allocator&) mi_attr_noexcept = default;
+ template<class U> mi_stl_allocator(const mi_stl_allocator<U>&) mi_attr_noexcept { }
+ mi_stl_allocator select_on_container_copy_construction() const { return *this; }
+ void deallocate(T* p, size_type) { mi_free(p); }
+
+ #if (__cplusplus >= 201703L) // C++17
+ mi_decl_nodiscard T* allocate(size_type count) { return static_cast<T*>(mi_new_n(count, sizeof(T))); }
+ mi_decl_nodiscard T* allocate(size_type count, const void*) { return allocate(count); }
+ #else
+ mi_decl_nodiscard pointer allocate(size_type count, const void* = 0) { return static_cast<pointer>(mi_new_n(count, sizeof(value_type))); }
+ #endif
+
+ #if ((__cplusplus >= 201103L) || (_MSC_VER > 1900)) // C++11
+ using propagate_on_container_copy_assignment = std::true_type;
+ using propagate_on_container_move_assignment = std::true_type;
+ using propagate_on_container_swap = std::true_type;
+ using is_always_equal = std::true_type;
+ template <class U, class ...Args> void construct(U* p, Args&& ...args) { ::new(p) U(std::forward<Args>(args)...); }
+ template <class U> void destroy(U* p) mi_attr_noexcept { p->~U(); }
+ #else
+ void construct(pointer p, value_type const& val) { ::new(p) value_type(val); }
+ void destroy(pointer p) { p->~value_type(); }
+ #endif
+
+ size_type max_size() const mi_attr_noexcept { return (PTRDIFF_MAX/sizeof(value_type)); }
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+};
+
+template<class T1,class T2> bool operator==(const mi_stl_allocator<T1>& , const mi_stl_allocator<T2>& ) mi_attr_noexcept { return true; }
+template<class T1,class T2> bool operator!=(const mi_stl_allocator<T1>& , const mi_stl_allocator<T2>& ) mi_attr_noexcept { return false; }
+#endif // __cplusplus
+
+#endif
--- /dev/null
+
+<img align="left" width="100" height="100" src="doc/mimalloc-logo.png"/>
+
+[<img align="right" src="https://dev.azure.com/Daan0324/mimalloc/_apis/build/status/microsoft.mimalloc?branchName=dev"/>](https://dev.azure.com/Daan0324/mimalloc/_build?definitionId=1&_a=summary)
+
+# mimalloc
+
+
+
+mimalloc (pronounced "me-malloc")
+is a general purpose allocator with excellent [performance](#performance) characteristics.
+Initially developed by Daan Leijen for the run-time systems of the
+[Koka](https://github.com/koka-lang/koka) and [Lean](https://github.com/leanprover/lean) languages.
+Latest release:`v1.6.4` (2020-08-06).
+
+It is a drop-in replacement for `malloc` and can be used in other programs
+without code changes, for example, on dynamically linked ELF-based systems (Linux, BSD, etc.) you can use it as:
+```
+> LD_PRELOAD=/usr/bin/libmimalloc.so myprogram
+```
+It also has an easy way to override the allocator in [Windows](#override_on_windows). Notable aspects of the design include:
+
+- __small and consistent__: the library is about 6k LOC using simple and
+ consistent data structures. This makes it very suitable
+ to integrate and adapt in other projects. For runtime systems it
+ provides hooks for a monotonic _heartbeat_ and deferred freeing (for
+ bounded worst-case times with reference counting).
+- __free list sharding__: the big idea: instead of one big free list (per size class) we have
+ many smaller lists per memory "page" which both reduces fragmentation
+ and increases locality --
+ things that are allocated close in time get allocated close in memory.
+ (A memory "page" in _mimalloc_ contains blocks of one size class and is
+ usually 64KiB on a 64-bit system).
+- __eager page reset__: when a "page" becomes empty (with increased chance
+ due to free list sharding) the memory is marked to the OS as unused ("reset" or "purged")
+ reducing (real) memory pressure and fragmentation, especially in long running
+ programs.
+- __secure__: _mimalloc_ can be built in secure mode, adding guard pages,
+ randomized allocation, encrypted free lists, etc. to protect against various
+ heap vulnerabilities. The performance penalty is usually around 10% on average
+ over our benchmarks.
+- __first-class heaps__: efficiently create and use multiple heaps to allocate across different regions.
+ A heap can be destroyed at once instead of deallocating each object separately.
+- __bounded__: it does not suffer from _blowup_ \[1\], has bounded worst-case allocation
+ times (_wcat_), bounded space overhead (~0.2% meta-data, with at most 12.5% waste in allocation sizes),
+ and has no internal points of contention using only atomic operations.
+- __fast__: In our benchmarks (see [below](#performance)),
+ _mimalloc_ outperforms other leading allocators (_jemalloc_, _tcmalloc_, _Hoard_, etc),
+ and usually uses less memory (up to 25% more in the worst case). A nice property
+ is that it does consistently well over a wide range of benchmarks. There is also good huge OS page
+ support for larger server programs.
+
+The [documentation](https://microsoft.github.io/mimalloc) gives a full overview of the API.
+You can read more on the design of _mimalloc_ in the [technical report](https://www.microsoft.com/en-us/research/publication/mimalloc-free-list-sharding-in-action) which also has detailed benchmark results.
+
+Enjoy!
+
+### Releases
+
+* 2020-08-06, `v1.6.4`: stable release 1.6: improved error recovery in low-memory situations,
+ support for IllumOS and Haiku, NUMA support for Vista/XP, improved NUMA detection for AMD Ryzen, ubsan support.
+* 2020-05-05, `v1.6.3`: stable release 1.6: improved behavior in out-of-memory situations, improved malloc zones on macOS,
+ build PIC static libraries by default, add option to abort on out-of-memory, line buffered statistics.
+* 2020-04-20, `v1.6.2`: stable release 1.6: fix compilation on Android, MingW, Raspberry, and Conda,
+ stability fix for Windows 7, fix multiple mimalloc instances in one executable, fix `strnlen` overload,
+ fix aligned debug padding.
+* 2020-02-17, `v1.6.1`: stable release 1.6: minor updates (build with clang-cl, fix alignment issue for small objects).
+* 2020-02-09, `v1.6.0`: stable release 1.6: fixed potential memory leak, improved overriding
+ and thread local support on FreeBSD, NetBSD, DragonFly, and macOSX. New byte-precise
+ heap block overflow detection in debug mode (besides the double-free detection and free-list
+ corruption detection). Add `nodiscard` attribute to most allocation functions.
+ Enable `MIMALLOC_PAGE_RESET` by default. New reclamation strategy for abandoned heap pages
+ for better memory footprint.
+* 2020-02-09, `v1.5.0`: stable release 1.5: improved free performance, small bug fixes.
+* 2020-01-22, `v1.4.0`: stable release 1.4: improved performance for delayed OS page reset,
+more eager concurrent free, addition of STL allocator, fixed potential memory leak.
+* 2020-01-15, `v1.3.0`: stable release 1.3: bug fixes, improved randomness and [stronger
+free list encoding](https://github.com/microsoft/mimalloc/blob/783e3377f79ee82af43a0793910a9f2d01ac7863/include/mimalloc-internal.h#L396) in secure mode.
+* 2019-12-22, `v1.2.2`: stable release 1.2: minor updates.
+* 2019-11-22, `v1.2.0`: stable release 1.2: bug fixes, improved secure mode (free list corruption checks, double free mitigation). Improved dynamic overriding on Windows.
+* 2019-10-07, `v1.1.0`: stable release 1.1.
+* 2019-09-01, `v1.0.8`: pre-release 8: more robust windows dynamic overriding, initial huge page support.
+* 2019-08-10, `v1.0.6`: pre-release 6: various performance improvements.
+
+Special thanks to:
+
+* Jason Gibson (@jasongibson) for exhaustive testing on large workloads and server environments and finding complex bugs in (early versions of) `mimalloc`.
+* Manuel Pöter (@mpoeter) and Sam Gross (@colesbury) for finding an ABA concurrency issue in abandoned segment reclamation.
+
+# Building
+
+## Windows
+
+Open `ide/vs2019/mimalloc.sln` in Visual Studio 2019 and build (or `ide/vs2017/mimalloc.sln`).
+The `mimalloc` project builds a static library (in `out/msvc-x64`), while the
+`mimalloc-override` project builds a DLL for overriding malloc
+in the entire program.
+
+## macOS, Linux, BSD, etc.
+
+We use [`cmake`](https://cmake.org)<sup>1</sup> as the build system:
+
+```
+> mkdir -p out/release
+> cd out/release
+> cmake ../..
+> make
+```
+This builds the library as a shared (dynamic)
+library (`.so` or `.dylib`), a static library (`.a`), and
+as a single object file (`.o`).
+
+`> sudo make install` (install the library and header files in `/usr/local/lib` and `/usr/local/include`)
+
+You can build the debug version which does many internal checks and
+maintains detailed statistics as:
+
+```
+> mkdir -p out/debug
+> cd out/debug
+> cmake -DCMAKE_BUILD_TYPE=Debug ../..
+> make
+```
+This will name the shared library as `libmimalloc-debug.so`.
+
+Finally, you can build a _secure_ version that uses guard pages, encrypted
+free lists, etc., as:
+```
+> mkdir -p out/secure
+> cd out/secure
+> cmake -DMI_SECURE=ON ../..
+> make
+```
+This will name the shared library as `libmimalloc-secure.so`.
+Use `ccmake`<sup>2</sup> instead of `cmake`
+to see and customize all the available build options.
+
+Notes:
+1. Install CMake: `sudo apt-get install cmake`
+2. Install CCMake: `sudo apt-get install cmake-curses-gui`
+
+
+
+# Using the library
+
+The preferred usage is including `<mimalloc.h>`, linking with
+the shared- or static library, and using the `mi_malloc` API exclusively for allocation. For example,
+```
+> gcc -o myprogram -lmimalloc myfile.c
+```
+
+mimalloc uses only safe OS calls (`mmap` and `VirtualAlloc`) and can co-exist
+with other allocators linked to the same program.
+If you use `cmake`, you can simply use:
+```
+find_package(mimalloc 1.4 REQUIRED)
+```
+in your `CMakeLists.txt` to find a locally installed mimalloc. Then use either:
+```
+target_link_libraries(myapp PUBLIC mimalloc)
+```
+to link with the shared (dynamic) library, or:
+```
+target_link_libraries(myapp PUBLIC mimalloc-static)
+```
+to link with the static library. See `test\CMakeLists.txt` for an example.
+
+For best performance in C++ programs, it is also recommended to override the
+global `new` and `delete` operators. For convience, mimalloc provides
+[`mimalloc-new-delete.h`](https://github.com/microsoft/mimalloc/blob/master/include/mimalloc-new-delete.h) which does this for you -- just include it in a single(!) source file in your project.
+In C++, mimalloc also provides the `mi_stl_allocator` struct which implements the `std::allocator`
+interface.
+
+You can pass environment variables to print verbose messages (`MIMALLOC_VERBOSE=1`)
+and statistics (`MIMALLOC_SHOW_STATS=1`) (in the debug version):
+```
+> env MIMALLOC_SHOW_STATS=1 ./cfrac 175451865205073170563711388363
+
+175451865205073170563711388363 = 374456281610909315237213 * 468551
+
+heap stats: peak total freed unit
+normal 2: 16.4 kb 17.5 mb 17.5 mb 16 b ok
+normal 3: 16.3 kb 15.2 mb 15.2 mb 24 b ok
+normal 4: 64 b 4.6 kb 4.6 kb 32 b ok
+normal 5: 80 b 118.4 kb 118.4 kb 40 b ok
+normal 6: 48 b 48 b 48 b 48 b ok
+normal 17: 960 b 960 b 960 b 320 b ok
+
+heap stats: peak total freed unit
+ normal: 33.9 kb 32.8 mb 32.8 mb 1 b ok
+ huge: 0 b 0 b 0 b 1 b ok
+ total: 33.9 kb 32.8 mb 32.8 mb 1 b ok
+malloc requested: 32.8 mb
+
+ committed: 58.2 kb 58.2 kb 58.2 kb 1 b ok
+ reserved: 2.0 mb 2.0 mb 2.0 mb 1 b ok
+ reset: 0 b 0 b 0 b 1 b ok
+ segments: 1 1 1
+-abandoned: 0
+ pages: 6 6 6
+-abandoned: 0
+ mmaps: 3
+ mmap fast: 0
+ mmap slow: 1
+ threads: 0
+ elapsed: 2.022s
+ process: user: 1.781s, system: 0.016s, faults: 756, reclaims: 0, rss: 2.7 mb
+```
+
+The above model of using the `mi_` prefixed API is not always possible
+though in existing programs that already use the standard malloc interface,
+and another option is to override the standard malloc interface
+completely and redirect all calls to the _mimalloc_ library instead .
+
+## Environment Options
+
+You can set further options either programmatically (using [`mi_option_set`](https://microsoft.github.io/mimalloc/group__options.html)),
+or via environment variables.
+
+- `MIMALLOC_SHOW_STATS=1`: show statistics when the program terminates.
+- `MIMALLOC_VERBOSE=1`: show verbose messages.
+- `MIMALLOC_SHOW_ERRORS=1`: show error and warning messages.
+- `MIMALLOC_PAGE_RESET=0`: by default, mimalloc will reset (or purge) OS pages that are not in use, to signal to the OS
+ that the underlying physical memory can be reused. This can reduce memory fragmentation in long running (server)
+ programs. By setting it to `0` this will no longer be done which can improve performance for batch-like programs.
+ As an alternative, the `MIMALLOC_RESET_DELAY=`<msecs> can be set higher (100ms by default) to make the page
+ reset occur less frequently instead of turning it off completely.
+- `MIMALLOC_USE_NUMA_NODES=N`: pretend there are at most `N` NUMA nodes. If not set, the actual NUMA nodes are detected
+ at runtime. Setting `N` to 1 may avoid problems in some virtual environments. Also, setting it to a lower number than
+ the actual NUMA nodes is fine and will only cause threads to potentially allocate more memory across actual NUMA
+ nodes (but this can happen in any case as NUMA local allocation is always a best effort but not guaranteed).
+- `MIMALLOC_LARGE_OS_PAGES=1`: use large OS pages (2MiB) when available; for some workloads this can significantly
+ improve performance. Use `MIMALLOC_VERBOSE` to check if the large OS pages are enabled -- usually one needs
+ to explicitly allow large OS pages (as on [Windows][windows-huge] and [Linux][linux-huge]). However, sometimes
+ the OS is very slow to reserve contiguous physical memory for large OS pages so use with care on systems that
+ can have fragmented memory (for that reason, we generally recommend to use `MIMALLOC_RESERVE_HUGE_OS_PAGES` instead whenever possible).
+ <!--
+ - `MIMALLOC_EAGER_REGION_COMMIT=1`: on Windows, commit large (256MiB) regions eagerly. On Windows, these regions
+ show in the working set even though usually just a small part is committed to physical memory. This is why it
+ turned off by default on Windows as it looks not good in the task manager. However, turning it on has no
+ real drawbacks and may improve performance by a little.
+ -->
+- `MIMALLOC_RESERVE_HUGE_OS_PAGES=N`: where N is the number of 1GiB _huge_ OS pages. This reserves the huge pages at
+ startup and sometimes this can give a large (latency) performance improvement on big workloads.
+ Usually it is better to not use
+ `MIMALLOC_LARGE_OS_PAGES` in combination with this setting. Just like large OS pages, use with care as reserving
+ contiguous physical memory can take a long time when memory is fragmented (but reserving the huge pages is done at
+ startup only once).
+ Note that we usually need to explicitly enable huge OS pages (as on [Windows][windows-huge] and [Linux][linux-huge])).
+ With huge OS pages, it may be beneficial to set the setting
+ `MIMALLOC_EAGER_COMMIT_DELAY=N` (`N` is 1 by default) to delay the initial `N` segments (of 4MiB)
+ of a thread to not allocate in the huge OS pages; this prevents threads that are short lived
+ and allocate just a little to take up space in the huge OS page area (which cannot be reset).
+
+Use caution when using `fork` in combination with either large or huge OS pages: on a fork, the OS uses copy-on-write
+for all pages in the original process including the huge OS pages. When any memory is now written in that area, the
+OS will copy the entire 1GiB huge page (or 2MiB large page) which can cause the memory usage to grow in big increments.
+
+[linux-huge]: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/tuning_and_optimizing_red_hat_enterprise_linux_for_oracle_9i_and_10g_databases/sect-oracle_9i_and_10g_tuning_guide-large_memory_optimization_big_pages_and_huge_pages-configuring_huge_pages_in_red_hat_enterprise_linux_4_or_5
+[windows-huge]: https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows?view=sql-server-2017
+
+## Secure Mode
+
+_mimalloc_ can be build in secure mode by using the `-DMI_SECURE=ON` flags in `cmake`. This build enables various mitigations
+to make mimalloc more robust against exploits. In particular:
+
+- All internal mimalloc pages are surrounded by guard pages and the heap metadata is behind a guard page as well (so a buffer overflow
+ exploit cannot reach into the metadata),
+- All free list pointers are
+ [encoded](https://github.com/microsoft/mimalloc/blob/783e3377f79ee82af43a0793910a9f2d01ac7863/include/mimalloc-internal.h#L396)
+ with per-page keys which is used both to prevent overwrites with a known pointer, as well as to detect heap corruption,
+- Double free's are detected (and ignored),
+- The free lists are initialized in a random order and allocation randomly chooses between extension and reuse within a page to
+ mitigate against attacks that rely on a predicable allocation order. Similarly, the larger heap blocks allocated by mimalloc
+ from the OS are also address randomized.
+
+As always, evaluate with care as part of an overall security strategy as all of the above are mitigations but not guarantees.
+
+## Debug Mode
+
+When _mimalloc_ is built using debug mode, various checks are done at runtime to catch development errors.
+
+- Statistics are maintained in detail for each object size. They can be shown using `MIMALLOC_SHOW_STATS=1` at runtime.
+- All objects have padding at the end to detect (byte precise) heap block overflows.
+- Double free's, and freeing invalid heap pointers are detected.
+- Corrupted free-lists and some forms of use-after-free are detected.
+
+
+# Overriding Malloc
+
+Overriding the standard `malloc` can be done either _dynamically_ or _statically_.
+
+## Dynamic override
+
+This is the recommended way to override the standard malloc interface.
+
+### Override on Linux, BSD
+
+On these ELF-based systems we preload the mimalloc shared
+library so all calls to the standard `malloc` interface are
+resolved to the _mimalloc_ library.
+```
+> env LD_PRELOAD=/usr/lib/libmimalloc.so myprogram
+```
+
+You can set extra environment variables to check that mimalloc is running,
+like:
+```
+> env MIMALLOC_VERBOSE=1 LD_PRELOAD=/usr/lib/libmimalloc.so myprogram
+```
+or run with the debug version to get detailed statistics:
+```
+> env MIMALLOC_SHOW_STATS=1 LD_PRELOAD=/usr/lib/libmimalloc-debug.so myprogram
+```
+
+### Override on MacOS
+
+On macOS we can also preload the mimalloc shared
+library so all calls to the standard `malloc` interface are
+resolved to the _mimalloc_ library.
+```
+> env DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=/usr/lib/libmimalloc.dylib myprogram
+```
+
+Note that certain security restrictions may apply when doing this from
+the [shell](https://stackoverflow.com/questions/43941322/dyld-insert-libraries-ignored-when-calling-application-through-bash).
+
+(Note: macOS support for dynamic overriding is recent, please report any issues.)
+
+### Override on Windows
+
+<span id="override_on_windows">Overriding on Windows</span> is robust and has the
+particular advantage to be able to redirect all malloc/free calls that go through
+the (dynamic) C runtime allocator, including those from other DLL's or libraries.
+
+The overriding on Windows requires that you link your program explicitly with
+the mimalloc DLL and use the C-runtime library as a DLL (using the `/MD` or `/MDd` switch).
+Also, the `mimalloc-redirect.dll` (or `mimalloc-redirect32.dll`) must be available
+in the same folder as the main `mimalloc-override.dll` at runtime (as it is a dependency).
+The redirection DLL ensures that all calls to the C runtime malloc API get redirected to
+mimalloc (in `mimalloc-override.dll`).
+
+To ensure the mimalloc DLL is loaded at run-time it is easiest to insert some
+call to the mimalloc API in the `main` function, like `mi_version()`
+(or use the `/INCLUDE:mi_version` switch on the linker). See the `mimalloc-override-test` project
+for an example on how to use this. For best performance on Windows with C++, it
+is also recommended to also override the `new`/`delete` operations (by including
+[`mimalloc-new-delete.h`](https://github.com/microsoft/mimalloc/blob/master/include/mimalloc-new-delete.h) a single(!) source file in your project).
+
+The environment variable `MIMALLOC_DISABLE_REDIRECT=1` can be used to disable dynamic
+overriding at run-time. Use `MIMALLOC_VERBOSE=1` to check if mimalloc was successfully redirected.
+
+(Note: in principle, it is possible to even patch existing executables without any recompilation
+if they are linked with the dynamic C runtime (`ucrtbase.dll`) -- just put the `mimalloc-override.dll`
+into the import table (and put `mimalloc-redirect.dll` in the same folder)
+Such patching can be done for example with [CFF Explorer](https://ntcore.com/?page_id=388)).
+
+
+## Static override
+
+On Unix-like systems, you can also statically link with _mimalloc_ to override the standard
+malloc interface. The recommended way is to link the final program with the
+_mimalloc_ single object file (`mimalloc-override.o`). We use
+an object file instead of a library file as linkers give preference to
+that over archives to resolve symbols. To ensure that the standard
+malloc interface resolves to the _mimalloc_ library, link it as the first
+object file. For example:
+```
+> gcc -o myprogram mimalloc-override.o myfile1.c ...
+```
+
+Another way to override statically that works on all platforms, is to
+link statically to mimalloc (as shown in the introduction) and include a
+header file in each source file that re-defines `malloc` etc. to `mi_malloc`.
+This is provided by [`mimalloc-override.h`](https://github.com/microsoft/mimalloc/blob/master/include/mimalloc-override.h). This only works reliably though if all sources are
+under your control or otherwise mixing of pointers from different heaps may occur!
+
+
+# Performance
+
+Last update: 2020-01-20
+
+We tested _mimalloc_ against many other top allocators over a wide
+range of benchmarks, ranging from various real world programs to
+synthetic benchmarks that see how the allocator behaves under more
+extreme circumstances. In our benchmark suite, _mimalloc_ outperforms other leading
+allocators (_jemalloc_, _tcmalloc_, _Hoard_, etc), and has a similar memory footprint. A nice property is that it
+does consistently well over the wide range of benchmarks.
+
+General memory allocators are interesting as there exists no algorithm that is
+optimal -- for a given allocator one can usually construct a workload
+where it does not do so well. The goal is thus to find an allocation
+strategy that performs well over a wide range of benchmarks without
+suffering from (too much) underperformance in less common situations.
+
+As always, interpret these results with care since some benchmarks test synthetic
+or uncommon situations that may never apply to your workloads. For example, most
+allocators do not do well on `xmalloc-testN` but that includes the best
+industrial allocators like _jemalloc_ and _tcmalloc_ that are used in some of
+the world's largest systems (like Chrome or FreeBSD).
+
+We show here only an overview -- for
+more specific details and further benchmarks we refer to the
+[technical report](https://www.microsoft.com/en-us/research/publication/mimalloc-free-list-sharding-in-action).
+The benchmark suite is automated and available separately
+as [mimalloc-bench](https://github.com/daanx/mimalloc-bench).
+
+
+## Benchmark Results on 36-core Intel
+
+Testing on a big Amazon EC2 compute instance
+([c5.18xlarge](https://aws.amazon.com/ec2/instance-types/#Compute_Optimized))
+consisting of a 72 processor Intel Xeon at 3GHz
+with 144GiB ECC memory, running Ubuntu 18.04.1 with glibc 2.27 and GCC 7.4.0.
+The measured allocators are _mimalloc_ (xmi, tag:v1.4.0, page reset enabled)
+and its secure build as _smi_,
+Google's [_tcmalloc_](https://github.com/gperftools/gperftools) (tc, tag:gperftools-2.7) used in Chrome,
+Facebook's [_jemalloc_](https://github.com/jemalloc/jemalloc) (je, tag:5.2.1) by Jason Evans used in Firefox and FreeBSD,
+the Intel thread building blocks [allocator](https://github.com/intel/tbb) (tbb, tag:2020),
+[rpmalloc](https://github.com/mjansson/rpmalloc) (rp,tag:1.4.0) by Mattias Jansson,
+the original scalable [_Hoard_](https://github.com/emeryberger/Hoard) (tag:3.13) allocator by Emery Berger \[1],
+the memory compacting [_Mesh_](https://github.com/plasma-umass/Mesh) (git:51222e7) allocator by
+Bobby Powers _et al_ \[8],
+and finally the default system allocator (glibc, 2.27) (based on _PtMalloc2_).
+
+<img width="90%" src="doc/bench-c5-18xlarge-2020-01-20-a.svg"/>
+<img width="90%" src="doc/bench-c5-18xlarge-2020-01-20-b.svg"/>
+
+Any benchmarks ending in `N` run on all processors in parallel.
+Results are averaged over 10 runs and reported relative
+to mimalloc (where 1.2 means it took 1.2× longer to run).
+The legend also contains the _overall relative score_ between the
+allocators where 100 points is the maximum if an allocator is fastest on
+all benchmarks.
+
+The single threaded _cfrac_ benchmark by Dave Barrett is an implementation of
+continued fraction factorization which uses many small short-lived allocations.
+All allocators do well on such common usage, where _mimalloc_ is just a tad
+faster than _tcmalloc_ and
+_jemalloc_.
+
+The _leanN_ program is interesting as a large realistic and
+concurrent workload of the [Lean](https://github.com/leanprover/lean)
+theorem prover compiling its own standard library, and there is a 7%
+speedup over _tcmalloc_. This is
+quite significant: if Lean spends 20% of its time in the
+allocator that means that _mimalloc_ is 1.3× faster than _tcmalloc_
+here. (This is surprising as that is not measured in a pure
+allocation benchmark like _alloc-test_. We conjecture that we see this
+outsized improvement here because _mimalloc_ has better locality in
+the allocation which improves performance for the *other* computations
+in a program as well).
+
+The single threaded _redis_ benchmark again show that most allocators do well on such workloads where _tcmalloc_
+did best this time.
+
+The _larsonN_ server benchmark by Larson and Krishnan \[2] allocates and frees between threads. They observed this
+behavior (which they call _bleeding_) in actual server applications, and the benchmark simulates this.
+Here, _mimalloc_ is quite a bit faster than _tcmalloc_ and _jemalloc_ probably due to the object migration between different threads.
+
+The _mstressN_ workload performs many allocations and re-allocations,
+and migrates objects between threads (as in _larsonN_). However, it also
+creates and destroys the _N_ worker threads a few times keeping some objects
+alive beyond the life time of the allocating thread. We observed this
+behavior in many larger server applications.
+
+The [_rptestN_](https://github.com/mjansson/rpmalloc-benchmark) benchmark
+by Mattias Jansson is a allocator test originally designed
+for _rpmalloc_, and tries to simulate realistic allocation patterns over
+multiple threads. Here the differences between allocators become more apparent.
+
+The second benchmark set tests specific aspects of the allocators and
+shows even more extreme differences between them.
+
+The _alloc-test_, by
+[OLogN Technologies AG](http://ithare.com/testing-memory-allocators-ptmalloc2-tcmalloc-hoard-jemalloc-while-trying-to-simulate-real-world-loads/), is a very allocation intensive benchmark doing millions of
+allocations in various size classes. The test is scaled such that when an
+allocator performs almost identically on _alloc-test1_ as _alloc-testN_ it
+means that it scales linearly. Here, _tcmalloc_, and
+_Hoard_ seem to scale less well and do more than 10% worse on the multi-core version. Even the best industrial
+allocators (_tcmalloc_, _jemalloc_, and _tbb_) are more than 10% slower as _mimalloc_ here.
+
+The _sh6bench_ and _sh8bench_ benchmarks are
+developed by [MicroQuill](http://www.microquill.com/) as part of SmartHeap.
+In _sh6bench_ _mimalloc_ does much
+better than the others (more than 1.5× faster than _jemalloc_).
+We cannot explain this well but believe it is
+caused in part by the "reverse" free-ing pattern in _sh6bench_.
+The _sh8bench_ is a variation with object migration
+between threads; whereas _tcmalloc_ did well on _sh6bench_, the addition of object migration causes it to be 10× slower than before.
+
+The _xmalloc-testN_ benchmark by Lever and Boreham \[5] and Christian Eder, simulates an asymmetric workload where
+some threads only allocate, and others only free -- they observed this pattern in
+larger server applications. Here we see that
+the _mimalloc_ technique of having non-contended sharded thread free
+lists pays off as it outperforms others by a very large margin. Only _rpmalloc_ and _tbb_ also scale well on this benchmark.
+
+The _cache-scratch_ benchmark by Emery Berger \[1], and introduced with
+the Hoard allocator to test for _passive-false_ sharing of cache lines.
+With a single thread they all
+perform the same, but when running with multiple threads the potential allocator
+induced false sharing of the cache lines can cause large run-time differences.
+Crundal \[6] describes in detail why the false cache line sharing occurs in the _tcmalloc_ design, and also discusses how this
+can be avoided with some small implementation changes.
+Only the _tbb_, _rpmalloc_ and _mesh_ allocators also avoid the
+cache line sharing completely, while _Hoard_ and _glibc_ seem to mitigate
+the effects. Kukanov and Voss \[7] describe in detail
+how the design of _tbb_ avoids the false cache line sharing.
+
+## On 24-core AMD Epyc
+
+For completeness, here are the results on a
+[r5a.12xlarge](https://aws.amazon.com/ec2/instance-types/#Memory_Optimized) instance
+having a 48 processor AMD Epyc 7000 at 2.5GHz with 384GiB of memory.
+The results are similar to the Intel results but it is interesting to
+see the differences in the _larsonN_, _mstressN_, and _xmalloc-testN_ benchmarks.
+
+<img width="90%" src="doc/bench-r5a-12xlarge-2020-01-16-a.svg"/>
+<img width="90%" src="doc/bench-r5a-12xlarge-2020-01-16-b.svg"/>
+
+
+## Peak Working Set
+
+The following figure shows the peak working set (rss) of the allocators
+on the benchmarks (on the c5.18xlarge instance).
+
+<img width="90%" src="doc/bench-c5-18xlarge-2020-01-20-rss-a.svg"/>
+<img width="90%" src="doc/bench-c5-18xlarge-2020-01-20-rss-b.svg"/>
+
+Note that the _xmalloc-testN_ memory usage should be disregarded as it
+allocates more the faster the program runs. Similarly, memory usage of
+_mstressN_, _rptestN_ and _sh8bench_ can vary depending on scheduling and
+speed. Nevertheless, even though _mimalloc_ is fast on these benchmarks we
+believe the memory usage is too high and hope to improve.
+
+
+# References
+
+- \[1] Emery D. Berger, Kathryn S. McKinley, Robert D. Blumofe, and Paul R. Wilson.
+ _Hoard: A Scalable Memory Allocator for Multithreaded Applications_
+ the Ninth International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS-IX). Cambridge, MA, November 2000.
+ [pdf](http://www.cs.utexas.edu/users/mckinley/papers/asplos-2000.pdf)
+
+- \[2] P. Larson and M. Krishnan. _Memory allocation for long-running server applications_.
+ In ISMM, Vancouver, B.C., Canada, 1998. [pdf](http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.45.1947&rep=rep1&type=pdf)
+
+- \[3] D. Grunwald, B. Zorn, and R. Henderson.
+ _Improving the cache locality of memory allocation_. In R. Cartwright, editor,
+ Proceedings of the Conference on Programming Language Design and Implementation, pages 177–186, New York, NY, USA, June 1993. [pdf](http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.43.6621&rep=rep1&type=pdf)
+
+- \[4] J. Barnes and P. Hut. _A hierarchical O(n*log(n)) force-calculation algorithm_. Nature, 324:446-449, 1986.
+
+- \[5] C. Lever, and D. Boreham. _Malloc() Performance in a Multithreaded Linux Environment._
+ In USENIX Annual Technical Conference, Freenix Session. San Diego, CA. Jun. 2000.
+ Available at <https://github.com/kuszmaul/SuperMalloc/tree/master/tests>
+
+- \[6] Timothy Crundal. _Reducing Active-False Sharing in TCMalloc_. 2016. CS16S1 project at the Australian National University. [pdf](http://courses.cecs.anu.edu.au/courses/CSPROJECTS/16S1/Reports/Timothy_Crundal_Report.pdf)
+
+- \[7] Alexey Kukanov, and Michael J Voss.
+ _The Foundations for Scalable Multi-Core Software in Intel Threading Building Blocks._
+ Intel Technology Journal 11 (4). 2007
+
+- \[8] Bobby Powers, David Tench, Emery D. Berger, and Andrew McGregor.
+ _Mesh: Compacting Memory Management for C/C++_
+ In Proceedings of the 40th ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI'19), June 2019, pages 333-–346.
+
+<!--
+- \[9] Paul Liétar, Theodore Butler, Sylvan Clebsch, Sophia Drossopoulou, Juliana Franco, Matthew J Parkinson,
+ Alex Shamis, Christoph M Wintersteiger, and David Chisnall.
+ _Snmalloc: A Message Passing Allocator._
+ In Proceedings of the 2019 ACM SIGPLAN International Symposium on Memory Management, 122–135. ACM. 2019.
+-->
+
+
+# Contributing
+
+This project welcomes contributions and suggestions. Most contributions require you to agree to a
+Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
+the rights to use your contribution. For details, visit https://cla.microsoft.com.
+
+When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
+a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
+provided by the bot. You will only need to do this once across all repos using our CLA.
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+
+#include <string.h> // memset, memcpy
+
+// ------------------------------------------------------
+// Aligned Allocation
+// ------------------------------------------------------
+
+static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero) mi_attr_noexcept {
+ // note: we don't require `size > offset`, we just guarantee that
+ // the address at offset is aligned regardless of the allocated size.
+ mi_assert(alignment > 0);
+ if (mi_unlikely(size > PTRDIFF_MAX)) return NULL; // we don't allocate more than PTRDIFF_MAX (see <https://sourceware.org/ml/libc-announce/2019/msg00001.html>)
+ if (mi_unlikely(alignment==0 || !_mi_is_power_of_two(alignment))) return NULL; // require power-of-two (see <https://en.cppreference.com/w/c/memory/aligned_alloc>)
+ const uintptr_t align_mask = alignment-1; // for any x, `(x & align_mask) == (x % alignment)`
+
+ // try if there is a small block available with just the right alignment
+ const size_t padsize = size + MI_PADDING_SIZE;
+ if (mi_likely(padsize <= MI_SMALL_SIZE_MAX)) {
+ mi_page_t* page = _mi_heap_get_free_small_page(heap,padsize);
+ const bool is_aligned = (((uintptr_t)page->free+offset) & align_mask)==0;
+ if (mi_likely(page->free != NULL && is_aligned))
+ {
+ #if MI_STAT>1
+ mi_heap_stat_increase( heap, malloc, size);
+ #endif
+ void* p = _mi_page_malloc(heap,page,padsize); // TODO: inline _mi_page_malloc
+ mi_assert_internal(p != NULL);
+ mi_assert_internal(((uintptr_t)p + offset) % alignment == 0);
+ if (zero) _mi_block_zero_init(page,p,size);
+ return p;
+ }
+ }
+
+ // use regular allocation if it is guaranteed to fit the alignment constraints
+ if (offset==0 && alignment<=padsize && padsize<=MI_MEDIUM_OBJ_SIZE_MAX && (padsize&align_mask)==0) {
+ void* p = _mi_heap_malloc_zero(heap, size, zero);
+ mi_assert_internal(p == NULL || ((uintptr_t)p % alignment) == 0);
+ return p;
+ }
+
+ // otherwise over-allocate
+ void* p = _mi_heap_malloc_zero(heap, size + alignment - 1, zero);
+ if (p == NULL) return NULL;
+
+ // .. and align within the allocation
+ uintptr_t adjust = alignment - (((uintptr_t)p + offset) & align_mask);
+ mi_assert_internal(adjust <= alignment);
+ void* aligned_p = (adjust == alignment ? p : (void*)((uintptr_t)p + adjust));
+ if (aligned_p != p) mi_page_set_has_aligned(_mi_ptr_page(p), true);
+ mi_assert_internal(((uintptr_t)aligned_p + offset) % alignment == 0);
+ mi_assert_internal( p == _mi_page_ptr_unalign(_mi_ptr_segment(aligned_p),_mi_ptr_page(aligned_p),aligned_p) );
+ return aligned_p;
+}
+
+
+mi_decl_restrict void* mi_heap_malloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_malloc_zero_aligned_at(heap, size, alignment, offset, false);
+}
+
+mi_decl_restrict void* mi_heap_malloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept {
+ return mi_heap_malloc_aligned_at(heap, size, alignment, 0);
+}
+
+mi_decl_restrict void* mi_heap_zalloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_malloc_zero_aligned_at(heap, size, alignment, offset, true);
+}
+
+mi_decl_restrict void* mi_heap_zalloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept {
+ return mi_heap_zalloc_aligned_at(heap, size, alignment, 0);
+}
+
+mi_decl_restrict void* mi_heap_calloc_aligned_at(mi_heap_t* heap, size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(count, size, &total)) return NULL;
+ return mi_heap_zalloc_aligned_at(heap, total, alignment, offset);
+}
+
+mi_decl_restrict void* mi_heap_calloc_aligned(mi_heap_t* heap, size_t count, size_t size, size_t alignment) mi_attr_noexcept {
+ return mi_heap_calloc_aligned_at(heap,count,size,alignment,0);
+}
+
+mi_decl_restrict void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_malloc_aligned_at(mi_get_default_heap(), size, alignment, offset);
+}
+
+mi_decl_restrict void* mi_malloc_aligned(size_t size, size_t alignment) mi_attr_noexcept {
+ return mi_heap_malloc_aligned(mi_get_default_heap(), size, alignment);
+}
+
+mi_decl_restrict void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_zalloc_aligned_at(mi_get_default_heap(), size, alignment, offset);
+}
+
+mi_decl_restrict void* mi_zalloc_aligned(size_t size, size_t alignment) mi_attr_noexcept {
+ return mi_heap_zalloc_aligned(mi_get_default_heap(), size, alignment);
+}
+
+mi_decl_restrict void* mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_calloc_aligned_at(mi_get_default_heap(), count, size, alignment, offset);
+}
+
+mi_decl_restrict void* mi_calloc_aligned(size_t count, size_t size, size_t alignment) mi_attr_noexcept {
+ return mi_heap_calloc_aligned(mi_get_default_heap(), count, size, alignment);
+}
+
+
+static void* mi_heap_realloc_zero_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset, bool zero) mi_attr_noexcept {
+ mi_assert(alignment > 0);
+ if (alignment <= sizeof(uintptr_t)) return _mi_heap_realloc_zero(heap,p,newsize,zero);
+ if (p == NULL) return mi_heap_malloc_zero_aligned_at(heap,newsize,alignment,offset,zero);
+ size_t size = mi_usable_size(p);
+ if (newsize <= size && newsize >= (size - (size / 2))
+ && (((uintptr_t)p + offset) % alignment) == 0) {
+ return p; // reallocation still fits, is aligned and not more than 50% waste
+ }
+ else {
+ void* newp = mi_heap_malloc_aligned_at(heap,newsize,alignment,offset);
+ if (newp != NULL) {
+ if (zero && newsize > size) {
+ const mi_page_t* page = _mi_ptr_page(newp);
+ if (page->is_zero) {
+ // already zero initialized
+ mi_assert_expensive(mi_mem_is_zero(newp,newsize));
+ }
+ else {
+ // also set last word in the previous allocation to zero to ensure any padding is zero-initialized
+ size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0);
+ memset((uint8_t*)newp + start, 0, newsize - start);
+ }
+ }
+ memcpy(newp, p, (newsize > size ? size : newsize));
+ mi_free(p); // only free if successful
+ }
+ return newp;
+ }
+}
+
+static void* mi_heap_realloc_zero_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, bool zero) mi_attr_noexcept {
+ mi_assert(alignment > 0);
+ if (alignment <= sizeof(uintptr_t)) return _mi_heap_realloc_zero(heap,p,newsize,zero);
+ size_t offset = ((uintptr_t)p % alignment); // use offset of previous allocation (p can be NULL)
+ return mi_heap_realloc_zero_aligned_at(heap,p,newsize,alignment,offset,zero);
+}
+
+void* mi_heap_realloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_realloc_zero_aligned_at(heap,p,newsize,alignment,offset,false);
+}
+
+void* mi_heap_realloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept {
+ return mi_heap_realloc_zero_aligned(heap,p,newsize,alignment,false);
+}
+
+void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_realloc_zero_aligned_at(heap, p, newsize, alignment, offset, true);
+}
+
+void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept {
+ return mi_heap_realloc_zero_aligned(heap, p, newsize, alignment, true);
+}
+
+void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(newcount, size, &total)) return NULL;
+ return mi_heap_rezalloc_aligned_at(heap, p, total, alignment, offset);
+}
+
+void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(newcount, size, &total)) return NULL;
+ return mi_heap_rezalloc_aligned(heap, p, total, alignment);
+}
+
+void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_realloc_aligned_at(mi_get_default_heap(), p, newsize, alignment, offset);
+}
+
+void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept {
+ return mi_heap_realloc_aligned(mi_get_default_heap(), p, newsize, alignment);
+}
+
+void* mi_rezalloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_rezalloc_aligned_at(mi_get_default_heap(), p, newsize, alignment, offset);
+}
+
+void* mi_rezalloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept {
+ return mi_heap_rezalloc_aligned(mi_get_default_heap(), p, newsize, alignment);
+}
+
+void* mi_recalloc_aligned_at(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_recalloc_aligned_at(mi_get_default_heap(), p, newcount, size, alignment, offset);
+}
+
+void* mi_recalloc_aligned(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept {
+ return mi_heap_recalloc_aligned(mi_get_default_heap(), p, newcount, size, alignment);
+}
+
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+
+#if defined(MI_MALLOC_OVERRIDE)
+
+#if !defined(__APPLE__)
+#error "this file should only be included on macOS"
+#endif
+
+/* ------------------------------------------------------
+ Override system malloc on macOS
+ This is done through the malloc zone interface.
+ It seems we also need to interpose (see `alloc-override.c`)
+ or otherwise we get zone errors as there are usually
+ already allocations done by the time we take over the
+ zone. Unfortunately, that means we need to replace
+ the `free` with a checked free (`cfree`) impacting
+ performance.
+------------------------------------------------------ */
+
+#include <AvailabilityMacros.h>
+#include <malloc/malloc.h>
+#include <string.h> // memset
+
+#if defined(MAC_OS_X_VERSION_10_6) && \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+// only available from OSX 10.6
+extern malloc_zone_t* malloc_default_purgeable_zone(void) __attribute__((weak_import));
+#endif
+
+
+/* ------------------------------------------------------
+ malloc zone members
+------------------------------------------------------ */
+
+static size_t zone_size(malloc_zone_t* zone, const void* p) {
+ UNUSED(zone);
+ if (!mi_is_in_heap_region(p))
+ return 0; // not our pointer, bail out
+
+ return mi_usable_size(p);
+}
+
+static void* zone_malloc(malloc_zone_t* zone, size_t size) {
+ UNUSED(zone);
+ return mi_malloc(size);
+}
+
+static void* zone_calloc(malloc_zone_t* zone, size_t count, size_t size) {
+ UNUSED(zone);
+ return mi_calloc(count, size);
+}
+
+static void* zone_valloc(malloc_zone_t* zone, size_t size) {
+ UNUSED(zone);
+ return mi_malloc_aligned(size, _mi_os_page_size());
+}
+
+static void zone_free(malloc_zone_t* zone, void* p) {
+ UNUSED(zone);
+ return mi_free(p);
+}
+
+static void* zone_realloc(malloc_zone_t* zone, void* p, size_t newsize) {
+ UNUSED(zone);
+ return mi_realloc(p, newsize);
+}
+
+static void* zone_memalign(malloc_zone_t* zone, size_t alignment, size_t size) {
+ UNUSED(zone);
+ return mi_malloc_aligned(size,alignment);
+}
+
+static void zone_destroy(malloc_zone_t* zone) {
+ UNUSED(zone);
+ // todo: ignore for now?
+}
+
+static unsigned zone_batch_malloc(malloc_zone_t* zone, size_t size, void** ps, unsigned count) {
+ size_t i;
+ for (i = 0; i < count; i++) {
+ ps[i] = zone_malloc(zone, size);
+ if (ps[i] == NULL) break;
+ }
+ return i;
+}
+
+static void zone_batch_free(malloc_zone_t* zone, void** ps, unsigned count) {
+ for(size_t i = 0; i < count; i++) {
+ zone_free(zone, ps[i]);
+ ps[i] = NULL;
+ }
+}
+
+static size_t zone_pressure_relief(malloc_zone_t* zone, size_t size) {
+ UNUSED(zone); UNUSED(size);
+ mi_collect(false);
+ return 0;
+}
+
+static void zone_free_definite_size(malloc_zone_t* zone, void* p, size_t size) {
+ UNUSED(size);
+ zone_free(zone,p);
+}
+
+
+/* ------------------------------------------------------
+ Introspection members
+------------------------------------------------------ */
+
+static kern_return_t intro_enumerator(task_t task, void* p,
+ unsigned type_mask, vm_address_t zone_address,
+ memory_reader_t reader,
+ vm_range_recorder_t recorder)
+{
+ // todo: enumerate all memory
+ UNUSED(task); UNUSED(p); UNUSED(type_mask); UNUSED(zone_address);
+ UNUSED(reader); UNUSED(recorder);
+ return KERN_SUCCESS;
+}
+
+static size_t intro_good_size(malloc_zone_t* zone, size_t size) {
+ UNUSED(zone);
+ return mi_good_size(size);
+}
+
+static boolean_t intro_check(malloc_zone_t* zone) {
+ UNUSED(zone);
+ return true;
+}
+
+static void intro_print(malloc_zone_t* zone, boolean_t verbose) {
+ UNUSED(zone); UNUSED(verbose);
+ mi_stats_print(NULL);
+}
+
+static void intro_log(malloc_zone_t* zone, void* p) {
+ UNUSED(zone); UNUSED(p);
+ // todo?
+}
+
+static void intro_force_lock(malloc_zone_t* zone) {
+ UNUSED(zone);
+ // todo?
+}
+
+static void intro_force_unlock(malloc_zone_t* zone) {
+ UNUSED(zone);
+ // todo?
+}
+
+static void intro_statistics(malloc_zone_t* zone, malloc_statistics_t* stats) {
+ UNUSED(zone);
+ // todo...
+ stats->blocks_in_use = 0;
+ stats->size_in_use = 0;
+ stats->max_size_in_use = 0;
+ stats->size_allocated = 0;
+}
+
+static boolean_t intro_zone_locked(malloc_zone_t* zone) {
+ UNUSED(zone);
+ return false;
+}
+
+
+/* ------------------------------------------------------
+ At process start, override the default allocator
+------------------------------------------------------ */
+
+static malloc_zone_t* mi_get_default_zone()
+{
+ // The first returned zone is the real default
+ malloc_zone_t** zones = NULL;
+ unsigned count = 0;
+ kern_return_t ret = malloc_get_all_zones(0, NULL, (vm_address_t**)&zones, &count);
+ if (ret == KERN_SUCCESS && count > 0) {
+ return zones[0];
+ }
+ else {
+ // fallback
+ return malloc_default_zone();
+ }
+}
+
+static void __attribute__((constructor)) _mi_macos_override_malloc()
+{
+ static malloc_introspection_t intro;
+ memset(&intro, 0, sizeof(intro));
+
+ intro.enumerator = &intro_enumerator;
+ intro.good_size = &intro_good_size;
+ intro.check = &intro_check;
+ intro.print = &intro_print;
+ intro.log = &intro_log;
+ intro.force_lock = &intro_force_lock;
+ intro.force_unlock = &intro_force_unlock;
+
+ static malloc_zone_t zone;
+ memset(&zone, 0, sizeof(zone));
+
+ zone.version = 4;
+ zone.zone_name = "mimalloc";
+ zone.size = &zone_size;
+ zone.introspect = &intro;
+ zone.malloc = &zone_malloc;
+ zone.calloc = &zone_calloc;
+ zone.valloc = &zone_valloc;
+ zone.free = &zone_free;
+ zone.realloc = &zone_realloc;
+ zone.destroy = &zone_destroy;
+ zone.batch_malloc = &zone_batch_malloc;
+ zone.batch_free = &zone_batch_free;
+
+ malloc_zone_t* purgeable_zone = NULL;
+
+#if defined(MAC_OS_X_VERSION_10_6) && \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+ // switch to version 9 on OSX 10.6 to support memalign.
+ zone.version = 9;
+ zone.memalign = &zone_memalign;
+ zone.free_definite_size = &zone_free_definite_size;
+ zone.pressure_relief = &zone_pressure_relief;
+ intro.zone_locked = &intro_zone_locked;
+ intro.statistics = &intro_statistics;
+
+ // force the purgeable zone to exist to avoid strange bugs
+ if (malloc_default_purgeable_zone) {
+ purgeable_zone = malloc_default_purgeable_zone();
+ }
+#endif
+
+ // Register our zone
+ malloc_zone_register(&zone);
+
+ // Unregister the default zone, this makes our zone the new default
+ // as that was the last registered.
+ malloc_zone_t *default_zone = mi_get_default_zone();
+ malloc_zone_unregister(default_zone);
+
+ // Reregister the default zone so free and realloc in that zone keep working.
+ malloc_zone_register(default_zone);
+
+ // Unregister, and re-register the purgeable_zone to avoid bugs if it occurs
+ // earlier than the default zone.
+ if (purgeable_zone != NULL) {
+ malloc_zone_unregister(purgeable_zone);
+ malloc_zone_register(purgeable_zone);
+ }
+
+}
+
+#endif // MI_MALLOC_OVERRIDE
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+#if !defined(MI_IN_ALLOC_C)
+#error "this file should be included from 'alloc.c' (so aliases can work)"
+#endif
+
+#if defined(MI_MALLOC_OVERRIDE) && defined(_WIN32) && !(defined(MI_SHARED_LIB) && defined(_DLL))
+#error "It is only possible to override "malloc" on Windows when building as a DLL (and linking the C runtime as a DLL)"
+#endif
+
+#if defined(MI_MALLOC_OVERRIDE) && !(defined(_WIN32)) // || (defined(__MACH__) && !defined(MI_INTERPOSE)))
+
+// ------------------------------------------------------
+// Override system malloc
+// ------------------------------------------------------
+
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(__MACH__)
+ // use aliasing to alias the exported function to one of our `mi_` functions
+ #if (defined(__GNUC__) && __GNUC__ >= 9)
+ #define MI_FORWARD(fun) __attribute__((alias(#fun), used, visibility("default"), copy(fun)))
+ #else
+ #define MI_FORWARD(fun) __attribute__((alias(#fun), used, visibility("default")))
+ #endif
+ #define MI_FORWARD1(fun,x) MI_FORWARD(fun)
+ #define MI_FORWARD2(fun,x,y) MI_FORWARD(fun)
+ #define MI_FORWARD3(fun,x,y,z) MI_FORWARD(fun)
+ #define MI_FORWARD0(fun,x) MI_FORWARD(fun)
+ #define MI_FORWARD02(fun,x,y) MI_FORWARD(fun)
+#else
+ // use forwarding by calling our `mi_` function
+ #define MI_FORWARD1(fun,x) { return fun(x); }
+ #define MI_FORWARD2(fun,x,y) { return fun(x,y); }
+ #define MI_FORWARD3(fun,x,y,z) { return fun(x,y,z); }
+ #define MI_FORWARD0(fun,x) { fun(x); }
+ #define MI_FORWARD02(fun,x,y) { fun(x,y); }
+#endif
+
+#if defined(__APPLE__) && defined(MI_SHARED_LIB_EXPORT) && defined(MI_INTERPOSE)
+ // use interposing so `DYLD_INSERT_LIBRARIES` works without `DYLD_FORCE_FLAT_NAMESPACE=1`
+ // See: <https://books.google.com/books?id=K8vUkpOXhN4C&pg=PA73>
+ struct mi_interpose_s {
+ const void* replacement;
+ const void* target;
+ };
+ #define MI_INTERPOSE_FUN(oldfun,newfun) { (const void*)&newfun, (const void*)&oldfun }
+ #define MI_INTERPOSE_MI(fun) MI_INTERPOSE_FUN(fun,mi_##fun)
+ __attribute__((used)) static struct mi_interpose_s _mi_interposes[] __attribute__((section("__DATA, __interpose"))) =
+ {
+ MI_INTERPOSE_MI(malloc),
+ MI_INTERPOSE_MI(calloc),
+ MI_INTERPOSE_MI(realloc),
+ MI_INTERPOSE_MI(strdup),
+ MI_INTERPOSE_MI(strndup),
+ MI_INTERPOSE_MI(realpath),
+ MI_INTERPOSE_MI(posix_memalign),
+ MI_INTERPOSE_MI(reallocf),
+ MI_INTERPOSE_MI(valloc),
+ // some code allocates from a zone but deallocates using plain free :-( (like NxHashResizeToCapacity <https://github.com/nneonneo/osx-10.9-opensource/blob/master/objc4-551.1/runtime/hashtable2.mm>)
+ MI_INTERPOSE_FUN(free,mi_cfree), // use safe free that checks if pointers are from us
+ };
+#elif defined(_MSC_VER)
+ // cannot override malloc unless using a dll.
+ // we just override new/delete which does work in a static library.
+#else
+ // On all other systems forward to our API
+ void* malloc(size_t size) MI_FORWARD1(mi_malloc, size);
+ void* calloc(size_t size, size_t n) MI_FORWARD2(mi_calloc, size, n);
+ void* realloc(void* p, size_t newsize) MI_FORWARD2(mi_realloc, p, newsize);
+ void free(void* p) MI_FORWARD0(mi_free, p);
+#endif
+
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(__MACH__)
+#pragma GCC visibility push(default)
+#endif
+
+// ------------------------------------------------------
+// Override new/delete
+// This is not really necessary as they usually call
+// malloc/free anyway, but it improves performance.
+// ------------------------------------------------------
+#ifdef __cplusplus
+ // ------------------------------------------------------
+ // With a C++ compiler we override the new/delete operators.
+ // see <https://en.cppreference.com/w/cpp/memory/new/operator_new>
+ // ------------------------------------------------------
+ #include <new>
+ void operator delete(void* p) noexcept MI_FORWARD0(mi_free,p);
+ void operator delete[](void* p) noexcept MI_FORWARD0(mi_free,p);
+
+ void* operator new(std::size_t n) noexcept(false) MI_FORWARD1(mi_new,n);
+ void* operator new[](std::size_t n) noexcept(false) MI_FORWARD1(mi_new,n);
+
+ void* operator new (std::size_t n, const std::nothrow_t& tag) noexcept { UNUSED(tag); return mi_new_nothrow(n); }
+ void* operator new[](std::size_t n, const std::nothrow_t& tag) noexcept { UNUSED(tag); return mi_new_nothrow(n); }
+
+ #if (__cplusplus >= 201402L || _MSC_VER >= 1916)
+ void operator delete (void* p, std::size_t n) noexcept MI_FORWARD02(mi_free_size,p,n);
+ void operator delete[](void* p, std::size_t n) noexcept MI_FORWARD02(mi_free_size,p,n);
+ #endif
+
+ #if (__cplusplus > 201402L && defined(__cpp_aligned_new)) && (!defined(__GNUC__) || (__GNUC__ > 5))
+ void operator delete (void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
+ void operator delete[](void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
+ void operator delete (void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast<size_t>(al)); };
+ void operator delete[](void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast<size_t>(al)); };
+
+ void* operator new( std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast<size_t>(al)); }
+ void* operator new[]( std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast<size_t>(al)); }
+ void* operator new (std::size_t n, std::align_val_t al, const std::nothrow_t&) noexcept { return mi_new_aligned_nothrow(n, static_cast<size_t>(al)); }
+ void* operator new[](std::size_t n, std::align_val_t al, const std::nothrow_t&) noexcept { return mi_new_aligned_nothrow(n, static_cast<size_t>(al)); }
+ #endif
+
+#elif (defined(__GNUC__) || defined(__clang__))
+ // ------------------------------------------------------
+ // Override by defining the mangled C++ names of the operators (as
+ // used by GCC and CLang).
+ // See <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling>
+ // ------------------------------------------------------
+ void _ZdlPv(void* p) MI_FORWARD0(mi_free,p); // delete
+ void _ZdaPv(void* p) MI_FORWARD0(mi_free,p); // delete[]
+ void _ZdlPvm(void* p, size_t n) MI_FORWARD02(mi_free_size,p,n);
+ void _ZdaPvm(void* p, size_t n) MI_FORWARD02(mi_free_size,p,n);
+ void _ZdlPvSt11align_val_t(void* p, size_t al) { mi_free_aligned(p,al); }
+ void _ZdaPvSt11align_val_t(void* p, size_t al) { mi_free_aligned(p,al); }
+ void _ZdlPvmSt11align_val_t(void* p, size_t n, size_t al) { mi_free_size_aligned(p,n,al); }
+ void _ZdaPvmSt11align_val_t(void* p, size_t n, size_t al) { mi_free_size_aligned(p,n,al); }
+
+ typedef struct mi_nothrow_s { } mi_nothrow_t;
+ #if (MI_INTPTR_SIZE==8)
+ void* _Znwm(size_t n) MI_FORWARD1(mi_new,n); // new 64-bit
+ void* _Znam(size_t n) MI_FORWARD1(mi_new,n); // new[] 64-bit
+ void* _ZnwmSt11align_val_t(size_t n, size_t al) MI_FORWARD2(mi_new_aligned, n, al);
+ void* _ZnamSt11align_val_t(size_t n, size_t al) MI_FORWARD2(mi_new_aligned, n, al);
+ void* _ZnwmRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { UNUSED(tag); return mi_new_nothrow(n); }
+ void* _ZnamRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { UNUSED(tag); return mi_new_nothrow(n); }
+ void* _ZnwmSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { UNUSED(tag); return mi_new_aligned_nothrow(n,al); }
+ void* _ZnamSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { UNUSED(tag); return mi_new_aligned_nothrow(n,al); }
+ #elif (MI_INTPTR_SIZE==4)
+ void* _Znwj(size_t n) MI_FORWARD1(mi_new,n); // new 64-bit
+ void* _Znaj(size_t n) MI_FORWARD1(mi_new,n); // new[] 64-bit
+ void* _ZnwjSt11align_val_t(size_t n, size_t al) MI_FORWARD2(mi_new_aligned, n, al);
+ void* _ZnajSt11align_val_t(size_t n, size_t al) MI_FORWARD2(mi_new_aligned, n, al);
+ void* _ZnwjRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { UNUSED(tag); return mi_new_nothrow(n); }
+ void* _ZnajRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { UNUSED(tag); return mi_new_nothrow(n); }
+ void* _ZnwjSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { UNUSED(tag); return mi_new_aligned_nothrow(n,al); }
+ void* _ZnajSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { UNUSED(tag); return mi_new_aligned_nothrow(n,al); }
+ #else
+ #error "define overloads for new/delete for this platform (just for performance, can be skipped)"
+ #endif
+#endif // __cplusplus
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ------------------------------------------------------
+// Posix & Unix functions definitions
+// ------------------------------------------------------
+
+void cfree(void* p) MI_FORWARD0(mi_free, p);
+void* reallocf(void* p, size_t newsize) MI_FORWARD2(mi_reallocf,p,newsize);
+size_t malloc_size(const void* p) MI_FORWARD1(mi_usable_size,p);
+#if !defined(__ANDROID__)
+size_t malloc_usable_size(void *p) MI_FORWARD1(mi_usable_size,p);
+#else
+size_t malloc_usable_size(const void *p) MI_FORWARD1(mi_usable_size,p);
+#endif
+
+// no forwarding here due to aliasing/name mangling issues
+void* valloc(size_t size) { return mi_valloc(size); }
+void* pvalloc(size_t size) { return mi_pvalloc(size); }
+void* reallocarray(void* p, size_t count, size_t size) { return mi_reallocarray(p, count, size); }
+void* memalign(size_t alignment, size_t size) { return mi_memalign(alignment, size); }
+int posix_memalign(void** p, size_t alignment, size_t size) { return mi_posix_memalign(p, alignment, size); }
+void* _aligned_malloc(size_t alignment, size_t size) { return mi_aligned_alloc(alignment, size); }
+
+// on some glibc `aligned_alloc` is declared `static inline` so we cannot override it (e.g. Conda). This happens
+// when _GLIBCXX_HAVE_ALIGNED_ALLOC is not defined. However, in those cases it will use `memalign`, `posix_memalign`,
+// or `_aligned_malloc` and we can avoid overriding it ourselves.
+// We should always override if using C compilation. (issue #276)
+#if _GLIBCXX_HAVE_ALIGNED_ALLOC || !defined(__cplusplus)
+void* aligned_alloc(size_t alignment, size_t size) { return mi_aligned_alloc(alignment, size); }
+#endif
+
+
+#if defined(__GLIBC__) && defined(__linux__)
+ // forward __libc interface (needed for glibc-based Linux distributions)
+ void* __libc_malloc(size_t size) MI_FORWARD1(mi_malloc,size);
+ void* __libc_calloc(size_t count, size_t size) MI_FORWARD2(mi_calloc,count,size);
+ void* __libc_realloc(void* p, size_t size) MI_FORWARD2(mi_realloc,p,size);
+ void __libc_free(void* p) MI_FORWARD0(mi_free,p);
+ void __libc_cfree(void* p) MI_FORWARD0(mi_free,p);
+
+ void* __libc_valloc(size_t size) { return mi_valloc(size); }
+ void* __libc_pvalloc(size_t size) { return mi_pvalloc(size); }
+ void* __libc_memalign(size_t alignment, size_t size) { return mi_memalign(alignment,size); }
+ int __posix_memalign(void** p, size_t alignment, size_t size) { return mi_posix_memalign(p,alignment,size); }
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(__MACH__)
+#pragma GCC visibility pop
+#endif
+
+#endif // MI_MALLOC_OVERRIDE && !_WIN32
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018,2019, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+// ------------------------------------------------------------------------
+// mi prefixed publi definitions of various Posix, Unix, and C++ functions
+// for convenience and used when overriding these functions.
+// ------------------------------------------------------------------------
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+
+// ------------------------------------------------------
+// Posix & Unix functions definitions
+// ------------------------------------------------------
+
+#include <errno.h>
+#include <string.h> // memcpy
+#include <stdlib.h> // getenv
+
+#ifndef EINVAL
+#define EINVAL 22
+#endif
+#ifndef ENOMEM
+#define ENOMEM 12
+#endif
+
+
+size_t mi_malloc_size(const void* p) mi_attr_noexcept {
+ return mi_usable_size(p);
+}
+
+size_t mi_malloc_usable_size(const void *p) mi_attr_noexcept {
+ return mi_usable_size(p);
+}
+
+void mi_cfree(void* p) mi_attr_noexcept {
+ if (mi_is_in_heap_region(p)) {
+ mi_free(p);
+ }
+}
+
+int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept {
+ // Note: The spec dictates we should not modify `*p` on an error. (issue#27)
+ // <http://man7.org/linux/man-pages/man3/posix_memalign.3.html>
+ if (p == NULL) return EINVAL;
+ if (alignment % sizeof(void*) != 0) return EINVAL; // natural alignment
+ if (!_mi_is_power_of_two(alignment)) return EINVAL; // not a power of 2
+ void* q = (mi_malloc_satisfies_alignment(alignment, size) ? mi_malloc(size) : mi_malloc_aligned(size, alignment));
+ if (q==NULL && size != 0) return ENOMEM;
+ mi_assert_internal(((uintptr_t)q % alignment) == 0);
+ *p = q;
+ return 0;
+}
+
+mi_decl_restrict void* mi_memalign(size_t alignment, size_t size) mi_attr_noexcept {
+ void* p = (mi_malloc_satisfies_alignment(alignment,size) ? mi_malloc(size) : mi_malloc_aligned(size, alignment));
+ mi_assert_internal(((uintptr_t)p % alignment) == 0);
+ return p;
+}
+
+mi_decl_restrict void* mi_valloc(size_t size) mi_attr_noexcept {
+ return mi_memalign( _mi_os_page_size(), size );
+}
+
+mi_decl_restrict void* mi_pvalloc(size_t size) mi_attr_noexcept {
+ size_t psize = _mi_os_page_size();
+ if (size >= SIZE_MAX - psize) return NULL; // overflow
+ size_t asize = _mi_align_up(size, psize);
+ return mi_malloc_aligned(asize, psize);
+}
+
+mi_decl_restrict void* mi_aligned_alloc(size_t alignment, size_t size) mi_attr_noexcept {
+ if (alignment==0 || !_mi_is_power_of_two(alignment)) return NULL;
+ if ((size&(alignment-1)) != 0) return NULL; // C11 requires integral multiple, see <https://en.cppreference.com/w/c/memory/aligned_alloc>
+ void* p = (mi_malloc_satisfies_alignment(alignment, size) ? mi_malloc(size) : mi_malloc_aligned(size, alignment));
+ mi_assert_internal(((uintptr_t)p % alignment) == 0);
+ return p;
+}
+
+void* mi_reallocarray( void* p, size_t count, size_t size ) mi_attr_noexcept { // BSD
+ void* newp = mi_reallocn(p,count,size);
+ if (newp==NULL) errno = ENOMEM;
+ return newp;
+}
+
+void* mi__expand(void* p, size_t newsize) mi_attr_noexcept { // Microsoft
+ void* res = mi_expand(p, newsize);
+ if (res == NULL) errno = ENOMEM;
+ return res;
+}
+
+mi_decl_restrict unsigned short* mi_wcsdup(const unsigned short* s) mi_attr_noexcept {
+ if (s==NULL) return NULL;
+ size_t len;
+ for(len = 0; s[len] != 0; len++) { }
+ size_t size = (len+1)*sizeof(unsigned short);
+ unsigned short* p = (unsigned short*)mi_malloc(size);
+ if (p != NULL) {
+ memcpy(p,s,size);
+ }
+ return p;
+}
+
+mi_decl_restrict unsigned char* mi_mbsdup(const unsigned char* s) mi_attr_noexcept {
+ return (unsigned char*)mi_strdup((const char*)s);
+}
+
+int mi_dupenv_s(char** buf, size_t* size, const char* name) mi_attr_noexcept {
+ if (buf==NULL || name==NULL) return EINVAL;
+ if (size != NULL) *size = 0;
+ #pragma warning(suppress:4996)
+ char* p = getenv(name);
+ if (p==NULL) {
+ *buf = NULL;
+ }
+ else {
+ *buf = mi_strdup(p);
+ if (*buf==NULL) return ENOMEM;
+ if (size != NULL) *size = strlen(p);
+ }
+ return 0;
+}
+
+int mi_wdupenv_s(unsigned short** buf, size_t* size, const unsigned short* name) mi_attr_noexcept {
+ if (buf==NULL || name==NULL) return EINVAL;
+ if (size != NULL) *size = 0;
+#if !defined(_WIN32) || (defined(WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP))
+ // not supported
+ *buf = NULL;
+ return EINVAL;
+#else
+ #pragma warning(suppress:4996)
+ unsigned short* p = (unsigned short*)_wgetenv((const wchar_t*)name);
+ if (p==NULL) {
+ *buf = NULL;
+ }
+ else {
+ *buf = mi_wcsdup(p);
+ if (*buf==NULL) return ENOMEM;
+ if (size != NULL) *size = wcslen((const wchar_t*)p);
+ }
+ return 0;
+#endif
+}
+
+void* mi_aligned_offset_recalloc(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { // Microsoft
+ return mi_recalloc_aligned_at(p, newcount, size, alignment, offset);
+}
+
+void* mi_aligned_recalloc(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { // Microsoft
+ return mi_recalloc_aligned(p, newcount, size, alignment);
+}
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+#include "mimalloc-atomic.h"
+
+#include <string.h> // memset, memcpy, strlen
+#include <stdlib.h> // malloc, exit
+
+#define MI_IN_ALLOC_C
+#include "alloc-override.c"
+#undef MI_IN_ALLOC_C
+
+// ------------------------------------------------------
+// Allocation
+// ------------------------------------------------------
+
+// Fast allocation in a page: just pop from the free list.
+// Fall back to generic allocation only if the list is empty.
+extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept {
+ mi_assert_internal(page->xblock_size==0||mi_page_block_size(page) >= size);
+ mi_block_t* block = page->free;
+ if (mi_unlikely(block == NULL)) {
+ return _mi_malloc_generic(heap, size);
+ }
+ mi_assert_internal(block != NULL && _mi_ptr_page(block) == page);
+ // pop from the free list
+ page->free = mi_block_next(page, block);
+ page->used++;
+ mi_assert_internal(page->free == NULL || _mi_ptr_page(page->free) == page);
+#if (MI_DEBUG>0)
+ if (!page->is_zero) { memset(block, MI_DEBUG_UNINIT, size); }
+#elif (MI_SECURE!=0)
+ block->next = 0; // don't leak internal data
+#endif
+#if (MI_STAT>1)
+ const size_t bsize = mi_page_usable_block_size(page);
+ if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
+ const size_t bin = _mi_bin(bsize);
+ mi_heap_stat_increase(heap, normal[bin], 1);
+ }
+#endif
+#if (MI_PADDING > 0) && defined(MI_ENCODE_FREELIST)
+ mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + mi_page_usable_block_size(page));
+ ptrdiff_t delta = ((uint8_t*)padding - (uint8_t*)block - (size - MI_PADDING_SIZE));
+ mi_assert_internal(delta >= 0 && mi_page_usable_block_size(page) >= (size - MI_PADDING_SIZE + delta));
+ padding->canary = (uint32_t)(mi_ptr_encode(page,block,page->keys));
+ padding->delta = (uint32_t)(delta);
+ uint8_t* fill = (uint8_t*)padding - delta;
+ const size_t maxpad = (delta > MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : delta); // set at most N initial padding bytes
+ for (size_t i = 0; i < maxpad; i++) { fill[i] = MI_DEBUG_PADDING; }
+#endif
+ return block;
+}
+
+// allocate a small block
+extern inline mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_t size) mi_attr_noexcept {
+ mi_assert(heap!=NULL);
+ mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local
+ mi_assert(size <= MI_SMALL_SIZE_MAX);
+ #if (MI_PADDING)
+ if (size == 0) {
+ size = sizeof(void*);
+ }
+ #endif
+ mi_page_t* page = _mi_heap_get_free_small_page(heap,size + MI_PADDING_SIZE);
+ void* p = _mi_page_malloc(heap, page, size + MI_PADDING_SIZE);
+ mi_assert_internal(p==NULL || mi_usable_size(p) >= size);
+ #if MI_STAT>1
+ if (p != NULL) {
+ if (!mi_heap_is_initialized(heap)) { heap = mi_get_default_heap(); }
+ mi_heap_stat_increase(heap, malloc, mi_usable_size(p));
+ }
+ #endif
+ return p;
+}
+
+extern inline mi_decl_restrict void* mi_malloc_small(size_t size) mi_attr_noexcept {
+ return mi_heap_malloc_small(mi_get_default_heap(), size);
+}
+
+// The main allocation function
+extern inline mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept {
+ if (mi_likely(size <= MI_SMALL_SIZE_MAX)) {
+ return mi_heap_malloc_small(heap, size);
+ }
+ else {
+ mi_assert(heap!=NULL);
+ mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local
+ void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE); // note: size can overflow but it is detected in malloc_generic
+ mi_assert_internal(p == NULL || mi_usable_size(p) >= size);
+ #if MI_STAT>1
+ if (p != NULL) {
+ if (!mi_heap_is_initialized(heap)) { heap = mi_get_default_heap(); }
+ mi_heap_stat_increase(heap, malloc, mi_usable_size(p));
+ }
+ #endif
+ return p;
+ }
+}
+
+extern inline mi_decl_restrict void* mi_malloc(size_t size) mi_attr_noexcept {
+ return mi_heap_malloc(mi_get_default_heap(), size);
+}
+
+
+void _mi_block_zero_init(const mi_page_t* page, void* p, size_t size) {
+ // note: we need to initialize the whole usable block size to zero, not just the requested size,
+ // or the recalloc/rezalloc functions cannot safely expand in place (see issue #63)
+ UNUSED(size);
+ mi_assert_internal(p != NULL);
+ mi_assert_internal(mi_usable_size(p) >= size); // size can be zero
+ mi_assert_internal(_mi_ptr_page(p)==page);
+ if (page->is_zero && size > sizeof(mi_block_t)) {
+ // already zero initialized memory
+ ((mi_block_t*)p)->next = 0; // clear the free list pointer
+ mi_assert_expensive(mi_mem_is_zero(p, mi_usable_size(p)));
+ }
+ else {
+ // otherwise memset
+ memset(p, 0, mi_usable_size(p));
+ }
+}
+
+// zero initialized small block
+mi_decl_restrict void* mi_zalloc_small(size_t size) mi_attr_noexcept {
+ void* p = mi_malloc_small(size);
+ if (p != NULL) {
+ _mi_block_zero_init(_mi_ptr_page(p), p, size); // todo: can we avoid getting the page again?
+ }
+ return p;
+}
+
+void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero) {
+ void* p = mi_heap_malloc(heap,size);
+ if (zero && p != NULL) {
+ _mi_block_zero_init(_mi_ptr_page(p),p,size); // todo: can we avoid getting the page again?
+ }
+ return p;
+}
+
+extern inline mi_decl_restrict void* mi_heap_zalloc(mi_heap_t* heap, size_t size) mi_attr_noexcept {
+ return _mi_heap_malloc_zero(heap, size, true);
+}
+
+mi_decl_restrict void* mi_zalloc(size_t size) mi_attr_noexcept {
+ return mi_heap_zalloc(mi_get_default_heap(),size);
+}
+
+
+// ------------------------------------------------------
+// Check for double free in secure and debug mode
+// This is somewhat expensive so only enabled for secure mode 4
+// ------------------------------------------------------
+
+#if (MI_ENCODE_FREELIST && (MI_SECURE>=4 || MI_DEBUG!=0))
+// linear check if the free list contains a specific element
+static bool mi_list_contains(const mi_page_t* page, const mi_block_t* list, const mi_block_t* elem) {
+ while (list != NULL) {
+ if (elem==list) return true;
+ list = mi_block_next(page, list);
+ }
+ return false;
+}
+
+static mi_decl_noinline bool mi_check_is_double_freex(const mi_page_t* page, const mi_block_t* block) {
+ // The decoded value is in the same page (or NULL).
+ // Walk the free lists to verify positively if it is already freed
+ if (mi_list_contains(page, page->free, block) ||
+ mi_list_contains(page, page->local_free, block) ||
+ mi_list_contains(page, mi_page_thread_free(page), block))
+ {
+ _mi_error_message(EAGAIN, "double free detected of block %p with size %zu\n", block, mi_page_block_size(page));
+ return true;
+ }
+ return false;
+}
+
+static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block_t* block) {
+ mi_block_t* n = mi_block_nextx(page, block, page->keys); // pretend it is freed, and get the decoded first field
+ if (((uintptr_t)n & (MI_INTPTR_SIZE-1))==0 && // quick check: aligned pointer?
+ (n==NULL || mi_is_in_same_page(block, n))) // quick check: in same page or NULL?
+ {
+ // Suspicous: decoded value a in block is in the same page (or NULL) -- maybe a double free?
+ // (continue in separate function to improve code generation)
+ return mi_check_is_double_freex(page, block);
+ }
+ return false;
+}
+#else
+static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block_t* block) {
+ UNUSED(page);
+ UNUSED(block);
+ return false;
+}
+#endif
+
+// ---------------------------------------------------------------------------
+// Check for heap block overflow by setting up padding at the end of the block
+// ---------------------------------------------------------------------------
+
+#if (MI_PADDING>0) && defined(MI_ENCODE_FREELIST)
+static bool mi_page_decode_padding(const mi_page_t* page, const mi_block_t* block, size_t* delta, size_t* bsize) {
+ *bsize = mi_page_usable_block_size(page);
+ const mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + *bsize);
+ *delta = padding->delta;
+ return ((uint32_t)mi_ptr_encode(page,block,page->keys) == padding->canary && *delta <= *bsize);
+}
+
+// Return the exact usable size of a block.
+static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block) {
+ size_t bsize;
+ size_t delta;
+ bool ok = mi_page_decode_padding(page, block, &delta, &bsize);
+ mi_assert_internal(ok); mi_assert_internal(delta <= bsize);
+ return (ok ? bsize - delta : 0);
+}
+
+static bool mi_verify_padding(const mi_page_t* page, const mi_block_t* block, size_t* size, size_t* wrong) {
+ size_t bsize;
+ size_t delta;
+ bool ok = mi_page_decode_padding(page, block, &delta, &bsize);
+ *size = *wrong = bsize;
+ if (!ok) return false;
+ mi_assert_internal(bsize >= delta);
+ *size = bsize - delta;
+ uint8_t* fill = (uint8_t*)block + bsize - delta;
+ const size_t maxpad = (delta > MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : delta); // check at most the first N padding bytes
+ for (size_t i = 0; i < maxpad; i++) {
+ if (fill[i] != MI_DEBUG_PADDING) {
+ *wrong = bsize - delta + i;
+ return false;
+ }
+ }
+ return true;
+}
+
+static void mi_check_padding(const mi_page_t* page, const mi_block_t* block) {
+ size_t size;
+ size_t wrong;
+ if (!mi_verify_padding(page,block,&size,&wrong)) {
+ _mi_error_message(EFAULT, "buffer overflow in heap block %p of size %zu: write after %zu bytes\n", block, size, wrong );
+ }
+}
+
+// When a non-thread-local block is freed, it becomes part of the thread delayed free
+// list that is freed later by the owning heap. If the exact usable size is too small to
+// contain the pointer for the delayed list, then shrink the padding (by decreasing delta)
+// so it will later not trigger an overflow error in `mi_free_block`.
+static void mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size) {
+ size_t bsize;
+ size_t delta;
+ bool ok = mi_page_decode_padding(page, block, &delta, &bsize);
+ mi_assert_internal(ok);
+ if (!ok || (bsize - delta) >= min_size) return; // usually already enough space
+ mi_assert_internal(bsize >= min_size);
+ if (bsize < min_size) return; // should never happen
+ size_t new_delta = (bsize - min_size);
+ mi_assert_internal(new_delta < bsize);
+ mi_padding_t* padding = (mi_padding_t*)((uint8_t*)block + bsize);
+ padding->delta = (uint32_t)new_delta;
+}
+#else
+static void mi_check_padding(const mi_page_t* page, const mi_block_t* block) {
+ UNUSED(page);
+ UNUSED(block);
+}
+
+static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block) {
+ UNUSED(block);
+ return mi_page_usable_block_size(page);
+}
+
+static void mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size) {
+ UNUSED(page);
+ UNUSED(block);
+ UNUSED(min_size);
+}
+#endif
+
+// ------------------------------------------------------
+// Free
+// ------------------------------------------------------
+
+// multi-threaded free
+static mi_decl_noinline void _mi_free_block_mt(mi_page_t* page, mi_block_t* block)
+{
+ // The padding check may access the non-thread-owned page for the key values.
+ // that is safe as these are constant and the page won't be freed (as the block is not freed yet).
+ mi_check_padding(page, block);
+ mi_padding_shrink(page, block, sizeof(mi_block_t)); // for small size, ensure we can fit the delayed thread pointers without triggering overflow detection
+ #if (MI_DEBUG!=0)
+ memset(block, MI_DEBUG_FREED, mi_usable_size(block));
+ #endif
+
+ // huge page segments are always abandoned and can be freed immediately
+ mi_segment_t* const segment = _mi_page_segment(page);
+ if (segment->page_kind==MI_PAGE_HUGE) {
+ _mi_segment_huge_page_free(segment, page, block);
+ return;
+ }
+
+ // Try to put the block on either the page-local thread free list, or the heap delayed free list.
+ mi_thread_free_t tfree;
+ mi_thread_free_t tfreex;
+ bool use_delayed;
+ do {
+ tfree = mi_atomic_read_relaxed(&page->xthread_free);
+ use_delayed = (mi_tf_delayed(tfree) == MI_USE_DELAYED_FREE);
+ if (mi_unlikely(use_delayed)) {
+ // unlikely: this only happens on the first concurrent free in a page that is in the full list
+ tfreex = mi_tf_set_delayed(tfree,MI_DELAYED_FREEING);
+ }
+ else {
+ // usual: directly add to page thread_free list
+ mi_block_set_next(page, block, mi_tf_block(tfree));
+ tfreex = mi_tf_set_block(tfree,block);
+ }
+ } while (!mi_atomic_cas_weak(&page->xthread_free, tfreex, tfree));
+
+ if (mi_unlikely(use_delayed)) {
+ // racy read on `heap`, but ok because MI_DELAYED_FREEING is set (see `mi_heap_delete` and `mi_heap_collect_abandon`)
+ mi_heap_t* const heap = mi_page_heap(page);
+ mi_assert_internal(heap != NULL);
+ if (heap != NULL) {
+ // add to the delayed free list of this heap. (do this atomically as the lock only protects heap memory validity)
+ mi_block_t* dfree;
+ do {
+ dfree = mi_atomic_read_ptr_relaxed(mi_block_t,&heap->thread_delayed_free);
+ mi_block_set_nextx(heap,block,dfree, heap->keys);
+ } while (!mi_atomic_cas_ptr_weak(mi_block_t,&heap->thread_delayed_free, block, dfree));
+ }
+
+ // and reset the MI_DELAYED_FREEING flag
+ do {
+ tfreex = tfree = mi_atomic_read_relaxed(&page->xthread_free);
+ mi_assert_internal(mi_tf_delayed(tfree) == MI_DELAYED_FREEING);
+ tfreex = mi_tf_set_delayed(tfree,MI_NO_DELAYED_FREE);
+ } while (!mi_atomic_cas_weak(&page->xthread_free, tfreex, tfree));
+ }
+}
+
+
+// regular free
+static inline void _mi_free_block(mi_page_t* page, bool local, mi_block_t* block)
+{
+ // and push it on the free list
+ if (mi_likely(local)) {
+ // owning thread can free a block directly
+ if (mi_unlikely(mi_check_is_double_free(page, block))) return;
+ mi_check_padding(page, block);
+ #if (MI_DEBUG!=0)
+ memset(block, MI_DEBUG_FREED, mi_page_block_size(page));
+ #endif
+ mi_block_set_next(page, block, page->local_free);
+ page->local_free = block;
+ page->used--;
+ if (mi_unlikely(mi_page_all_free(page))) {
+ _mi_page_retire(page);
+ }
+ else if (mi_unlikely(mi_page_is_in_full(page))) {
+ _mi_page_unfull(page);
+ }
+ }
+ else {
+ _mi_free_block_mt(page,block);
+ }
+}
+
+
+// Adjust a block that was allocated aligned, to the actual start of the block in the page.
+mi_block_t* _mi_page_ptr_unalign(const mi_segment_t* segment, const mi_page_t* page, const void* p) {
+ mi_assert_internal(page!=NULL && p!=NULL);
+ const size_t diff = (uint8_t*)p - _mi_page_start(segment, page, NULL);
+ const size_t adjust = (diff % mi_page_block_size(page));
+ return (mi_block_t*)((uintptr_t)p - adjust);
+}
+
+
+static void mi_decl_noinline mi_free_generic(const mi_segment_t* segment, bool local, void* p) {
+ mi_page_t* const page = _mi_segment_page_of(segment, p);
+ mi_block_t* const block = (mi_page_has_aligned(page) ? _mi_page_ptr_unalign(segment, page, p) : (mi_block_t*)p);
+ _mi_free_block(page, local, block);
+}
+
+// Get the segment data belonging to a pointer
+// This is just a single `and` in assembly but does further checks in debug mode
+// (and secure mode) if this was a valid pointer.
+static inline mi_segment_t* mi_checked_ptr_segment(const void* p, const char* msg)
+{
+ UNUSED(msg);
+#if (MI_DEBUG>0)
+ if (mi_unlikely(((uintptr_t)p & (MI_INTPTR_SIZE - 1)) != 0)) {
+ _mi_error_message(EINVAL, "%s: invalid (unaligned) pointer: %p\n", msg, p);
+ return NULL;
+ }
+#endif
+
+ mi_segment_t* const segment = _mi_ptr_segment(p);
+ if (mi_unlikely(segment == NULL)) return NULL; // checks also for (p==NULL)
+
+#if (MI_DEBUG>0)
+ if (mi_unlikely(!mi_is_in_heap_region(p))) {
+ _mi_warning_message("%s: pointer might not point to a valid heap region: %p\n"
+ "(this may still be a valid very large allocation (over 64MiB))\n", msg, p);
+ if (mi_likely(_mi_ptr_cookie(segment) == segment->cookie)) {
+ _mi_warning_message("(yes, the previous pointer %p was valid after all)\n", p);
+ }
+ }
+#endif
+#if (MI_DEBUG>0 || MI_SECURE>=4)
+ if (mi_unlikely(_mi_ptr_cookie(segment) != segment->cookie)) {
+ _mi_error_message(EINVAL, "%s: pointer does not point to a valid heap space: %p\n", p);
+ }
+#endif
+ return segment;
+}
+
+
+// Free a block
+void mi_free(void* p) mi_attr_noexcept
+{
+ const mi_segment_t* const segment = mi_checked_ptr_segment(p,"mi_free");
+ if (mi_unlikely(segment == NULL)) return;
+
+ const uintptr_t tid = _mi_thread_id();
+ mi_page_t* const page = _mi_segment_page_of(segment, p);
+ mi_block_t* const block = (mi_block_t*)p;
+
+#if (MI_STAT>1)
+ mi_heap_t* const heap = mi_heap_get_default();
+ const size_t bsize = mi_page_usable_block_size(page);
+ mi_heap_stat_decrease(heap, malloc, bsize);
+ if (bsize <= MI_LARGE_OBJ_SIZE_MAX) { // huge page stats are accounted for in `_mi_page_retire`
+ mi_heap_stat_decrease(heap, normal[_mi_bin(bsize)], 1);
+ }
+#endif
+
+ if (mi_likely(tid == segment->thread_id && page->flags.full_aligned == 0)) { // the thread id matches and it is not a full page, nor has aligned blocks
+ // local, and not full or aligned
+ if (mi_unlikely(mi_check_is_double_free(page,block))) return;
+ mi_check_padding(page, block);
+ #if (MI_DEBUG!=0)
+ memset(block, MI_DEBUG_FREED, mi_page_block_size(page));
+ #endif
+ mi_block_set_next(page, block, page->local_free);
+ page->local_free = block;
+ page->used--;
+ if (mi_unlikely(mi_page_all_free(page))) {
+ _mi_page_retire(page);
+ }
+ }
+ else {
+ // non-local, aligned blocks, or a full page; use the more generic path
+ // note: recalc page in generic to improve code generation
+ mi_free_generic(segment, tid == segment->thread_id, p);
+ }
+}
+
+bool _mi_free_delayed_block(mi_block_t* block) {
+ // get segment and page
+ const mi_segment_t* const segment = _mi_ptr_segment(block);
+ mi_assert_internal(_mi_ptr_cookie(segment) == segment->cookie);
+ mi_assert_internal(_mi_thread_id() == segment->thread_id);
+ mi_page_t* const page = _mi_segment_page_of(segment, block);
+
+ // Clear the no-delayed flag so delayed freeing is used again for this page.
+ // This must be done before collecting the free lists on this page -- otherwise
+ // some blocks may end up in the page `thread_free` list with no blocks in the
+ // heap `thread_delayed_free` list which may cause the page to be never freed!
+ // (it would only be freed if we happen to scan it in `mi_page_queue_find_free_ex`)
+ _mi_page_use_delayed_free(page, MI_USE_DELAYED_FREE, false /* dont overwrite never delayed */);
+
+ // collect all other non-local frees to ensure up-to-date `used` count
+ _mi_page_free_collect(page, false);
+
+ // and free the block (possibly freeing the page as well since used is updated)
+ _mi_free_block(page, true, block);
+ return true;
+}
+
+// Bytes available in a block
+static size_t _mi_usable_size(const void* p, const char* msg) mi_attr_noexcept {
+ const mi_segment_t* const segment = mi_checked_ptr_segment(p,msg);
+ if (segment==NULL) return 0;
+ const mi_page_t* const page = _mi_segment_page_of(segment, p);
+ const mi_block_t* block = (const mi_block_t*)p;
+ if (mi_unlikely(mi_page_has_aligned(page))) {
+ block = _mi_page_ptr_unalign(segment, page, p);
+ size_t size = mi_page_usable_size_of(page, block);
+ ptrdiff_t const adjust = (uint8_t*)p - (uint8_t*)block;
+ mi_assert_internal(adjust >= 0 && (size_t)adjust <= size);
+ return (size - adjust);
+ }
+ else {
+ return mi_page_usable_size_of(page, block);
+ }
+}
+
+size_t mi_usable_size(const void* p) mi_attr_noexcept {
+ return _mi_usable_size(p, "mi_usable_size");
+}
+
+
+// ------------------------------------------------------
+// ensure explicit external inline definitions are emitted!
+// ------------------------------------------------------
+
+#ifdef __cplusplus
+void* _mi_externs[] = {
+ (void*)&_mi_page_malloc,
+ (void*)&mi_malloc,
+ (void*)&mi_malloc_small,
+ (void*)&mi_heap_malloc,
+ (void*)&mi_heap_zalloc,
+ (void*)&mi_heap_malloc_small
+};
+#endif
+
+
+// ------------------------------------------------------
+// Allocation extensions
+// ------------------------------------------------------
+
+void mi_free_size(void* p, size_t size) mi_attr_noexcept {
+ UNUSED_RELEASE(size);
+ mi_assert(p == NULL || size <= _mi_usable_size(p,"mi_free_size"));
+ mi_free(p);
+}
+
+void mi_free_size_aligned(void* p, size_t size, size_t alignment) mi_attr_noexcept {
+ UNUSED_RELEASE(alignment);
+ mi_assert(((uintptr_t)p % alignment) == 0);
+ mi_free_size(p,size);
+}
+
+void mi_free_aligned(void* p, size_t alignment) mi_attr_noexcept {
+ UNUSED_RELEASE(alignment);
+ mi_assert(((uintptr_t)p % alignment) == 0);
+ mi_free(p);
+}
+
+extern inline mi_decl_restrict void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(count,size,&total)) return NULL;
+ return mi_heap_zalloc(heap,total);
+}
+
+mi_decl_restrict void* mi_calloc(size_t count, size_t size) mi_attr_noexcept {
+ return mi_heap_calloc(mi_get_default_heap(),count,size);
+}
+
+// Uninitialized `calloc`
+extern mi_decl_restrict void* mi_heap_mallocn(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(count, size, &total)) return NULL;
+ return mi_heap_malloc(heap, total);
+}
+
+mi_decl_restrict void* mi_mallocn(size_t count, size_t size) mi_attr_noexcept {
+ return mi_heap_mallocn(mi_get_default_heap(),count,size);
+}
+
+// Expand in place or fail
+void* mi_expand(void* p, size_t newsize) mi_attr_noexcept {
+ if (p == NULL) return NULL;
+ size_t size = _mi_usable_size(p,"mi_expand");
+ if (newsize > size) return NULL;
+ return p; // it fits
+}
+
+void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero) {
+ if (p == NULL) return _mi_heap_malloc_zero(heap,newsize,zero);
+ size_t size = _mi_usable_size(p,"mi_realloc");
+ if (newsize <= size && newsize >= (size / 2)) {
+ return p; // reallocation still fits and not more than 50% waste
+ }
+ void* newp = mi_heap_malloc(heap,newsize);
+ if (mi_likely(newp != NULL)) {
+ if (zero && newsize > size) {
+ // also set last word in the previous allocation to zero to ensure any padding is zero-initialized
+ size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0);
+ memset((uint8_t*)newp + start, 0, newsize - start);
+ }
+ memcpy(newp, p, (newsize > size ? size : newsize));
+ mi_free(p); // only free if successful
+ }
+ return newp;
+}
+
+void* mi_heap_realloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept {
+ return _mi_heap_realloc_zero(heap, p, newsize, false);
+}
+
+void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(count, size, &total)) return NULL;
+ return mi_heap_realloc(heap, p, total);
+}
+
+
+// Reallocate but free `p` on errors
+void* mi_heap_reallocf(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept {
+ void* newp = mi_heap_realloc(heap, p, newsize);
+ if (newp==NULL && p!=NULL) mi_free(p);
+ return newp;
+}
+
+void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept {
+ return _mi_heap_realloc_zero(heap, p, newsize, true);
+}
+
+void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(count, size, &total)) return NULL;
+ return mi_heap_rezalloc(heap, p, total);
+}
+
+
+void* mi_realloc(void* p, size_t newsize) mi_attr_noexcept {
+ return mi_heap_realloc(mi_get_default_heap(),p,newsize);
+}
+
+void* mi_reallocn(void* p, size_t count, size_t size) mi_attr_noexcept {
+ return mi_heap_reallocn(mi_get_default_heap(),p,count,size);
+}
+
+// Reallocate but free `p` on errors
+void* mi_reallocf(void* p, size_t newsize) mi_attr_noexcept {
+ return mi_heap_reallocf(mi_get_default_heap(),p,newsize);
+}
+
+void* mi_rezalloc(void* p, size_t newsize) mi_attr_noexcept {
+ return mi_heap_rezalloc(mi_get_default_heap(), p, newsize);
+}
+
+void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept {
+ return mi_heap_recalloc(mi_get_default_heap(), p, count, size);
+}
+
+
+
+// ------------------------------------------------------
+// strdup, strndup, and realpath
+// ------------------------------------------------------
+
+// `strdup` using mi_malloc
+mi_decl_restrict char* mi_heap_strdup(mi_heap_t* heap, const char* s) mi_attr_noexcept {
+ if (s == NULL) return NULL;
+ size_t n = strlen(s);
+ char* t = (char*)mi_heap_malloc(heap,n+1);
+ if (t != NULL) memcpy(t, s, n + 1);
+ return t;
+}
+
+mi_decl_restrict char* mi_strdup(const char* s) mi_attr_noexcept {
+ return mi_heap_strdup(mi_get_default_heap(), s);
+}
+
+// `strndup` using mi_malloc
+mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n) mi_attr_noexcept {
+ if (s == NULL) return NULL;
+ const char* end = (const char*)memchr(s, 0, n); // find end of string in the first `n` characters (returns NULL if not found)
+ const size_t m = (end != NULL ? (size_t)(end - s) : n); // `m` is the minimum of `n` or the end-of-string
+ mi_assert_internal(m <= n);
+ char* t = (char*)mi_heap_malloc(heap, m+1);
+ if (t == NULL) return NULL;
+ memcpy(t, s, m);
+ t[m] = 0;
+ return t;
+}
+
+mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_attr_noexcept {
+ return mi_heap_strndup(mi_get_default_heap(),s,n);
+}
+
+#ifndef __wasi__
+// `realpath` using mi_malloc
+#ifdef _WIN32
+#ifndef PATH_MAX
+#define PATH_MAX MAX_PATH
+#endif
+#include <windows.h>
+mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept {
+ // todo: use GetFullPathNameW to allow longer file names
+ char buf[PATH_MAX];
+ DWORD res = GetFullPathNameA(fname, PATH_MAX, (resolved_name == NULL ? buf : resolved_name), NULL);
+ if (res == 0) {
+ errno = GetLastError(); return NULL;
+ }
+ else if (res > PATH_MAX) {
+ errno = EINVAL; return NULL;
+ }
+ else if (resolved_name != NULL) {
+ return resolved_name;
+ }
+ else {
+ return mi_heap_strndup(heap, buf, PATH_MAX);
+ }
+}
+#else
+#include <unistd.h> // pathconf
+static size_t mi_path_max() {
+ static size_t path_max = 0;
+ if (path_max <= 0) {
+ long m = pathconf("/",_PC_PATH_MAX);
+ if (m <= 0) path_max = 4096; // guess
+ else if (m < 256) path_max = 256; // at least 256
+ else path_max = m;
+ }
+ return path_max;
+}
+
+char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept {
+ if (resolved_name != NULL) {
+ return realpath(fname,resolved_name);
+ }
+ else {
+ size_t n = mi_path_max();
+ char* buf = (char*)mi_malloc(n+1);
+ if (buf==NULL) return NULL;
+ char* rname = realpath(fname,buf);
+ char* result = mi_heap_strndup(heap,rname,n); // ok if `rname==NULL`
+ mi_free(buf);
+ return result;
+ }
+}
+#endif
+
+mi_decl_restrict char* mi_realpath(const char* fname, char* resolved_name) mi_attr_noexcept {
+ return mi_heap_realpath(mi_get_default_heap(),fname,resolved_name);
+}
+#endif
+
+/*-------------------------------------------------------
+C++ new and new_aligned
+The standard requires calling into `get_new_handler` and
+throwing the bad_alloc exception on failure. If we compile
+with a C++ compiler we can implement this precisely. If we
+use a C compiler we cannot throw a `bad_alloc` exception
+but we call `exit` instead (i.e. not returning).
+-------------------------------------------------------*/
+
+#ifdef __cplusplus
+#include <new>
+static bool mi_try_new_handler(bool nothrow) {
+ std::new_handler h = std::get_new_handler();
+ if (h==NULL) {
+ if (!nothrow) throw std::bad_alloc();
+ return false;
+ }
+ else {
+ h();
+ return true;
+ }
+}
+#else
+typedef void (*std_new_handler_t)();
+
+#if (defined(__GNUC__) || defined(__clang__))
+std_new_handler_t __attribute((weak)) _ZSt15get_new_handlerv() {
+ return NULL;
+}
+std_new_handler_t mi_get_new_handler() {
+ return _ZSt15get_new_handlerv();
+}
+#else
+// note: on windows we could dynamically link to `?get_new_handler@std@@YAP6AXXZXZ`.
+std_new_handler_t mi_get_new_handler() {
+ return NULL;
+}
+#endif
+
+static bool mi_try_new_handler(bool nothrow) {
+ std_new_handler_t h = mi_get_new_handler();
+ if (h==NULL) {
+ if (!nothrow) exit(ENOMEM); // cannot throw in plain C, use exit as we are out of memory anyway.
+ return false;
+ }
+ else {
+ h();
+ return true;
+ }
+}
+#endif
+
+static mi_decl_noinline void* mi_try_new(size_t size, bool nothrow ) {
+ void* p = NULL;
+ while(p == NULL && mi_try_new_handler(nothrow)) {
+ p = mi_malloc(size);
+ }
+ return p;
+}
+
+mi_decl_restrict void* mi_new(size_t size) {
+ void* p = mi_malloc(size);
+ if (mi_unlikely(p == NULL)) return mi_try_new(size,false);
+ return p;
+}
+
+mi_decl_restrict void* mi_new_nothrow(size_t size) mi_attr_noexcept {
+ void* p = mi_malloc(size);
+ if (mi_unlikely(p == NULL)) return mi_try_new(size, true);
+ return p;
+}
+
+mi_decl_restrict void* mi_new_aligned(size_t size, size_t alignment) {
+ void* p;
+ do {
+ p = mi_malloc_aligned(size, alignment);
+ }
+ while(p == NULL && mi_try_new_handler(false));
+ return p;
+}
+
+mi_decl_restrict void* mi_new_aligned_nothrow(size_t size, size_t alignment) mi_attr_noexcept {
+ void* p;
+ do {
+ p = mi_malloc_aligned(size, alignment);
+ }
+ while(p == NULL && mi_try_new_handler(true));
+ return p;
+}
+
+mi_decl_restrict void* mi_new_n(size_t count, size_t size) {
+ size_t total;
+ if (mi_unlikely(mi_count_size_overflow(count, size, &total))) {
+ mi_try_new_handler(false); // on overflow we invoke the try_new_handler once to potentially throw std::bad_alloc
+ return NULL;
+ }
+ else {
+ return mi_new(total);
+ }
+}
+
+void* mi_new_realloc(void* p, size_t newsize) {
+ void* q;
+ do {
+ q = mi_realloc(p, newsize);
+ } while (q == NULL && mi_try_new_handler(false));
+ return q;
+}
+
+void* mi_new_reallocn(void* p, size_t newcount, size_t size) {
+ size_t total;
+ if (mi_unlikely(mi_count_size_overflow(newcount, size, &total))) {
+ mi_try_new_handler(false); // on overflow we invoke the try_new_handler once to potentially throw std::bad_alloc
+ return NULL;
+ }
+ else {
+ return mi_new_realloc(p, total);
+ }
+}
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2019, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+/* ----------------------------------------------------------------------------
+"Arenas" are fixed area's of OS memory from which we can allocate
+large blocks (>= MI_ARENA_BLOCK_SIZE, 32MiB).
+In contrast to the rest of mimalloc, the arenas are shared between
+threads and need to be accessed using atomic operations.
+
+Currently arenas are only used to for huge OS page (1GiB) reservations,
+otherwise it delegates to direct allocation from the OS.
+In the future, we can expose an API to manually add more kinds of arenas
+which is sometimes needed for embedded devices or shared memory for example.
+(We can also employ this with WASI or `sbrk` systems to reserve large arenas
+ on demand and be able to reuse them efficiently).
+
+The arena allocation needs to be thread safe and we use an atomic
+bitmap to allocate. The current implementation of the bitmap can
+only do this within a field (`uintptr_t`) so we can allocate at most
+blocks of 2GiB (64*32MiB) and no object can cross the boundary. This
+can lead to fragmentation but fortunately most objects will be regions
+of 256MiB in practice.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+#include "mimalloc-atomic.h"
+
+#include <string.h> // memset
+
+#include "bitmap.inc.c" // atomic bitmap
+
+
+// os.c
+void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool* large, mi_os_tld_t* tld);
+void _mi_os_free_ex(void* p, size_t size, bool was_committed, mi_stats_t* stats);
+void _mi_os_free(void* p, size_t size, mi_stats_t* stats);
+
+void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_secs, size_t* pages_reserved, size_t* psize);
+void _mi_os_free_huge_pages(void* p, size_t size, mi_stats_t* stats);
+
+bool _mi_os_commit(void* p, size_t size, bool* is_zero, mi_stats_t* stats);
+
+/* -----------------------------------------------------------
+ Arena allocation
+----------------------------------------------------------- */
+
+#define MI_SEGMENT_ALIGN MI_SEGMENT_SIZE
+#define MI_ARENA_BLOCK_SIZE (8*MI_SEGMENT_ALIGN) // 32MiB
+#define MI_ARENA_MAX_OBJ_SIZE (MI_BITMAP_FIELD_BITS * MI_ARENA_BLOCK_SIZE) // 2GiB
+#define MI_ARENA_MIN_OBJ_SIZE (MI_ARENA_BLOCK_SIZE/2) // 16MiB
+#define MI_MAX_ARENAS (64) // not more than 256 (since we use 8 bits in the memid)
+
+// A memory arena descriptor
+typedef struct mi_arena_s {
+ _Atomic(uint8_t*) start; // the start of the memory area
+ size_t block_count; // size of the area in arena blocks (of `MI_ARENA_BLOCK_SIZE`)
+ size_t field_count; // number of bitmap fields (where `field_count * MI_BITMAP_FIELD_BITS >= block_count`)
+ int numa_node; // associated NUMA node
+ bool is_zero_init; // is the arena zero initialized?
+ bool is_committed; // is the memory committed
+ bool is_large; // large OS page allocated
+ volatile _Atomic(uintptr_t) search_idx; // optimization to start the search for free blocks
+ mi_bitmap_field_t* blocks_dirty; // are the blocks potentially non-zero?
+ mi_bitmap_field_t* blocks_committed; // if `!is_committed`, are the blocks committed?
+ mi_bitmap_field_t blocks_inuse[1]; // in-place bitmap of in-use blocks (of size `field_count`)
+} mi_arena_t;
+
+
+// The available arenas
+static mi_decl_cache_align _Atomic(mi_arena_t*) mi_arenas[MI_MAX_ARENAS];
+static mi_decl_cache_align _Atomic(uintptr_t) mi_arena_count; // = 0
+
+
+/* -----------------------------------------------------------
+ Arena allocations get a memory id where the lower 8 bits are
+ the arena index +1, and the upper bits the block index.
+----------------------------------------------------------- */
+
+// Use `0` as a special id for direct OS allocated memory.
+#define MI_MEMID_OS 0
+
+static size_t mi_arena_id_create(size_t arena_index, mi_bitmap_index_t bitmap_index) {
+ mi_assert_internal(arena_index < 0xFE);
+ mi_assert_internal(((bitmap_index << 8) >> 8) == bitmap_index); // no overflow?
+ return ((bitmap_index << 8) | ((arena_index+1) & 0xFF));
+}
+
+static void mi_arena_id_indices(size_t memid, size_t* arena_index, mi_bitmap_index_t* bitmap_index) {
+ mi_assert_internal(memid != MI_MEMID_OS);
+ *arena_index = (memid & 0xFF) - 1;
+ *bitmap_index = (memid >> 8);
+}
+
+static size_t mi_block_count_of_size(size_t size) {
+ return _mi_divide_up(size, MI_ARENA_BLOCK_SIZE);
+}
+
+/* -----------------------------------------------------------
+ Thread safe allocation in an arena
+----------------------------------------------------------- */
+static bool mi_arena_alloc(mi_arena_t* arena, size_t blocks, mi_bitmap_index_t* bitmap_idx)
+{
+ const size_t fcount = arena->field_count;
+ size_t idx = mi_atomic_read(&arena->search_idx); // start from last search
+ for (size_t visited = 0; visited < fcount; visited++, idx++) {
+ if (idx >= fcount) idx = 0; // wrap around
+ // try to atomically claim a range of bits
+ if (mi_bitmap_try_find_claim_field(arena->blocks_inuse, idx, blocks, bitmap_idx)) {
+ mi_atomic_write(&arena->search_idx, idx); // start search from here next time
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/* -----------------------------------------------------------
+ Arena Allocation
+----------------------------------------------------------- */
+
+static void* mi_arena_alloc_from(mi_arena_t* arena, size_t arena_index, size_t needed_bcount,
+ bool* commit, bool* large, bool* is_zero, size_t* memid, mi_os_tld_t* tld)
+{
+ mi_bitmap_index_t bitmap_index;
+ if (!mi_arena_alloc(arena, needed_bcount, &bitmap_index)) return NULL;
+
+ // claimed it! set the dirty bits (todo: no need for an atomic op here?)
+ void* p = arena->start + (mi_bitmap_index_bit(bitmap_index)*MI_ARENA_BLOCK_SIZE);
+ *memid = mi_arena_id_create(arena_index, bitmap_index);
+ *is_zero = mi_bitmap_claim(arena->blocks_dirty, arena->field_count, needed_bcount, bitmap_index, NULL);
+ *large = arena->is_large;
+ if (arena->is_committed) {
+ // always committed
+ *commit = true;
+ }
+ else if (*commit) {
+ // arena not committed as a whole, but commit requested: ensure commit now
+ bool any_uncommitted;
+ mi_bitmap_claim(arena->blocks_committed, arena->field_count, needed_bcount, bitmap_index, &any_uncommitted);
+ if (any_uncommitted) {
+ bool commit_zero;
+ _mi_os_commit(p, needed_bcount * MI_ARENA_BLOCK_SIZE, &commit_zero, tld->stats);
+ if (commit_zero) *is_zero = true;
+ }
+ }
+ else {
+ // no need to commit, but check if already fully committed
+ *commit = mi_bitmap_is_claimed(arena->blocks_committed, arena->field_count, needed_bcount, bitmap_index);
+ }
+ return p;
+}
+
+void* _mi_arena_alloc_aligned(size_t size, size_t alignment,
+ bool* commit, bool* large, bool* is_zero,
+ size_t* memid, mi_os_tld_t* tld)
+{
+ mi_assert_internal(commit != NULL && large != NULL && is_zero != NULL && memid != NULL && tld != NULL);
+ mi_assert_internal(size > 0);
+ *memid = MI_MEMID_OS;
+ *is_zero = false;
+
+ // try to allocate in an arena if the alignment is small enough
+ // and the object is not too large or too small.
+ if (alignment <= MI_SEGMENT_ALIGN &&
+ size <= MI_ARENA_MAX_OBJ_SIZE &&
+ size >= MI_ARENA_MIN_OBJ_SIZE)
+ {
+ const size_t bcount = mi_block_count_of_size(size);
+ const int numa_node = _mi_os_numa_node(tld); // current numa node
+
+ mi_assert_internal(size <= bcount*MI_ARENA_BLOCK_SIZE);
+ // try numa affine allocation
+ for (size_t i = 0; i < MI_MAX_ARENAS; i++) {
+ mi_arena_t* arena = mi_atomic_read_ptr_relaxed(mi_arena_t, &mi_arenas[i]);
+ if (arena==NULL) break; // end reached
+ if ((arena->numa_node<0 || arena->numa_node==numa_node) && // numa local?
+ (*large || !arena->is_large)) // large OS pages allowed, or arena is not large OS pages
+ {
+ void* p = mi_arena_alloc_from(arena, i, bcount, commit, large, is_zero, memid, tld);
+ mi_assert_internal((uintptr_t)p % alignment == 0);
+ if (p != NULL) return p;
+ }
+ }
+ // try from another numa node instead..
+ for (size_t i = 0; i < MI_MAX_ARENAS; i++) {
+ mi_arena_t* arena = mi_atomic_read_ptr_relaxed(mi_arena_t, &mi_arenas[i]);
+ if (arena==NULL) break; // end reached
+ if ((arena->numa_node>=0 && arena->numa_node!=numa_node) && // not numa local!
+ (*large || !arena->is_large)) // large OS pages allowed, or arena is not large OS pages
+ {
+ void* p = mi_arena_alloc_from(arena, i, bcount, commit, large, is_zero, memid, tld);
+ mi_assert_internal((uintptr_t)p % alignment == 0);
+ if (p != NULL) return p;
+ }
+ }
+ }
+
+ // finally, fall back to the OS
+ *is_zero = true;
+ *memid = MI_MEMID_OS;
+ return _mi_os_alloc_aligned(size, alignment, *commit, large, tld);
+}
+
+void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_zero, size_t* memid, mi_os_tld_t* tld)
+{
+ return _mi_arena_alloc_aligned(size, MI_ARENA_BLOCK_SIZE, commit, large, is_zero, memid, tld);
+}
+
+/* -----------------------------------------------------------
+ Arena free
+----------------------------------------------------------- */
+
+void _mi_arena_free(void* p, size_t size, size_t memid, bool all_committed, mi_stats_t* stats) {
+ mi_assert_internal(size > 0 && stats != NULL);
+ if (p==NULL) return;
+ if (size==0) return;
+ if (memid == MI_MEMID_OS) {
+ // was a direct OS allocation, pass through
+ _mi_os_free_ex(p, size, all_committed, stats);
+ }
+ else {
+ // allocated in an arena
+ size_t arena_idx;
+ size_t bitmap_idx;
+ mi_arena_id_indices(memid, &arena_idx, &bitmap_idx);
+ mi_assert_internal(arena_idx < MI_MAX_ARENAS);
+ mi_arena_t* arena = mi_atomic_read_ptr_relaxed(mi_arena_t,&mi_arenas[arena_idx]);
+ mi_assert_internal(arena != NULL);
+ if (arena == NULL) {
+ _mi_error_message(EINVAL, "trying to free from non-existent arena: %p, size %zu, memid: 0x%zx\n", p, size, memid);
+ return;
+ }
+ mi_assert_internal(arena->field_count > mi_bitmap_index_field(bitmap_idx));
+ if (arena->field_count <= mi_bitmap_index_field(bitmap_idx)) {
+ _mi_error_message(EINVAL, "trying to free from non-existent arena block: %p, size %zu, memid: 0x%zx\n", p, size, memid);
+ return;
+ }
+ const size_t blocks = mi_block_count_of_size(size);
+ bool ones = mi_bitmap_unclaim(arena->blocks_inuse, arena->field_count, blocks, bitmap_idx);
+ if (!ones) {
+ _mi_error_message(EAGAIN, "trying to free an already freed block: %p, size %zu\n", p, size);
+ return;
+ };
+ }
+}
+
+/* -----------------------------------------------------------
+ Add an arena.
+----------------------------------------------------------- */
+
+static bool mi_arena_add(mi_arena_t* arena) {
+ mi_assert_internal(arena != NULL);
+ mi_assert_internal((uintptr_t)mi_atomic_read_ptr_relaxed(uint8_t,&arena->start) % MI_SEGMENT_ALIGN == 0);
+ mi_assert_internal(arena->block_count > 0);
+
+ uintptr_t i = mi_atomic_increment(&mi_arena_count);
+ if (i >= MI_MAX_ARENAS) {
+ mi_atomic_decrement(&mi_arena_count);
+ return false;
+ }
+ mi_atomic_write_ptr(mi_arena_t,&mi_arenas[i], arena);
+ return true;
+}
+
+
+/* -----------------------------------------------------------
+ Reserve a huge page arena.
+----------------------------------------------------------- */
+#include <errno.h> // ENOMEM
+
+// reserve at a specific numa node
+int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs) mi_attr_noexcept {
+ if (pages==0) return 0;
+ if (numa_node < -1) numa_node = -1;
+ if (numa_node >= 0) numa_node = numa_node % _mi_os_numa_node_count();
+ size_t hsize = 0;
+ size_t pages_reserved = 0;
+ void* p = _mi_os_alloc_huge_os_pages(pages, numa_node, timeout_msecs, &pages_reserved, &hsize);
+ if (p==NULL || pages_reserved==0) {
+ _mi_warning_message("failed to reserve %zu gb huge pages\n", pages);
+ return ENOMEM;
+ }
+ _mi_verbose_message("numa node %i: reserved %zu gb huge pages (of the %zu gb requested)\n", numa_node, pages_reserved, pages);
+
+ size_t bcount = mi_block_count_of_size(hsize);
+ size_t fields = _mi_divide_up(bcount, MI_BITMAP_FIELD_BITS);
+ size_t asize = sizeof(mi_arena_t) + (2*fields*sizeof(mi_bitmap_field_t));
+ mi_arena_t* arena = (mi_arena_t*)_mi_os_alloc(asize, &_mi_stats_main); // TODO: can we avoid allocating from the OS?
+ if (arena == NULL) {
+ _mi_os_free_huge_pages(p, hsize, &_mi_stats_main);
+ return ENOMEM;
+ }
+ arena->block_count = bcount;
+ arena->field_count = fields;
+ arena->start = (uint8_t*)p;
+ arena->numa_node = numa_node; // TODO: or get the current numa node if -1? (now it allows anyone to allocate on -1)
+ arena->is_large = true;
+ arena->is_zero_init = true;
+ arena->is_committed = true;
+ arena->search_idx = 0;
+ arena->blocks_dirty = &arena->blocks_inuse[fields]; // just after inuse bitmap
+ arena->blocks_committed = NULL;
+ // the bitmaps are already zero initialized due to os_alloc
+ // just claim leftover blocks if needed
+ ptrdiff_t post = (fields * MI_BITMAP_FIELD_BITS) - bcount;
+ mi_assert_internal(post >= 0);
+ if (post > 0) {
+ // don't use leftover bits at the end
+ mi_bitmap_index_t postidx = mi_bitmap_index_create(fields - 1, MI_BITMAP_FIELD_BITS - post);
+ mi_bitmap_claim(arena->blocks_inuse, fields, post, postidx, NULL);
+ }
+
+ mi_arena_add(arena);
+ return 0;
+}
+
+
+// reserve huge pages evenly among the given number of numa nodes (or use the available ones as detected)
+int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs) mi_attr_noexcept {
+ if (pages == 0) return 0;
+
+ // pages per numa node
+ size_t numa_count = (numa_nodes > 0 ? numa_nodes : _mi_os_numa_node_count());
+ if (numa_count <= 0) numa_count = 1;
+ const size_t pages_per = pages / numa_count;
+ const size_t pages_mod = pages % numa_count;
+ const size_t timeout_per = (timeout_msecs==0 ? 0 : (timeout_msecs / numa_count) + 50);
+
+ // reserve evenly among numa nodes
+ for (size_t numa_node = 0; numa_node < numa_count && pages > 0; numa_node++) {
+ size_t node_pages = pages_per; // can be 0
+ if (numa_node < pages_mod) node_pages++;
+ int err = mi_reserve_huge_os_pages_at(node_pages, (int)numa_node, timeout_per);
+ if (err) return err;
+ if (pages < node_pages) {
+ pages = 0;
+ }
+ else {
+ pages -= node_pages;
+ }
+ }
+
+ return 0;
+}
+
+int mi_reserve_huge_os_pages(size_t pages, double max_secs, size_t* pages_reserved) mi_attr_noexcept {
+ UNUSED(max_secs);
+ _mi_warning_message("mi_reserve_huge_os_pages is deprecated: use mi_reserve_huge_os_pages_interleave/at instead\n");
+ if (pages_reserved != NULL) *pages_reserved = 0;
+ int err = mi_reserve_huge_os_pages_interleave(pages, 0, (size_t)(max_secs * 1000.0));
+ if (err==0 && pages_reserved!=NULL) *pages_reserved = pages;
+ return err;
+}
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2019, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+/* ----------------------------------------------------------------------------
+This file is meant to be included in other files for efficiency.
+It implements a bitmap that can set/reset sequences of bits atomically
+and is used to concurrently claim memory ranges.
+
+A bitmap is an array of fields where each field is a machine word (`uintptr_t`)
+
+A current limitation is that the bit sequences cannot cross fields
+and that the sequence must be smaller or equal to the bits in a field.
+---------------------------------------------------------------------------- */
+#pragma once
+#ifndef MI_BITMAP_C
+#define MI_BITMAP_C
+
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+
+/* -----------------------------------------------------------
+ Bitmap definition
+----------------------------------------------------------- */
+
+#define MI_BITMAP_FIELD_BITS (8*MI_INTPTR_SIZE)
+#define MI_BITMAP_FIELD_FULL (~((uintptr_t)0)) // all bits set
+
+// An atomic bitmap of `uintptr_t` fields
+typedef volatile _Atomic(uintptr_t) mi_bitmap_field_t;
+typedef mi_bitmap_field_t* mi_bitmap_t;
+
+// A bitmap index is the index of the bit in a bitmap.
+typedef size_t mi_bitmap_index_t;
+
+// Create a bit index.
+static inline mi_bitmap_index_t mi_bitmap_index_create(size_t idx, size_t bitidx) {
+ mi_assert_internal(bitidx < MI_BITMAP_FIELD_BITS);
+ return (idx*MI_BITMAP_FIELD_BITS) + bitidx;
+}
+
+// Get the field index from a bit index.
+static inline size_t mi_bitmap_index_field(mi_bitmap_index_t bitmap_idx) {
+ return (bitmap_idx / MI_BITMAP_FIELD_BITS);
+}
+
+// Get the bit index in a bitmap field
+static inline size_t mi_bitmap_index_bit_in_field(mi_bitmap_index_t bitmap_idx) {
+ return (bitmap_idx % MI_BITMAP_FIELD_BITS);
+}
+
+// Get the full bit index
+static inline size_t mi_bitmap_index_bit(mi_bitmap_index_t bitmap_idx) {
+ return bitmap_idx;
+}
+
+
+// The bit mask for a given number of blocks at a specified bit index.
+static inline uintptr_t mi_bitmap_mask_(size_t count, size_t bitidx) {
+ mi_assert_internal(count + bitidx <= MI_BITMAP_FIELD_BITS);
+ if (count == MI_BITMAP_FIELD_BITS) return MI_BITMAP_FIELD_FULL;
+ return ((((uintptr_t)1 << count) - 1) << bitidx);
+}
+
+
+/* -----------------------------------------------------------
+ Use bit scan forward/reverse to quickly find the first zero bit if it is available
+----------------------------------------------------------- */
+#if defined(_MSC_VER)
+#define MI_HAVE_BITSCAN
+#include <intrin.h>
+static inline size_t mi_bsf(uintptr_t x) {
+ if (x==0) return 8*MI_INTPTR_SIZE;
+ DWORD idx;
+ MI_64(_BitScanForward)(&idx, x);
+ return idx;
+}
+static inline size_t mi_bsr(uintptr_t x) {
+ if (x==0) return 8*MI_INTPTR_SIZE;
+ DWORD idx;
+ MI_64(_BitScanReverse)(&idx, x);
+ return idx;
+}
+#elif defined(__GNUC__) || defined(__clang__)
+#include <limits.h> // LONG_MAX
+#define MI_HAVE_BITSCAN
+#if (INTPTR_MAX == LONG_MAX)
+# define MI_L(x) x##l
+#else
+# define MI_L(x) x##ll
+#endif
+static inline size_t mi_bsf(uintptr_t x) {
+ return (x==0 ? 8*MI_INTPTR_SIZE : MI_L(__builtin_ctz)(x));
+}
+static inline size_t mi_bsr(uintptr_t x) {
+ return (x==0 ? 8*MI_INTPTR_SIZE : (8*MI_INTPTR_SIZE - 1) - MI_L(__builtin_clz)(x));
+}
+#endif
+
+/* -----------------------------------------------------------
+ Claim a bit sequence atomically
+----------------------------------------------------------- */
+
+// Try to atomically claim a sequence of `count` bits at in `idx`
+// in the bitmap field. Returns `true` on success.
+static inline bool mi_bitmap_try_claim_field(mi_bitmap_t bitmap, size_t bitmap_fields, const size_t count, mi_bitmap_index_t bitmap_idx) {
+ const size_t idx = mi_bitmap_index_field(bitmap_idx);
+ const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
+ const uintptr_t mask = mi_bitmap_mask_(count, bitidx);
+ mi_assert_internal(bitmap_fields > idx); UNUSED(bitmap_fields);
+ mi_assert_internal(bitidx + count <= MI_BITMAP_FIELD_BITS);
+
+ uintptr_t field = mi_atomic_read_relaxed(&bitmap[idx]);
+ if ((field & mask) == 0) { // free?
+ if (mi_atomic_cas_strong(&bitmap[idx], (field|mask), field)) {
+ // claimed!
+ return true;
+ }
+ }
+ return false;
+}
+
+
+// Try to atomically claim a sequence of `count` bits in a single
+// field at `idx` in `bitmap`. Returns `true` on success.
+static inline bool mi_bitmap_try_find_claim_field(mi_bitmap_t bitmap, size_t idx, const size_t count, mi_bitmap_index_t* bitmap_idx)
+{
+ mi_assert_internal(bitmap_idx != NULL);
+ volatile _Atomic(uintptr_t)* field = &bitmap[idx];
+ uintptr_t map = mi_atomic_read(field);
+ if (map==MI_BITMAP_FIELD_FULL) return false; // short cut
+
+ // search for 0-bit sequence of length count
+ const uintptr_t mask = mi_bitmap_mask_(count, 0);
+ const size_t bitidx_max = MI_BITMAP_FIELD_BITS - count;
+
+#ifdef MI_HAVE_BITSCAN
+ size_t bitidx = mi_bsf(~map); // quickly find the first zero bit if possible
+#else
+ size_t bitidx = 0; // otherwise start at 0
+#endif
+ uintptr_t m = (mask << bitidx); // invariant: m == mask shifted by bitidx
+
+ // scan linearly for a free range of zero bits
+ while (bitidx <= bitidx_max) {
+ if ((map & m) == 0) { // are the mask bits free at bitidx?
+ mi_assert_internal((m >> bitidx) == mask); // no overflow?
+ const uintptr_t newmap = map | m;
+ mi_assert_internal((newmap^map) >> bitidx == mask);
+ if (!mi_atomic_cas_weak(field, newmap, map)) { // TODO: use strong cas here?
+ // no success, another thread claimed concurrently.. keep going
+ map = mi_atomic_read(field);
+ continue;
+ }
+ else {
+ // success, we claimed the bits!
+ *bitmap_idx = mi_bitmap_index_create(idx, bitidx);
+ return true;
+ }
+ }
+ else {
+ // on to the next bit range
+#ifdef MI_HAVE_BITSCAN
+ const size_t shift = (count == 1 ? 1 : mi_bsr(map & m) - bitidx + 1);
+ mi_assert_internal(shift > 0 && shift <= count);
+#else
+ const size_t shift = 1;
+#endif
+ bitidx += shift;
+ m <<= shift;
+ }
+ }
+ // no bits found
+ return false;
+}
+
+
+// Find `count` bits of 0 and set them to 1 atomically; returns `true` on success.
+// For now, `count` can be at most MI_BITMAP_FIELD_BITS and will never span fields.
+static inline bool mi_bitmap_try_find_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t* bitmap_idx) {
+ for (size_t idx = 0; idx < bitmap_fields; idx++) {
+ if (mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Set `count` bits at `bitmap_idx` to 0 atomically
+// Returns `true` if all `count` bits were 1 previously.
+static inline bool mi_bitmap_unclaim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
+ const size_t idx = mi_bitmap_index_field(bitmap_idx);
+ const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
+ const uintptr_t mask = mi_bitmap_mask_(count, bitidx);
+ mi_assert_internal(bitmap_fields > idx); UNUSED(bitmap_fields);
+ // mi_assert_internal((bitmap[idx] & mask) == mask);
+ uintptr_t prev = mi_atomic_and(&bitmap[idx], ~mask);
+ return ((prev & mask) == mask);
+}
+
+
+// Set `count` bits at `bitmap_idx` to 1 atomically
+// Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit.
+static inline bool mi_bitmap_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* any_zero) {
+ const size_t idx = mi_bitmap_index_field(bitmap_idx);
+ const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
+ const uintptr_t mask = mi_bitmap_mask_(count, bitidx);
+ mi_assert_internal(bitmap_fields > idx); UNUSED(bitmap_fields);
+ //mi_assert_internal(any_zero != NULL || (bitmap[idx] & mask) == 0);
+ uintptr_t prev = mi_atomic_or(&bitmap[idx], mask);
+ if (any_zero != NULL) *any_zero = ((prev & mask) != mask);
+ return ((prev & mask) == 0);
+}
+
+// Returns `true` if all `count` bits were 1. `any_ones` is `true` if there was at least one bit set to one.
+static inline bool mi_bitmap_is_claimedx(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* any_ones) {
+ const size_t idx = mi_bitmap_index_field(bitmap_idx);
+ const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
+ const uintptr_t mask = mi_bitmap_mask_(count, bitidx);
+ mi_assert_internal(bitmap_fields > idx); UNUSED(bitmap_fields);
+ uintptr_t field = mi_atomic_read_relaxed(&bitmap[idx]);
+ if (any_ones != NULL) *any_ones = ((field & mask) != 0);
+ return ((field & mask) == mask);
+}
+
+static inline bool mi_bitmap_is_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
+ return mi_bitmap_is_claimedx(bitmap, bitmap_fields, count, bitmap_idx, NULL);
+}
+
+static inline bool mi_bitmap_is_any_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
+ bool any_ones;
+ mi_bitmap_is_claimedx(bitmap, bitmap_fields, count, bitmap_idx, &any_ones);
+ return any_ones;
+}
+
+
+#endif
--- /dev/null
+/*----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+#include "mimalloc-atomic.h"
+
+#include <string.h> // memset, memcpy
+
+
+/* -----------------------------------------------------------
+ Helpers
+----------------------------------------------------------- */
+
+// return `true` if ok, `false` to break
+typedef bool (heap_page_visitor_fun)(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2);
+
+// Visit all pages in a heap; returns `false` if break was called.
+static bool mi_heap_visit_pages(mi_heap_t* heap, heap_page_visitor_fun* fn, void* arg1, void* arg2)
+{
+ if (heap==NULL || heap->page_count==0) return 0;
+
+ // visit all pages
+ #if MI_DEBUG>1
+ size_t total = heap->page_count;
+ #endif
+ size_t count = 0;
+ for (size_t i = 0; i <= MI_BIN_FULL; i++) {
+ mi_page_queue_t* pq = &heap->pages[i];
+ mi_page_t* page = pq->first;
+ while(page != NULL) {
+ mi_page_t* next = page->next; // save next in case the page gets removed from the queue
+ mi_assert_internal(mi_page_heap(page) == heap);
+ count++;
+ if (!fn(heap, pq, page, arg1, arg2)) return false;
+ page = next; // and continue
+ }
+ }
+ mi_assert_internal(count == total);
+ return true;
+}
+
+
+#if MI_DEBUG>=2
+static bool mi_heap_page_is_valid(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) {
+ UNUSED(arg1);
+ UNUSED(arg2);
+ UNUSED(pq);
+ mi_assert_internal(mi_page_heap(page) == heap);
+ mi_segment_t* segment = _mi_page_segment(page);
+ mi_assert_internal(segment->thread_id == heap->thread_id);
+ mi_assert_expensive(_mi_page_is_valid(page));
+ return true;
+}
+#endif
+#if MI_DEBUG>=3
+static bool mi_heap_is_valid(mi_heap_t* heap) {
+ mi_assert_internal(heap!=NULL);
+ mi_heap_visit_pages(heap, &mi_heap_page_is_valid, NULL, NULL);
+ return true;
+}
+#endif
+
+
+
+
+/* -----------------------------------------------------------
+ "Collect" pages by migrating `local_free` and `thread_free`
+ lists and freeing empty pages. This is done when a thread
+ stops (and in that case abandons pages if there are still
+ blocks alive)
+----------------------------------------------------------- */
+
+typedef enum mi_collect_e {
+ MI_NORMAL,
+ MI_FORCE,
+ MI_ABANDON
+} mi_collect_t;
+
+
+static bool mi_heap_page_collect(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg_collect, void* arg2 ) {
+ UNUSED(arg2);
+ UNUSED(heap);
+ mi_assert_internal(mi_heap_page_is_valid(heap, pq, page, NULL, NULL));
+ mi_collect_t collect = *((mi_collect_t*)arg_collect);
+ _mi_page_free_collect(page, collect >= MI_FORCE);
+ if (mi_page_all_free(page)) {
+ // no more used blocks, free the page.
+ // note: this will free retired pages as well.
+ _mi_page_free(page, pq, collect >= MI_FORCE);
+ }
+ else if (collect == MI_ABANDON) {
+ // still used blocks but the thread is done; abandon the page
+ _mi_page_abandon(page, pq);
+ }
+ return true; // don't break
+}
+
+static bool mi_heap_page_never_delayed_free(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) {
+ UNUSED(arg1);
+ UNUSED(arg2);
+ UNUSED(heap);
+ UNUSED(pq);
+ _mi_page_use_delayed_free(page, MI_NEVER_DELAYED_FREE, false);
+ return true; // don't break
+}
+
+static void mi_heap_collect_ex(mi_heap_t* heap, mi_collect_t collect)
+{
+ if (!mi_heap_is_initialized(heap)) return;
+ _mi_deferred_free(heap, collect >= MI_FORCE);
+
+ // note: never reclaim on collect but leave it to threads that need storage to reclaim
+ if (
+ #ifdef NDEBUG
+ collect == MI_FORCE
+ #else
+ collect >= MI_FORCE
+ #endif
+ && _mi_is_main_thread() && mi_heap_is_backing(heap) && !heap->no_reclaim)
+ {
+ // the main thread is abandoned (end-of-program), try to reclaim all abandoned segments.
+ // if all memory is freed by now, all segments should be freed.
+ _mi_abandoned_reclaim_all(heap, &heap->tld->segments);
+ }
+
+ // if abandoning, mark all pages to no longer add to delayed_free
+ if (collect == MI_ABANDON) {
+ mi_heap_visit_pages(heap, &mi_heap_page_never_delayed_free, NULL, NULL);
+ }
+
+ // free thread delayed blocks.
+ // (if abandoning, after this there are no more thread-delayed references into the pages.)
+ _mi_heap_delayed_free(heap);
+
+ // collect retired pages
+ _mi_heap_collect_retired(heap, collect >= MI_FORCE);
+
+ // collect all pages owned by this thread
+ mi_heap_visit_pages(heap, &mi_heap_page_collect, &collect, NULL);
+ mi_assert_internal( collect != MI_ABANDON || mi_atomic_read_ptr(mi_block_t,&heap->thread_delayed_free) == NULL );
+
+ // collect segment caches
+ if (collect >= MI_FORCE) {
+ _mi_segment_thread_collect(&heap->tld->segments);
+ }
+
+ // collect regions on program-exit (or shared library unload)
+ if (collect >= MI_FORCE && _mi_is_main_thread() && mi_heap_is_backing(heap)) {
+ _mi_mem_collect(&heap->tld->os);
+ }
+}
+
+void _mi_heap_collect_abandon(mi_heap_t* heap) {
+ mi_heap_collect_ex(heap, MI_ABANDON);
+}
+
+void mi_heap_collect(mi_heap_t* heap, bool force) mi_attr_noexcept {
+ mi_heap_collect_ex(heap, (force ? MI_FORCE : MI_NORMAL));
+}
+
+void mi_collect(bool force) mi_attr_noexcept {
+ mi_heap_collect(mi_get_default_heap(), force);
+}
+
+
+/* -----------------------------------------------------------
+ Heap new
+----------------------------------------------------------- */
+
+mi_heap_t* mi_heap_get_default(void) {
+ mi_thread_init();
+ return mi_get_default_heap();
+}
+
+mi_heap_t* mi_heap_get_backing(void) {
+ mi_heap_t* heap = mi_heap_get_default();
+ mi_assert_internal(heap!=NULL);
+ mi_heap_t* bheap = heap->tld->heap_backing;
+ mi_assert_internal(bheap!=NULL);
+ mi_assert_internal(bheap->thread_id == _mi_thread_id());
+ return bheap;
+}
+
+mi_heap_t* mi_heap_new(void) {
+ mi_heap_t* bheap = mi_heap_get_backing();
+ mi_heap_t* heap = mi_heap_malloc_tp(bheap, mi_heap_t); // todo: OS allocate in secure mode?
+ if (heap==NULL) return NULL;
+ memcpy(heap, &_mi_heap_empty, sizeof(mi_heap_t));
+ heap->tld = bheap->tld;
+ heap->thread_id = _mi_thread_id();
+ _mi_random_split(&bheap->random, &heap->random);
+ heap->cookie = _mi_heap_random_next(heap) | 1;
+ heap->keys[0] = _mi_heap_random_next(heap);
+ heap->keys[1] = _mi_heap_random_next(heap);
+ heap->no_reclaim = true; // don't reclaim abandoned pages or otherwise destroy is unsafe
+ // push on the thread local heaps list
+ heap->next = heap->tld->heaps;
+ heap->tld->heaps = heap;
+ return heap;
+}
+
+uintptr_t _mi_heap_random_next(mi_heap_t* heap) {
+ return _mi_random_next(&heap->random);
+}
+
+// zero out the page queues
+static void mi_heap_reset_pages(mi_heap_t* heap) {
+ mi_assert_internal(mi_heap_is_initialized(heap));
+ // TODO: copy full empty heap instead?
+ memset(&heap->pages_free_direct, 0, sizeof(heap->pages_free_direct));
+#ifdef MI_MEDIUM_DIRECT
+ memset(&heap->pages_free_medium, 0, sizeof(heap->pages_free_medium));
+#endif
+ memcpy(&heap->pages, &_mi_heap_empty.pages, sizeof(heap->pages));
+ heap->thread_delayed_free = NULL;
+ heap->page_count = 0;
+}
+
+// called from `mi_heap_destroy` and `mi_heap_delete` to free the internal heap resources.
+static void mi_heap_free(mi_heap_t* heap) {
+ mi_assert(heap != NULL);
+ mi_assert_internal(mi_heap_is_initialized(heap));
+ if (mi_heap_is_backing(heap)) return; // dont free the backing heap
+
+ // reset default
+ if (mi_heap_is_default(heap)) {
+ _mi_heap_set_default_direct(heap->tld->heap_backing);
+ }
+
+ // remove ourselves from the thread local heaps list
+ // linear search but we expect the number of heaps to be relatively small
+ mi_heap_t* prev = NULL;
+ mi_heap_t* curr = heap->tld->heaps;
+ while (curr != heap && curr != NULL) {
+ prev = curr;
+ curr = curr->next;
+ }
+ mi_assert_internal(curr == heap);
+ if (curr == heap) {
+ if (prev != NULL) { prev->next = heap->next; }
+ else { heap->tld->heaps = heap->next; }
+ }
+ mi_assert_internal(heap->tld->heaps != NULL);
+
+ // and free the used memory
+ mi_free(heap);
+}
+
+
+/* -----------------------------------------------------------
+ Heap destroy
+----------------------------------------------------------- */
+
+static bool _mi_heap_page_destroy(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) {
+ UNUSED(arg1);
+ UNUSED(arg2);
+ UNUSED(heap);
+ UNUSED(pq);
+
+ // ensure no more thread_delayed_free will be added
+ _mi_page_use_delayed_free(page, MI_NEVER_DELAYED_FREE, false);
+
+ // stats
+ const size_t bsize = mi_page_block_size(page);
+ if (bsize > MI_LARGE_OBJ_SIZE_MAX) {
+ if (bsize > MI_HUGE_OBJ_SIZE_MAX) {
+ _mi_stat_decrease(&heap->tld->stats.giant, bsize);
+ }
+ else {
+ _mi_stat_decrease(&heap->tld->stats.huge, bsize);
+ }
+ }
+#if (MI_STAT>1)
+ _mi_page_free_collect(page, false); // update used count
+ const size_t inuse = page->used;
+ if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
+ mi_heap_stat_decrease(heap, normal[_mi_bin(bsize)], inuse);
+ }
+ mi_heap_stat_decrease(heap, malloc, bsize * inuse); // todo: off for aligned blocks...
+#endif
+
+ /// pretend it is all free now
+ mi_assert_internal(mi_page_thread_free(page) == NULL);
+ page->used = 0;
+
+ // and free the page
+ // mi_page_free(page,false);
+ page->next = NULL;
+ page->prev = NULL;
+ _mi_segment_page_free(page,false /* no force? */, &heap->tld->segments);
+
+ return true; // keep going
+}
+
+void _mi_heap_destroy_pages(mi_heap_t* heap) {
+ mi_heap_visit_pages(heap, &_mi_heap_page_destroy, NULL, NULL);
+ mi_heap_reset_pages(heap);
+}
+
+void mi_heap_destroy(mi_heap_t* heap) {
+ mi_assert(heap != NULL);
+ mi_assert(mi_heap_is_initialized(heap));
+ mi_assert(heap->no_reclaim);
+ mi_assert_expensive(mi_heap_is_valid(heap));
+ if (!mi_heap_is_initialized(heap)) return;
+ if (!heap->no_reclaim) {
+ // don't free in case it may contain reclaimed pages
+ mi_heap_delete(heap);
+ }
+ else {
+ // free all pages
+ _mi_heap_destroy_pages(heap);
+ mi_heap_free(heap);
+ }
+}
+
+
+
+/* -----------------------------------------------------------
+ Safe Heap delete
+----------------------------------------------------------- */
+
+// Tranfer the pages from one heap to the other
+static void mi_heap_absorb(mi_heap_t* heap, mi_heap_t* from) {
+ mi_assert_internal(heap!=NULL);
+ if (from==NULL || from->page_count == 0) return;
+
+ // reduce the size of the delayed frees
+ _mi_heap_delayed_free(from);
+
+ // transfer all pages by appending the queues; this will set a new heap field
+ // so threads may do delayed frees in either heap for a while.
+ // note: appending waits for each page to not be in the `MI_DELAYED_FREEING` state
+ // so after this only the new heap will get delayed frees
+ for (size_t i = 0; i <= MI_BIN_FULL; i++) {
+ mi_page_queue_t* pq = &heap->pages[i];
+ mi_page_queue_t* append = &from->pages[i];
+ size_t pcount = _mi_page_queue_append(heap, pq, append);
+ heap->page_count += pcount;
+ from->page_count -= pcount;
+ }
+ mi_assert_internal(from->page_count == 0);
+
+ // and do outstanding delayed frees in the `from` heap
+ // note: be careful here as the `heap` field in all those pages no longer point to `from`,
+ // turns out to be ok as `_mi_heap_delayed_free` only visits the list and calls a
+ // the regular `_mi_free_delayed_block` which is safe.
+ _mi_heap_delayed_free(from);
+ mi_assert_internal(from->thread_delayed_free == NULL);
+
+ // and reset the `from` heap
+ mi_heap_reset_pages(from);
+}
+
+// Safe delete a heap without freeing any still allocated blocks in that heap.
+void mi_heap_delete(mi_heap_t* heap)
+{
+ mi_assert(heap != NULL);
+ mi_assert(mi_heap_is_initialized(heap));
+ mi_assert_expensive(mi_heap_is_valid(heap));
+ if (!mi_heap_is_initialized(heap)) return;
+
+ if (!mi_heap_is_backing(heap)) {
+ // tranfer still used pages to the backing heap
+ mi_heap_absorb(heap->tld->heap_backing, heap);
+ }
+ else {
+ // the backing heap abandons its pages
+ _mi_heap_collect_abandon(heap);
+ }
+ mi_assert_internal(heap->page_count==0);
+ mi_heap_free(heap);
+}
+
+mi_heap_t* mi_heap_set_default(mi_heap_t* heap) {
+ mi_assert(mi_heap_is_initialized(heap));
+ if (!mi_heap_is_initialized(heap)) return NULL;
+ mi_assert_expensive(mi_heap_is_valid(heap));
+ mi_heap_t* old = mi_get_default_heap();
+ _mi_heap_set_default_direct(heap);
+ return old;
+}
+
+
+
+
+/* -----------------------------------------------------------
+ Analysis
+----------------------------------------------------------- */
+
+// static since it is not thread safe to access heaps from other threads.
+static mi_heap_t* mi_heap_of_block(const void* p) {
+ if (p == NULL) return NULL;
+ mi_segment_t* segment = _mi_ptr_segment(p);
+ bool valid = (_mi_ptr_cookie(segment) == segment->cookie);
+ mi_assert_internal(valid);
+ if (mi_unlikely(!valid)) return NULL;
+ return mi_page_heap(_mi_segment_page_of(segment,p));
+}
+
+bool mi_heap_contains_block(mi_heap_t* heap, const void* p) {
+ mi_assert(heap != NULL);
+ if (!mi_heap_is_initialized(heap)) return false;
+ return (heap == mi_heap_of_block(p));
+}
+
+
+static bool mi_heap_page_check_owned(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* p, void* vfound) {
+ UNUSED(heap);
+ UNUSED(pq);
+ bool* found = (bool*)vfound;
+ mi_segment_t* segment = _mi_page_segment(page);
+ void* start = _mi_page_start(segment, page, NULL);
+ void* end = (uint8_t*)start + (page->capacity * mi_page_block_size(page));
+ *found = (p >= start && p < end);
+ return (!*found); // continue if not found
+}
+
+bool mi_heap_check_owned(mi_heap_t* heap, const void* p) {
+ mi_assert(heap != NULL);
+ if (!mi_heap_is_initialized(heap)) return false;
+ if (((uintptr_t)p & (MI_INTPTR_SIZE - 1)) != 0) return false; // only aligned pointers
+ bool found = false;
+ mi_heap_visit_pages(heap, &mi_heap_page_check_owned, (void*)p, &found);
+ return found;
+}
+
+bool mi_check_owned(const void* p) {
+ return mi_heap_check_owned(mi_get_default_heap(), p);
+}
+
+/* -----------------------------------------------------------
+ Visit all heap blocks and areas
+ Todo: enable visiting abandoned pages, and
+ enable visiting all blocks of all heaps across threads
+----------------------------------------------------------- */
+
+// Separate struct to keep `mi_page_t` out of the public interface
+typedef struct mi_heap_area_ex_s {
+ mi_heap_area_t area;
+ mi_page_t* page;
+} mi_heap_area_ex_t;
+
+static bool mi_heap_area_visit_blocks(const mi_heap_area_ex_t* xarea, mi_block_visit_fun* visitor, void* arg) {
+ mi_assert(xarea != NULL);
+ if (xarea==NULL) return true;
+ const mi_heap_area_t* area = &xarea->area;
+ mi_page_t* page = xarea->page;
+ mi_assert(page != NULL);
+ if (page == NULL) return true;
+
+ _mi_page_free_collect(page,true);
+ mi_assert_internal(page->local_free == NULL);
+ if (page->used == 0) return true;
+
+ const size_t bsize = mi_page_block_size(page);
+ size_t psize;
+ uint8_t* pstart = _mi_page_start(_mi_page_segment(page), page, &psize);
+
+ if (page->capacity == 1) {
+ // optimize page with one block
+ mi_assert_internal(page->used == 1 && page->free == NULL);
+ return visitor(mi_page_heap(page), area, pstart, bsize, arg);
+ }
+
+ // create a bitmap of free blocks.
+ #define MI_MAX_BLOCKS (MI_SMALL_PAGE_SIZE / sizeof(void*))
+ uintptr_t free_map[MI_MAX_BLOCKS / sizeof(uintptr_t)];
+ memset(free_map, 0, sizeof(free_map));
+
+ size_t free_count = 0;
+ for (mi_block_t* block = page->free; block != NULL; block = mi_block_next(page,block)) {
+ free_count++;
+ mi_assert_internal((uint8_t*)block >= pstart && (uint8_t*)block < (pstart + psize));
+ size_t offset = (uint8_t*)block - pstart;
+ mi_assert_internal(offset % bsize == 0);
+ size_t blockidx = offset / bsize; // Todo: avoid division?
+ mi_assert_internal( blockidx < MI_MAX_BLOCKS);
+ size_t bitidx = (blockidx / sizeof(uintptr_t));
+ size_t bit = blockidx - (bitidx * sizeof(uintptr_t));
+ free_map[bitidx] |= ((uintptr_t)1 << bit);
+ }
+ mi_assert_internal(page->capacity == (free_count + page->used));
+
+ // walk through all blocks skipping the free ones
+ size_t used_count = 0;
+ for (size_t i = 0; i < page->capacity; i++) {
+ size_t bitidx = (i / sizeof(uintptr_t));
+ size_t bit = i - (bitidx * sizeof(uintptr_t));
+ uintptr_t m = free_map[bitidx];
+ if (bit == 0 && m == UINTPTR_MAX) {
+ i += (sizeof(uintptr_t) - 1); // skip a run of free blocks
+ }
+ else if ((m & ((uintptr_t)1 << bit)) == 0) {
+ used_count++;
+ uint8_t* block = pstart + (i * bsize);
+ if (!visitor(mi_page_heap(page), area, block, bsize, arg)) return false;
+ }
+ }
+ mi_assert_internal(page->used == used_count);
+ return true;
+}
+
+typedef bool (mi_heap_area_visit_fun)(const mi_heap_t* heap, const mi_heap_area_ex_t* area, void* arg);
+
+
+static bool mi_heap_visit_areas_page(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* vfun, void* arg) {
+ UNUSED(heap);
+ UNUSED(pq);
+ mi_heap_area_visit_fun* fun = (mi_heap_area_visit_fun*)vfun;
+ mi_heap_area_ex_t xarea;
+ const size_t bsize = mi_page_block_size(page);
+ xarea.page = page;
+ xarea.area.reserved = page->reserved * bsize;
+ xarea.area.committed = page->capacity * bsize;
+ xarea.area.blocks = _mi_page_start(_mi_page_segment(page), page, NULL);
+ xarea.area.used = page->used;
+ xarea.area.block_size = bsize;
+ return fun(heap, &xarea, arg);
+}
+
+// Visit all heap pages as areas
+static bool mi_heap_visit_areas(const mi_heap_t* heap, mi_heap_area_visit_fun* visitor, void* arg) {
+ if (visitor == NULL) return false;
+ return mi_heap_visit_pages((mi_heap_t*)heap, &mi_heap_visit_areas_page, (void*)(visitor), arg); // note: function pointer to void* :-{
+}
+
+// Just to pass arguments
+typedef struct mi_visit_blocks_args_s {
+ bool visit_blocks;
+ mi_block_visit_fun* visitor;
+ void* arg;
+} mi_visit_blocks_args_t;
+
+static bool mi_heap_area_visitor(const mi_heap_t* heap, const mi_heap_area_ex_t* xarea, void* arg) {
+ mi_visit_blocks_args_t* args = (mi_visit_blocks_args_t*)arg;
+ if (!args->visitor(heap, &xarea->area, NULL, xarea->area.block_size, args->arg)) return false;
+ if (args->visit_blocks) {
+ return mi_heap_area_visit_blocks(xarea, args->visitor, args->arg);
+ }
+ else {
+ return true;
+ }
+}
+
+// Visit all blocks in a heap
+bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_blocks, mi_block_visit_fun* visitor, void* arg) {
+ mi_visit_blocks_args_t args = { visit_blocks, visitor, arg };
+ return mi_heap_visit_areas(heap, &mi_heap_area_visitor, &args);
+}
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+
+#include <string.h> // memcpy, memset
+#include <stdlib.h> // atexit
+
+// Empty page used to initialize the small free pages array
+const mi_page_t _mi_page_empty = {
+ 0, false, false, false, false,
+ 0, // capacity
+ 0, // reserved capacity
+ { 0 }, // flags
+ false, // is_zero
+ 0, // retire_expire
+ NULL, // free
+ #if MI_ENCODE_FREELIST
+ { 0, 0 },
+ #endif
+ 0, // used
+ 0, // xblock_size
+ NULL, // local_free
+ ATOMIC_VAR_INIT(0), // xthread_free
+ ATOMIC_VAR_INIT(0), // xheap
+ NULL, NULL
+};
+
+#define MI_PAGE_EMPTY() ((mi_page_t*)&_mi_page_empty)
+
+#if (MI_PADDING>0) && (MI_INTPTR_SIZE >= 8)
+#define MI_SMALL_PAGES_EMPTY { MI_INIT128(MI_PAGE_EMPTY), MI_PAGE_EMPTY(), MI_PAGE_EMPTY() }
+#elif (MI_PADDING>0)
+#define MI_SMALL_PAGES_EMPTY { MI_INIT128(MI_PAGE_EMPTY), MI_PAGE_EMPTY(), MI_PAGE_EMPTY(), MI_PAGE_EMPTY() }
+#else
+#define MI_SMALL_PAGES_EMPTY { MI_INIT128(MI_PAGE_EMPTY), MI_PAGE_EMPTY() }
+#endif
+
+
+// Empty page queues for every bin
+#define QNULL(sz) { NULL, NULL, (sz)*sizeof(uintptr_t) }
+#define MI_PAGE_QUEUES_EMPTY \
+ { QNULL(1), \
+ QNULL( 1), QNULL( 2), QNULL( 3), QNULL( 4), QNULL( 5), QNULL( 6), QNULL( 7), QNULL( 8), /* 8 */ \
+ QNULL( 10), QNULL( 12), QNULL( 14), QNULL( 16), QNULL( 20), QNULL( 24), QNULL( 28), QNULL( 32), /* 16 */ \
+ QNULL( 40), QNULL( 48), QNULL( 56), QNULL( 64), QNULL( 80), QNULL( 96), QNULL( 112), QNULL( 128), /* 24 */ \
+ QNULL( 160), QNULL( 192), QNULL( 224), QNULL( 256), QNULL( 320), QNULL( 384), QNULL( 448), QNULL( 512), /* 32 */ \
+ QNULL( 640), QNULL( 768), QNULL( 896), QNULL( 1024), QNULL( 1280), QNULL( 1536), QNULL( 1792), QNULL( 2048), /* 40 */ \
+ QNULL( 2560), QNULL( 3072), QNULL( 3584), QNULL( 4096), QNULL( 5120), QNULL( 6144), QNULL( 7168), QNULL( 8192), /* 48 */ \
+ QNULL( 10240), QNULL( 12288), QNULL( 14336), QNULL( 16384), QNULL( 20480), QNULL( 24576), QNULL( 28672), QNULL( 32768), /* 56 */ \
+ QNULL( 40960), QNULL( 49152), QNULL( 57344), QNULL( 65536), QNULL( 81920), QNULL( 98304), QNULL(114688), QNULL(131072), /* 64 */ \
+ QNULL(163840), QNULL(196608), QNULL(229376), QNULL(262144), QNULL(327680), QNULL(393216), QNULL(458752), QNULL(524288), /* 72 */ \
+ QNULL(MI_LARGE_OBJ_WSIZE_MAX + 1 /* 655360, Huge queue */), \
+ QNULL(MI_LARGE_OBJ_WSIZE_MAX + 2) /* Full queue */ }
+
+#define MI_STAT_COUNT_NULL() {0,0,0,0}
+
+// Empty statistics
+#if MI_STAT>1
+#define MI_STAT_COUNT_END_NULL() , { MI_STAT_COUNT_NULL(), MI_INIT32(MI_STAT_COUNT_NULL) }
+#else
+#define MI_STAT_COUNT_END_NULL()
+#endif
+
+#define MI_STATS_NULL \
+ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
+ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
+ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
+ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
+ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
+ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
+ MI_STAT_COUNT_NULL(), \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } \
+ MI_STAT_COUNT_END_NULL()
+
+// --------------------------------------------------------
+// Statically allocate an empty heap as the initial
+// thread local value for the default heap,
+// and statically allocate the backing heap for the main
+// thread so it can function without doing any allocation
+// itself (as accessing a thread local for the first time
+// may lead to allocation itself on some platforms)
+// --------------------------------------------------------
+
+const mi_heap_t _mi_heap_empty = {
+ NULL,
+ MI_SMALL_PAGES_EMPTY,
+ MI_PAGE_QUEUES_EMPTY,
+ ATOMIC_VAR_INIT(NULL),
+ 0, // tid
+ 0, // cookie
+ { 0, 0 }, // keys
+ { {0}, {0}, 0 },
+ 0, // page count
+ MI_BIN_FULL, 0, // page retired min/max
+ NULL, // next
+ false
+};
+
+// the thread-local default heap for allocation
+mi_decl_thread mi_heap_t* _mi_heap_default = (mi_heap_t*)&_mi_heap_empty;
+
+extern mi_heap_t _mi_heap_main;
+
+static mi_tld_t tld_main = {
+ 0, false,
+ &_mi_heap_main, &_mi_heap_main,
+ { { NULL, NULL }, {NULL ,NULL}, {NULL ,NULL, 0},
+ 0, 0, 0, 0, 0, 0, NULL,
+ &tld_main.stats, &tld_main.os
+ }, // segments
+ { 0, &tld_main.stats }, // os
+ { MI_STATS_NULL } // stats
+};
+
+mi_heap_t _mi_heap_main = {
+ &tld_main,
+ MI_SMALL_PAGES_EMPTY,
+ MI_PAGE_QUEUES_EMPTY,
+ ATOMIC_VAR_INIT(NULL),
+ 0, // thread id
+ 0, // initial cookie
+ { 0, 0 }, // the key of the main heap can be fixed (unlike page keys that need to be secure!)
+ { {0x846ca68b}, {0}, 0 }, // random
+ 0, // page count
+ MI_BIN_FULL, 0, // page retired min/max
+ NULL, // next heap
+ false // can reclaim
+};
+
+bool _mi_process_is_initialized = false; // set to `true` in `mi_process_init`.
+
+mi_stats_t _mi_stats_main = { MI_STATS_NULL };
+
+
+static void mi_heap_main_init(void) {
+ if (_mi_heap_main.cookie == 0) {
+ _mi_heap_main.thread_id = _mi_thread_id();
+ _mi_heap_main.cookie = _os_random_weak((uintptr_t)&mi_heap_main_init);
+ _mi_random_init(&_mi_heap_main.random);
+ _mi_heap_main.keys[0] = _mi_heap_random_next(&_mi_heap_main);
+ _mi_heap_main.keys[1] = _mi_heap_random_next(&_mi_heap_main);
+ }
+}
+
+mi_heap_t* _mi_heap_main_get(void) {
+ mi_heap_main_init();
+ return &_mi_heap_main;
+}
+
+
+/* -----------------------------------------------------------
+ Initialization and freeing of the thread local heaps
+----------------------------------------------------------- */
+
+// note: in x64 in release build `sizeof(mi_thread_data_t)` is under 4KiB (= OS page size).
+typedef struct mi_thread_data_s {
+ mi_heap_t heap; // must come first due to cast in `_mi_heap_done`
+ mi_tld_t tld;
+} mi_thread_data_t;
+
+// Initialize the thread local default heap, called from `mi_thread_init`
+static bool _mi_heap_init(void) {
+ if (mi_heap_is_initialized(mi_get_default_heap())) return true;
+ if (_mi_is_main_thread()) {
+ // mi_assert_internal(_mi_heap_main.thread_id != 0); // can happen on freeBSD where alloc is called before any initialization
+ // the main heap is statically allocated
+ mi_heap_main_init();
+ _mi_heap_set_default_direct(&_mi_heap_main);
+ //mi_assert_internal(_mi_heap_default->tld->heap_backing == mi_get_default_heap());
+ }
+ else {
+ // use `_mi_os_alloc` to allocate directly from the OS
+ mi_thread_data_t* td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &_mi_stats_main); // Todo: more efficient allocation?
+ if (td == NULL) {
+ // if this fails, try once more. (issue #257)
+ td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &_mi_stats_main);
+ if (td == NULL) {
+ // really out of memory
+ _mi_error_message(ENOMEM, "unable to allocate thread local heap metadata (%zu bytes)\n", sizeof(mi_thread_data_t));
+ return false;
+ }
+ }
+ // OS allocated so already zero initialized
+ mi_tld_t* tld = &td->tld;
+ mi_heap_t* heap = &td->heap;
+ memcpy(heap, &_mi_heap_empty, sizeof(*heap));
+ heap->thread_id = _mi_thread_id();
+ _mi_random_init(&heap->random);
+ heap->cookie = _mi_heap_random_next(heap) | 1;
+ heap->keys[0] = _mi_heap_random_next(heap);
+ heap->keys[1] = _mi_heap_random_next(heap);
+ heap->tld = tld;
+ tld->heap_backing = heap;
+ tld->heaps = heap;
+ tld->segments.stats = &tld->stats;
+ tld->segments.os = &tld->os;
+ tld->os.stats = &tld->stats;
+ _mi_heap_set_default_direct(heap);
+ }
+ return false;
+}
+
+// Free the thread local default heap (called from `mi_thread_done`)
+static bool _mi_heap_done(mi_heap_t* heap) {
+ if (!mi_heap_is_initialized(heap)) return true;
+
+ // reset default heap
+ _mi_heap_set_default_direct(_mi_is_main_thread() ? &_mi_heap_main : (mi_heap_t*)&_mi_heap_empty);
+
+ // switch to backing heap
+ heap = heap->tld->heap_backing;
+ if (!mi_heap_is_initialized(heap)) return false;
+
+ // delete all non-backing heaps in this thread
+ mi_heap_t* curr = heap->tld->heaps;
+ while (curr != NULL) {
+ mi_heap_t* next = curr->next; // save `next` as `curr` will be freed
+ if (curr != heap) {
+ mi_assert_internal(!mi_heap_is_backing(curr));
+ mi_heap_delete(curr);
+ }
+ curr = next;
+ }
+ mi_assert_internal(heap->tld->heaps == heap && heap->next == NULL);
+ mi_assert_internal(mi_heap_is_backing(heap));
+
+ // collect if not the main thread
+ if (heap != &_mi_heap_main) {
+ _mi_heap_collect_abandon(heap);
+ }
+
+
+ // merge stats
+ _mi_stats_done(&heap->tld->stats);
+
+ // free if not the main thread
+ if (heap != &_mi_heap_main) {
+ mi_assert_internal(heap->tld->segments.count == 0 || heap->thread_id != _mi_thread_id());
+ _mi_os_free(heap, sizeof(mi_thread_data_t), &_mi_stats_main);
+ }
+#if 0
+ // never free the main thread even in debug mode; if a dll is linked statically with mimalloc,
+ // there may still be delete/free calls after the mi_fls_done is called. Issue #207
+ else {
+ _mi_heap_destroy_pages(heap);
+ mi_assert_internal(heap->tld->heap_backing == &_mi_heap_main);
+ }
+#endif
+ return false;
+}
+
+
+
+// --------------------------------------------------------
+// Try to run `mi_thread_done()` automatically so any memory
+// owned by the thread but not yet released can be abandoned
+// and re-owned by another thread.
+//
+// 1. windows dynamic library:
+// call from DllMain on DLL_THREAD_DETACH
+// 2. windows static library:
+// use `FlsAlloc` to call a destructor when the thread is done
+// 3. unix, pthreads:
+// use a pthread key to call a destructor when a pthread is done
+//
+// In the last two cases we also need to call `mi_process_init`
+// to set up the thread local keys.
+// --------------------------------------------------------
+
+static void _mi_thread_done(mi_heap_t* default_heap);
+
+#ifdef __wasi__
+// no pthreads in the WebAssembly Standard Interface
+#elif !defined(_WIN32)
+#define MI_USE_PTHREADS
+#endif
+
+#if defined(_WIN32) && defined(MI_SHARED_LIB)
+ // nothing to do as it is done in DllMain
+#elif defined(_WIN32) && !defined(MI_SHARED_LIB)
+ // use thread local storage keys to detect thread ending
+ #include <windows.h>
+ #include <fibersapi.h>
+ #if (_WIN32_WINNT < 0x600) // before Windows Vista
+ WINBASEAPI DWORD WINAPI FlsAlloc( _In_opt_ PFLS_CALLBACK_FUNCTION lpCallback );
+ WINBASEAPI PVOID WINAPI FlsGetValue( _In_ DWORD dwFlsIndex );
+ WINBASEAPI BOOL WINAPI FlsSetValue( _In_ DWORD dwFlsIndex, _In_opt_ PVOID lpFlsData );
+ WINBASEAPI BOOL WINAPI FlsFree(_In_ DWORD dwFlsIndex);
+ #endif
+ static DWORD mi_fls_key = (DWORD)(-1);
+ static void NTAPI mi_fls_done(PVOID value) {
+ if (value!=NULL) _mi_thread_done((mi_heap_t*)value);
+ }
+#elif defined(MI_USE_PTHREADS)
+ // use pthread local storage keys to detect thread ending
+ // (and used with MI_TLS_PTHREADS for the default heap)
+ #include <pthread.h>
+ pthread_key_t _mi_heap_default_key = (pthread_key_t)(-1);
+ static void mi_pthread_done(void* value) {
+ if (value!=NULL) _mi_thread_done((mi_heap_t*)value);
+ }
+#elif defined(__wasi__)
+// no pthreads in the WebAssembly Standard Interface
+#else
+ #pragma message("define a way to call mi_thread_done when a thread is done")
+#endif
+
+// Set up handlers so `mi_thread_done` is called automatically
+static void mi_process_setup_auto_thread_done(void) {
+ static bool tls_initialized = false; // fine if it races
+ if (tls_initialized) return;
+ tls_initialized = true;
+ #if defined(_WIN32) && defined(MI_SHARED_LIB)
+ // nothing to do as it is done in DllMain
+ #elif defined(_WIN32) && !defined(MI_SHARED_LIB)
+ mi_fls_key = FlsAlloc(&mi_fls_done);
+ #elif defined(MI_USE_PTHREADS)
+ mi_assert_internal(_mi_heap_default_key == (pthread_key_t)(-1));
+ pthread_key_create(&_mi_heap_default_key, &mi_pthread_done);
+ #endif
+ _mi_heap_set_default_direct(&_mi_heap_main);
+}
+
+
+bool _mi_is_main_thread(void) {
+ return (_mi_heap_main.thread_id==0 || _mi_heap_main.thread_id == _mi_thread_id());
+}
+
+// This is called from the `mi_malloc_generic`
+void mi_thread_init(void) mi_attr_noexcept
+{
+ // ensure our process has started already
+ mi_process_init();
+
+ // initialize the thread local default heap
+ // (this will call `_mi_heap_set_default_direct` and thus set the
+ // fiber/pthread key to a non-zero value, ensuring `_mi_thread_done` is called)
+ if (_mi_heap_init()) return; // returns true if already initialized
+
+ // don't further initialize for the main thread
+ if (_mi_is_main_thread()) return;
+
+ mi_heap_t* const heap = mi_get_default_heap();
+ if (mi_heap_is_initialized(heap)) { _mi_stat_increase(&heap->tld->stats.threads, 1); }
+
+ //_mi_verbose_message("thread init: 0x%zx\n", _mi_thread_id());
+}
+
+void mi_thread_done(void) mi_attr_noexcept {
+ _mi_thread_done(mi_get_default_heap());
+}
+
+static void _mi_thread_done(mi_heap_t* heap) {
+ // check thread-id as on Windows shutdown with FLS the main (exit) thread may call this on thread-local heaps...
+ if (heap->thread_id != _mi_thread_id()) return;
+
+ // stats
+ if (!_mi_is_main_thread() && mi_heap_is_initialized(heap)) {
+ _mi_stat_decrease(&heap->tld->stats.threads, 1);
+ }
+
+ // abandon the thread local heap
+ if (_mi_heap_done(heap)) return; // returns true if already ran
+}
+
+void _mi_heap_set_default_direct(mi_heap_t* heap) {
+ mi_assert_internal(heap != NULL);
+ #if defined(MI_TLS_SLOT)
+ mi_tls_slot_set(MI_TLS_SLOT,heap);
+ #elif defined(MI_TLS_PTHREAD_SLOT_OFS)
+ *mi_tls_pthread_heap_slot() = heap;
+ #elif defined(MI_TLS_PTHREAD)
+ // we use _mi_heap_default_key
+ #else
+ _mi_heap_default = heap;
+ #endif
+
+ // ensure the default heap is passed to `_mi_thread_done`
+ // setting to a non-NULL value also ensures `mi_thread_done` is called.
+ #if defined(_WIN32) && defined(MI_SHARED_LIB)
+ // nothing to do as it is done in DllMain
+ #elif defined(_WIN32) && !defined(MI_SHARED_LIB)
+ mi_assert_internal(mi_fls_key != 0);
+ FlsSetValue(mi_fls_key, heap);
+ #elif defined(MI_USE_PTHREADS)
+ if (_mi_heap_default_key != (pthread_key_t)(-1)) { // can happen during recursive invocation on freeBSD
+ pthread_setspecific(_mi_heap_default_key, heap);
+ }
+ #endif
+}
+
+
+// --------------------------------------------------------
+// Run functions on process init/done, and thread init/done
+// --------------------------------------------------------
+static void mi_process_done(void);
+
+static bool os_preloading = true; // true until this module is initialized
+static bool mi_redirected = false; // true if malloc redirects to mi_malloc
+
+// Returns true if this module has not been initialized; Don't use C runtime routines until it returns false.
+bool _mi_preloading() {
+ return os_preloading;
+}
+
+bool mi_is_redirected() mi_attr_noexcept {
+ return mi_redirected;
+}
+
+// Communicate with the redirection module on Windows
+#if defined(_WIN32) && defined(MI_SHARED_LIB)
+#ifdef __cplusplus
+extern "C" {
+#endif
+mi_decl_export void _mi_redirect_entry(DWORD reason) {
+ // called on redirection; careful as this may be called before DllMain
+ if (reason == DLL_PROCESS_ATTACH) {
+ mi_redirected = true;
+ }
+ else if (reason == DLL_PROCESS_DETACH) {
+ mi_redirected = false;
+ }
+ else if (reason == DLL_THREAD_DETACH) {
+ mi_thread_done();
+ }
+}
+__declspec(dllimport) bool mi_allocator_init(const char** message);
+__declspec(dllimport) void mi_allocator_done();
+#ifdef __cplusplus
+}
+#endif
+#else
+static bool mi_allocator_init(const char** message) {
+ if (message != NULL) *message = NULL;
+ return true;
+}
+static void mi_allocator_done() {
+ // nothing to do
+}
+#endif
+
+// Called once by the process loader
+static void mi_process_load(void) {
+ mi_heap_main_init();
+ #if defined(MI_TLS_RECURSE_GUARD)
+ volatile mi_heap_t* dummy = _mi_heap_default; // access TLS to allocate it before setting tls_initialized to true;
+ UNUSED(dummy);
+ #endif
+ os_preloading = false;
+ atexit(&mi_process_done);
+ _mi_options_init();
+ mi_process_init();
+ //mi_stats_reset();-
+ if (mi_redirected) _mi_verbose_message("malloc is redirected.\n");
+
+ // show message from the redirector (if present)
+ const char* msg = NULL;
+ mi_allocator_init(&msg);
+ if (msg != NULL && (mi_option_is_enabled(mi_option_verbose) || mi_option_is_enabled(mi_option_show_errors))) {
+ _mi_fputs(NULL,NULL,NULL,msg);
+ }
+}
+
+// Initialize the process; called by thread_init or the process loader
+void mi_process_init(void) mi_attr_noexcept {
+ // ensure we are called once
+ if (_mi_process_is_initialized) return;
+ _mi_process_is_initialized = true;
+ mi_process_setup_auto_thread_done();
+
+ _mi_verbose_message("process init: 0x%zx\n", _mi_thread_id());
+ _mi_os_init();
+ mi_heap_main_init();
+ #if (MI_DEBUG)
+ _mi_verbose_message("debug level : %d\n", MI_DEBUG);
+ #endif
+ _mi_verbose_message("secure level: %d\n", MI_SECURE);
+ mi_thread_init();
+ mi_stats_reset(); // only call stat reset *after* thread init (or the heap tld == NULL)
+
+ if (mi_option_is_enabled(mi_option_reserve_huge_os_pages)) {
+ size_t pages = mi_option_get(mi_option_reserve_huge_os_pages);
+ mi_reserve_huge_os_pages_interleave(pages, 0, pages*500);
+ }
+}
+
+// Called when the process is done (through `at_exit`)
+static void mi_process_done(void) {
+ // only shutdown if we were initialized
+ if (!_mi_process_is_initialized) return;
+ // ensure we are called once
+ static bool process_done = false;
+ if (process_done) return;
+ process_done = true;
+
+ #if defined(_WIN32) && !defined(MI_SHARED_LIB)
+ FlsSetValue(mi_fls_key, NULL); // don't call main-thread callback
+ FlsFree(mi_fls_key); // call thread-done on all threads to prevent dangling callback pointer if statically linked with a DLL; Issue #208
+ #endif
+
+ #if (MI_DEBUG != 0) || !defined(MI_SHARED_LIB)
+ // free all memory if possible on process exit. This is not needed for a stand-alone process
+ // but should be done if mimalloc is statically linked into another shared library which
+ // is repeatedly loaded/unloaded, see issue #281.
+ mi_collect(true /* force */ );
+ #endif
+
+ if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) {
+ mi_stats_print(NULL);
+ }
+ mi_allocator_done();
+ _mi_verbose_message("process done: 0x%zx\n", _mi_heap_main.thread_id);
+ os_preloading = true; // don't call the C runtime anymore
+}
+
+
+
+#if defined(_WIN32) && defined(MI_SHARED_LIB)
+ // Windows DLL: easy to hook into process_init and thread_done
+ __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
+ UNUSED(reserved);
+ UNUSED(inst);
+ if (reason==DLL_PROCESS_ATTACH) {
+ mi_process_load();
+ }
+ else if (reason==DLL_THREAD_DETACH) {
+ if (!mi_is_redirected()) mi_thread_done();
+ }
+ return TRUE;
+ }
+
+#elif defined(__cplusplus)
+ // C++: use static initialization to detect process start
+ static bool _mi_process_init(void) {
+ mi_process_load();
+ return (_mi_heap_main.thread_id != 0);
+ }
+ static bool mi_initialized = _mi_process_init();
+
+#elif defined(__GNUC__) || defined(__clang__)
+ // GCC,Clang: use the constructor attribute
+ static void __attribute__((constructor)) _mi_process_init(void) {
+ mi_process_load();
+ }
+
+#elif defined(_MSC_VER)
+ // MSVC: use data section magic for static libraries
+ // See <https://www.codeguru.com/cpp/misc/misc/applicationcontrol/article.php/c6945/Running-Code-Before-and-After-Main.htm>
+ static int _mi_process_init(void) {
+ mi_process_load();
+ return 0;
+ }
+ typedef int(*_crt_cb)(void);
+ #ifdef _M_X64
+ __pragma(comment(linker, "/include:" "_mi_msvc_initu"))
+ #pragma section(".CRT$XIU", long, read)
+ #else
+ __pragma(comment(linker, "/include:" "__mi_msvc_initu"))
+ #endif
+ #pragma data_seg(".CRT$XIU")
+ _crt_cb _mi_msvc_initu[] = { &_mi_process_init };
+ #pragma data_seg()
+
+#else
+#pragma message("define a way to call mi_process_load on your platform")
+#endif
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+#include "mimalloc-atomic.h"
+
+#include <stdio.h>
+#include <stdlib.h> // strtol
+#include <string.h> // strncpy, strncat, strlen, strstr
+#include <ctype.h> // toupper
+#include <stdarg.h>
+
+static uintptr_t mi_max_error_count = 16; // stop outputting errors after this
+
+static void mi_add_stderr_output();
+
+int mi_version(void) mi_attr_noexcept {
+ return MI_MALLOC_VERSION;
+}
+
+#ifdef _WIN32
+#include <conio.h>
+#endif
+
+// --------------------------------------------------------
+// Options
+// These can be accessed by multiple threads and may be
+// concurrently initialized, but an initializing data race
+// is ok since they resolve to the same value.
+// --------------------------------------------------------
+typedef enum mi_init_e {
+ UNINIT, // not yet initialized
+ DEFAULTED, // not found in the environment, use default value
+ INITIALIZED // found in environment or set explicitly
+} mi_init_t;
+
+typedef struct mi_option_desc_s {
+ long value; // the value
+ mi_init_t init; // is it initialized yet? (from the environment)
+ mi_option_t option; // for debugging: the option index should match the option
+ const char* name; // option name without `mimalloc_` prefix
+} mi_option_desc_t;
+
+#define MI_OPTION(opt) mi_option_##opt, #opt
+#define MI_OPTION_DESC(opt) {0, UNINIT, MI_OPTION(opt) }
+
+static mi_option_desc_t options[_mi_option_last] =
+{
+ // stable options
+#if MI_DEBUG || defined(MI_SHOW_ERRORS)
+ { 1, UNINIT, MI_OPTION(show_errors) },
+#else
+ { 0, UNINIT, MI_OPTION(show_errors) },
+#endif
+ { 0, UNINIT, MI_OPTION(show_stats) },
+ { 0, UNINIT, MI_OPTION(verbose) },
+
+ // the following options are experimental and not all combinations make sense.
+ { 1, UNINIT, MI_OPTION(eager_commit) }, // commit on demand
+ #if defined(_WIN32) || (MI_INTPTR_SIZE <= 4) // and other OS's without overcommit?
+ { 0, UNINIT, MI_OPTION(eager_region_commit) },
+ { 1, UNINIT, MI_OPTION(reset_decommits) }, // reset decommits memory
+ #else
+ { 1, UNINIT, MI_OPTION(eager_region_commit) },
+ { 0, UNINIT, MI_OPTION(reset_decommits) }, // reset uses MADV_FREE/MADV_DONTNEED
+ #endif
+ { 0, UNINIT, MI_OPTION(large_os_pages) }, // use large OS pages, use only with eager commit to prevent fragmentation of VMA's
+ { 0, UNINIT, MI_OPTION(reserve_huge_os_pages) },
+ { 0, UNINIT, MI_OPTION(segment_cache) }, // cache N segments per thread
+ { 1, UNINIT, MI_OPTION(page_reset) }, // reset page memory on free
+ { 0, UNINIT, MI_OPTION(abandoned_page_reset) },// reset free page memory when a thread terminates
+ { 0, UNINIT, MI_OPTION(segment_reset) }, // reset segment memory on free (needs eager commit)
+#if defined(__NetBSD__)
+ { 0, UNINIT, MI_OPTION(eager_commit_delay) }, // the first N segments per thread are not eagerly committed
+#else
+ { 1, UNINIT, MI_OPTION(eager_commit_delay) }, // the first N segments per thread are not eagerly committed
+#endif
+ { 100, UNINIT, MI_OPTION(reset_delay) }, // reset delay in milli-seconds
+ { 0, UNINIT, MI_OPTION(use_numa_nodes) }, // 0 = use available numa nodes, otherwise use at most N nodes.
+ { 100, UNINIT, MI_OPTION(os_tag) }, // only apple specific for now but might serve more or less related purpose
+ { 16, UNINIT, MI_OPTION(max_errors) } // maximum errors that are output
+};
+
+static void mi_option_init(mi_option_desc_t* desc);
+
+void _mi_options_init(void) {
+ // called on process load; should not be called before the CRT is initialized!
+ // (e.g. do not call this from process_init as that may run before CRT initialization)
+ mi_add_stderr_output(); // now it safe to use stderr for output
+ for(int i = 0; i < _mi_option_last; i++ ) {
+ mi_option_t option = (mi_option_t)i;
+ long l = mi_option_get(option); UNUSED(l); // initialize
+ if (option != mi_option_verbose) {
+ mi_option_desc_t* desc = &options[option];
+ _mi_verbose_message("option '%s': %ld\n", desc->name, desc->value);
+ }
+ }
+ mi_max_error_count = mi_option_get(mi_option_max_errors);
+}
+
+long mi_option_get(mi_option_t option) {
+ mi_assert(option >= 0 && option < _mi_option_last);
+ mi_option_desc_t* desc = &options[option];
+ mi_assert(desc->option == option); // index should match the option
+ if (mi_unlikely(desc->init == UNINIT)) {
+ mi_option_init(desc);
+ }
+ return desc->value;
+}
+
+void mi_option_set(mi_option_t option, long value) {
+ mi_assert(option >= 0 && option < _mi_option_last);
+ mi_option_desc_t* desc = &options[option];
+ mi_assert(desc->option == option); // index should match the option
+ desc->value = value;
+ desc->init = INITIALIZED;
+}
+
+void mi_option_set_default(mi_option_t option, long value) {
+ mi_assert(option >= 0 && option < _mi_option_last);
+ mi_option_desc_t* desc = &options[option];
+ if (desc->init != INITIALIZED) {
+ desc->value = value;
+ }
+}
+
+bool mi_option_is_enabled(mi_option_t option) {
+ return (mi_option_get(option) != 0);
+}
+
+void mi_option_set_enabled(mi_option_t option, bool enable) {
+ mi_option_set(option, (enable ? 1 : 0));
+}
+
+void mi_option_set_enabled_default(mi_option_t option, bool enable) {
+ mi_option_set_default(option, (enable ? 1 : 0));
+}
+
+void mi_option_enable(mi_option_t option) {
+ mi_option_set_enabled(option,true);
+}
+
+void mi_option_disable(mi_option_t option) {
+ mi_option_set_enabled(option,false);
+}
+
+
+static void mi_out_stderr(const char* msg, void* arg) {
+ UNUSED(arg);
+ #ifdef _WIN32
+ // on windows with redirection, the C runtime cannot handle locale dependent output
+ // after the main thread closes so we use direct console output.
+ if (!_mi_preloading()) { _cputs(msg); }
+ #else
+ fputs(msg, stderr);
+ #endif
+}
+
+// Since an output function can be registered earliest in the `main`
+// function we also buffer output that happens earlier. When
+// an output function is registered it is called immediately with
+// the output up to that point.
+#ifndef MI_MAX_DELAY_OUTPUT
+#define MI_MAX_DELAY_OUTPUT (32*1024)
+#endif
+static char out_buf[MI_MAX_DELAY_OUTPUT+1];
+static _Atomic(uintptr_t) out_len;
+
+static void mi_out_buf(const char* msg, void* arg) {
+ UNUSED(arg);
+ if (msg==NULL) return;
+ if (mi_atomic_read_relaxed(&out_len)>=MI_MAX_DELAY_OUTPUT) return;
+ size_t n = strlen(msg);
+ if (n==0) return;
+ // claim space
+ uintptr_t start = mi_atomic_add(&out_len, n);
+ if (start >= MI_MAX_DELAY_OUTPUT) return;
+ // check bound
+ if (start+n >= MI_MAX_DELAY_OUTPUT) {
+ n = MI_MAX_DELAY_OUTPUT-start-1;
+ }
+ memcpy(&out_buf[start], msg, n);
+}
+
+static void mi_out_buf_flush(mi_output_fun* out, bool no_more_buf, void* arg) {
+ if (out==NULL) return;
+ // claim (if `no_more_buf == true`, no more output will be added after this point)
+ size_t count = mi_atomic_add(&out_len, (no_more_buf ? MI_MAX_DELAY_OUTPUT : 1));
+ // and output the current contents
+ if (count>MI_MAX_DELAY_OUTPUT) count = MI_MAX_DELAY_OUTPUT;
+ out_buf[count] = 0;
+ out(out_buf,arg);
+ if (!no_more_buf) {
+ out_buf[count] = '\n'; // if continue with the buffer, insert a newline
+ }
+}
+
+
+// Once this module is loaded, switch to this routine
+// which outputs to stderr and the delayed output buffer.
+static void mi_out_buf_stderr(const char* msg, void* arg) {
+ mi_out_stderr(msg,arg);
+ mi_out_buf(msg,arg);
+}
+
+
+
+// --------------------------------------------------------
+// Default output handler
+// --------------------------------------------------------
+
+// Should be atomic but gives errors on many platforms as generally we cannot cast a function pointer to a uintptr_t.
+// For now, don't register output from multiple threads.
+#pragma warning(suppress:4180)
+static mi_output_fun* volatile mi_out_default; // = NULL
+static volatile _Atomic(void*) mi_out_arg; // = NULL
+
+static mi_output_fun* mi_out_get_default(void** parg) {
+ if (parg != NULL) { *parg = mi_atomic_read_ptr(void,&mi_out_arg); }
+ mi_output_fun* out = mi_out_default;
+ return (out == NULL ? &mi_out_buf : out);
+}
+
+void mi_register_output(mi_output_fun* out, void* arg) mi_attr_noexcept {
+ mi_out_default = (out == NULL ? &mi_out_stderr : out); // stop using the delayed output buffer
+ mi_atomic_write_ptr(void,&mi_out_arg, arg);
+ if (out!=NULL) mi_out_buf_flush(out,true,arg); // output all the delayed output now
+}
+
+// add stderr to the delayed output after the module is loaded
+static void mi_add_stderr_output() {
+ mi_assert_internal(mi_out_default == NULL);
+ mi_out_buf_flush(&mi_out_stderr, false, NULL); // flush current contents to stderr
+ mi_out_default = &mi_out_buf_stderr; // and add stderr to the delayed output
+}
+
+// --------------------------------------------------------
+// Messages, all end up calling `_mi_fputs`.
+// --------------------------------------------------------
+static volatile _Atomic(uintptr_t) error_count; // = 0; // when MAX_ERROR_COUNT stop emitting errors and warnings
+
+// When overriding malloc, we may recurse into mi_vfprintf if an allocation
+// inside the C runtime causes another message.
+static mi_decl_thread bool recurse = false;
+
+static bool mi_recurse_enter(void) {
+ #ifdef MI_TLS_RECURSE_GUARD
+ if (_mi_preloading()) return true;
+ #endif
+ if (recurse) return false;
+ recurse = true;
+ return true;
+}
+
+static void mi_recurse_exit(void) {
+ #ifdef MI_TLS_RECURSE_GUARD
+ if (_mi_preloading()) return;
+ #endif
+ recurse = false;
+}
+
+void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message) {
+ if (out==NULL || (FILE*)out==stdout || (FILE*)out==stderr) { // TODO: use mi_out_stderr for stderr?
+ if (!mi_recurse_enter()) return;
+ out = mi_out_get_default(&arg);
+ if (prefix != NULL) out(prefix, arg);
+ out(message, arg);
+ mi_recurse_exit();
+ }
+ else {
+ if (prefix != NULL) out(prefix, arg);
+ out(message, arg);
+ }
+}
+
+// Define our own limited `fprintf` that avoids memory allocation.
+// We do this using `snprintf` with a limited buffer.
+static void mi_vfprintf( mi_output_fun* out, void* arg, const char* prefix, const char* fmt, va_list args ) {
+ char buf[512];
+ if (fmt==NULL) return;
+ if (!mi_recurse_enter()) return;
+ vsnprintf(buf,sizeof(buf)-1,fmt,args);
+ mi_recurse_exit();
+ _mi_fputs(out,arg,prefix,buf);
+}
+
+void _mi_fprintf( mi_output_fun* out, void* arg, const char* fmt, ... ) {
+ va_list args;
+ va_start(args,fmt);
+ mi_vfprintf(out,arg,NULL,fmt,args);
+ va_end(args);
+}
+
+void _mi_trace_message(const char* fmt, ...) {
+ if (mi_option_get(mi_option_verbose) <= 1) return; // only with verbose level 2 or higher
+ va_list args;
+ va_start(args, fmt);
+ mi_vfprintf(NULL, NULL, "mimalloc: ", fmt, args);
+ va_end(args);
+}
+
+void _mi_verbose_message(const char* fmt, ...) {
+ if (!mi_option_is_enabled(mi_option_verbose)) return;
+ va_list args;
+ va_start(args,fmt);
+ mi_vfprintf(NULL, NULL, "mimalloc: ", fmt, args);
+ va_end(args);
+}
+
+static void mi_show_error_message(const char* fmt, va_list args) {
+ if (!mi_option_is_enabled(mi_option_show_errors) && !mi_option_is_enabled(mi_option_verbose)) return;
+ if (mi_atomic_increment(&error_count) > mi_max_error_count) return;
+ mi_vfprintf(NULL, NULL, "mimalloc: error: ", fmt, args);
+}
+
+void _mi_warning_message(const char* fmt, ...) {
+ if (!mi_option_is_enabled(mi_option_show_errors) && !mi_option_is_enabled(mi_option_verbose)) return;
+ if (mi_atomic_increment(&error_count) > mi_max_error_count) return;
+ va_list args;
+ va_start(args,fmt);
+ mi_vfprintf(NULL, NULL, "mimalloc: warning: ", fmt, args);
+ va_end(args);
+}
+
+
+#if MI_DEBUG
+void _mi_assert_fail(const char* assertion, const char* fname, unsigned line, const char* func ) {
+ _mi_fprintf(NULL, NULL, "mimalloc: assertion failed: at \"%s\":%u, %s\n assertion: \"%s\"\n", fname, line, (func==NULL?"":func), assertion);
+ abort();
+}
+#endif
+
+// --------------------------------------------------------
+// Errors
+// --------------------------------------------------------
+
+static mi_error_fun* volatile mi_error_handler; // = NULL
+static volatile _Atomic(void*) mi_error_arg; // = NULL
+
+static void mi_error_default(int err) {
+ UNUSED(err);
+#if (MI_DEBUG>0)
+ if (err==EFAULT) {
+ #ifdef _MSC_VER
+ __debugbreak();
+ #endif
+ abort();
+ }
+#endif
+#if (MI_SECURE>0)
+ if (err==EFAULT) { // abort on serious errors in secure mode (corrupted meta-data)
+ abort();
+ }
+#endif
+#if defined(MI_XMALLOC)
+ if (err==ENOMEM || err==EOVERFLOW) { // abort on memory allocation fails in xmalloc mode
+ abort();
+ }
+#endif
+}
+
+void mi_register_error(mi_error_fun* fun, void* arg) {
+ mi_error_handler = fun; // can be NULL
+ mi_atomic_write_ptr(void,&mi_error_arg, arg);
+}
+
+void _mi_error_message(int err, const char* fmt, ...) {
+ // show detailed error message
+ va_list args;
+ va_start(args, fmt);
+ mi_show_error_message(fmt, args);
+ va_end(args);
+ // and call the error handler which may abort (or return normally)
+ if (mi_error_handler != NULL) {
+ mi_error_handler(err, mi_atomic_read_ptr(void,&mi_error_arg));
+ }
+ else {
+ mi_error_default(err);
+ }
+}
+
+// --------------------------------------------------------
+// Initialize options by checking the environment
+// --------------------------------------------------------
+
+static void mi_strlcpy(char* dest, const char* src, size_t dest_size) {
+ dest[0] = 0;
+ #pragma warning(suppress:4996)
+ strncpy(dest, src, dest_size - 1);
+ dest[dest_size - 1] = 0;
+}
+
+static void mi_strlcat(char* dest, const char* src, size_t dest_size) {
+ #pragma warning(suppress:4996)
+ strncat(dest, src, dest_size - 1);
+ dest[dest_size - 1] = 0;
+}
+
+static inline int mi_strnicmp(const char* s, const char* t, size_t n) {
+ if (n==0) return 0;
+ for (; *s != 0 && *t != 0 && n > 0; s++, t++, n--) {
+ if (toupper(*s) != toupper(*t)) break;
+ }
+ return (n==0 ? 0 : *s - *t);
+}
+
+#if defined _WIN32
+// On Windows use GetEnvironmentVariable instead of getenv to work
+// reliably even when this is invoked before the C runtime is initialized.
+// i.e. when `_mi_preloading() == true`.
+// Note: on windows, environment names are not case sensitive.
+#include <windows.h>
+static bool mi_getenv(const char* name, char* result, size_t result_size) {
+ result[0] = 0;
+ size_t len = GetEnvironmentVariableA(name, result, (DWORD)result_size);
+ return (len > 0 && len < result_size);
+}
+#elif !defined(MI_USE_ENVIRON) || (MI_USE_ENVIRON!=0)
+// On Posix systemsr use `environ` to acces environment variables
+// even before the C runtime is initialized.
+#if defined(__APPLE__)
+#include <crt_externs.h>
+static char** mi_get_environ(void) {
+ return (*_NSGetEnviron());
+}
+#else
+extern char** environ;
+static char** mi_get_environ(void) {
+ return environ;
+}
+#endif
+static bool mi_getenv(const char* name, char* result, size_t result_size) {
+ if (name==NULL) return false;
+ const size_t len = strlen(name);
+ if (len == 0) return false;
+ char** env = mi_get_environ();
+ if (env == NULL) return false;
+ // compare up to 256 entries
+ for (int i = 0; i < 256 && env[i] != NULL; i++) {
+ const char* s = env[i];
+ if (mi_strnicmp(name, s, len) == 0 && s[len] == '=') { // case insensitive
+ // found it
+ mi_strlcpy(result, s + len + 1, result_size);
+ return true;
+ }
+ }
+ return false;
+}
+#else
+// fallback: use standard C `getenv` but this cannot be used while initializing the C runtime
+static bool mi_getenv(const char* name, char* result, size_t result_size) {
+ // cannot call getenv() when still initializing the C runtime.
+ if (_mi_preloading()) return false;
+ const char* s = getenv(name);
+ if (s == NULL) {
+ // we check the upper case name too.
+ char buf[64+1];
+ size_t len = strlen(name);
+ if (len >= sizeof(buf)) len = sizeof(buf) - 1;
+ for (size_t i = 0; i < len; i++) {
+ buf[i] = toupper(name[i]);
+ }
+ buf[len] = 0;
+ s = getenv(buf);
+ }
+ if (s != NULL && strlen(s) < result_size) {
+ mi_strlcpy(result, s, result_size);
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+#endif
+
+static void mi_option_init(mi_option_desc_t* desc) {
+ // Read option value from the environment
+ char buf[64+1];
+ mi_strlcpy(buf, "mimalloc_", sizeof(buf));
+ mi_strlcat(buf, desc->name, sizeof(buf));
+ char s[64+1];
+ if (mi_getenv(buf, s, sizeof(s))) {
+ size_t len = strlen(s);
+ if (len >= sizeof(buf)) len = sizeof(buf) - 1;
+ for (size_t i = 0; i < len; i++) {
+ buf[i] = (char)toupper(s[i]);
+ }
+ buf[len] = 0;
+ if (buf[0]==0 || strstr("1;TRUE;YES;ON", buf) != NULL) {
+ desc->value = 1;
+ desc->init = INITIALIZED;
+ }
+ else if (strstr("0;FALSE;NO;OFF", buf) != NULL) {
+ desc->value = 0;
+ desc->init = INITIALIZED;
+ }
+ else {
+ char* end = buf;
+ long value = strtol(buf, &end, 10);
+ if (*end == 0) {
+ desc->value = value;
+ desc->init = INITIALIZED;
+ }
+ else {
+ _mi_warning_message("environment option mimalloc_%s has an invalid value: %s\n", desc->name, buf);
+ desc->init = DEFAULTED;
+ }
+ }
+ mi_assert_internal(desc->init != UNINIT);
+ }
+ else if (!_mi_preloading()) {
+ desc->init = DEFAULTED;
+ }
+}
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE // ensure mmap flags are defined
+#endif
+
+#if defined(__sun)
+// illumos provides new mman.h api when any of these are defined
+// otherwise the old api based on caddr_t which predates the void pointers one.
+// stock solaris provides only the former, chose to atomically to discard those
+// flags only here rather than project wide tough.
+#undef _XOPEN_SOURCE
+#undef _POSIX_C_SOURCE
+#endif
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+#include "mimalloc-atomic.h"
+
+#include <string.h> // strerror
+
+
+#if defined(_WIN32)
+#include <windows.h>
+#elif defined(__wasi__)
+// stdlib.h is all we need, and has already been included in mimalloc.h
+#else
+#include <sys/mman.h> // mmap
+#include <unistd.h> // sysconf
+#if defined(__linux__)
+#include <features.h>
+#if defined(__GLIBC__)
+#include <linux/mman.h> // linux mmap flags
+#else
+#include <sys/mman.h>
+#endif
+#endif
+#if defined(__APPLE__)
+#include <TargetConditionals.h>
+#if !TARGET_IOS_IPHONE && !TARGET_IOS_SIMULATOR
+#include <mach/vm_statistics.h>
+#endif
+#endif
+#if defined(__HAIKU__)
+#define madvise posix_madvise
+#define MADV_DONTNEED POSIX_MADV_DONTNEED
+#endif
+#endif
+
+/* -----------------------------------------------------------
+ Initialization.
+ On windows initializes support for aligned allocation and
+ large OS pages (if MIMALLOC_LARGE_OS_PAGES is true).
+----------------------------------------------------------- */
+bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* stats);
+
+static void* mi_align_up_ptr(void* p, size_t alignment) {
+ return (void*)_mi_align_up((uintptr_t)p, alignment);
+}
+
+static uintptr_t _mi_align_down(uintptr_t sz, size_t alignment) {
+ return (sz / alignment) * alignment;
+}
+
+static void* mi_align_down_ptr(void* p, size_t alignment) {
+ return (void*)_mi_align_down((uintptr_t)p, alignment);
+}
+
+// page size (initialized properly in `os_init`)
+static size_t os_page_size = 4096;
+
+// minimal allocation granularity
+static size_t os_alloc_granularity = 4096;
+
+// if non-zero, use large page allocation
+static size_t large_os_page_size = 0;
+
+// OS (small) page size
+size_t _mi_os_page_size() {
+ return os_page_size;
+}
+
+// if large OS pages are supported (2 or 4MiB), then return the size, otherwise return the small page size (4KiB)
+size_t _mi_os_large_page_size() {
+ return (large_os_page_size != 0 ? large_os_page_size : _mi_os_page_size());
+}
+
+static bool use_large_os_page(size_t size, size_t alignment) {
+ // if we have access, check the size and alignment requirements
+ if (large_os_page_size == 0 || !mi_option_is_enabled(mi_option_large_os_pages)) return false;
+ return ((size % large_os_page_size) == 0 && (alignment % large_os_page_size) == 0);
+}
+
+// round to a good OS allocation size (bounded by max 12.5% waste)
+size_t _mi_os_good_alloc_size(size_t size) {
+ size_t align_size;
+ if (size < 512*KiB) align_size = _mi_os_page_size();
+ else if (size < 2*MiB) align_size = 64*KiB;
+ else if (size < 8*MiB) align_size = 256*KiB;
+ else if (size < 32*MiB) align_size = 1*MiB;
+ else align_size = 4*MiB;
+ if (size >= (SIZE_MAX - align_size)) return size; // possible overflow?
+ return _mi_align_up(size, align_size);
+}
+
+#if defined(_WIN32)
+// We use VirtualAlloc2 for aligned allocation, but it is only supported on Windows 10 and Windows Server 2016.
+// So, we need to look it up dynamically to run on older systems. (use __stdcall for 32-bit compatibility)
+// NtAllocateVirtualAllocEx is used for huge OS page allocation (1GiB)
+//
+// We hide MEM_EXTENDED_PARAMETER to compile with older SDK's.
+#include <winternl.h>
+typedef PVOID (__stdcall *PVirtualAlloc2)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, /* MEM_EXTENDED_PARAMETER* */ void*, ULONG);
+typedef NTSTATUS (__stdcall *PNtAllocateVirtualMemoryEx)(HANDLE, PVOID*, SIZE_T*, ULONG, ULONG, /* MEM_EXTENDED_PARAMETER* */ PVOID, ULONG);
+static PVirtualAlloc2 pVirtualAlloc2 = NULL;
+static PNtAllocateVirtualMemoryEx pNtAllocateVirtualMemoryEx = NULL;
+
+// Similarly, GetNumaProcesorNodeEx is only supported since Windows 7
+#if (_WIN32_WINNT < 0x601) // before Win7
+typedef struct _PROCESSOR_NUMBER { WORD Group; BYTE Number; BYTE Reserved; } PROCESSOR_NUMBER, *PPROCESSOR_NUMBER;
+#endif
+typedef VOID (__stdcall *PGetCurrentProcessorNumberEx)(PPROCESSOR_NUMBER ProcNumber);
+typedef BOOL (__stdcall *PGetNumaProcessorNodeEx)(PPROCESSOR_NUMBER Processor, PUSHORT NodeNumber);
+typedef BOOL (__stdcall* PGetNumaNodeProcessorMaskEx)(USHORT Node, PGROUP_AFFINITY ProcessorMask);
+static PGetCurrentProcessorNumberEx pGetCurrentProcessorNumberEx = NULL;
+static PGetNumaProcessorNodeEx pGetNumaProcessorNodeEx = NULL;
+static PGetNumaNodeProcessorMaskEx pGetNumaNodeProcessorMaskEx = NULL;
+
+static bool mi_win_enable_large_os_pages()
+{
+ if (large_os_page_size > 0) return true;
+
+ // Try to see if large OS pages are supported
+ // To use large pages on Windows, we first need access permission
+ // Set "Lock pages in memory" permission in the group policy editor
+ // <https://devblogs.microsoft.com/oldnewthing/20110128-00/?p=11643>
+ unsigned long err = 0;
+ HANDLE token = NULL;
+ BOOL ok = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token);
+ if (ok) {
+ TOKEN_PRIVILEGES tp;
+ ok = LookupPrivilegeValue(NULL, TEXT("SeLockMemoryPrivilege"), &tp.Privileges[0].Luid);
+ if (ok) {
+ tp.PrivilegeCount = 1;
+ tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ ok = AdjustTokenPrivileges(token, FALSE, &tp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
+ if (ok) {
+ err = GetLastError();
+ ok = (err == ERROR_SUCCESS);
+ if (ok) {
+ large_os_page_size = GetLargePageMinimum();
+ }
+ }
+ }
+ CloseHandle(token);
+ }
+ if (!ok) {
+ if (err == 0) err = GetLastError();
+ _mi_warning_message("cannot enable large OS page support, error %lu\n", err);
+ }
+ return (ok!=0);
+}
+
+void _mi_os_init(void) {
+ // get the page size
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ if (si.dwPageSize > 0) os_page_size = si.dwPageSize;
+ if (si.dwAllocationGranularity > 0) os_alloc_granularity = si.dwAllocationGranularity;
+ // get the VirtualAlloc2 function
+ HINSTANCE hDll;
+ hDll = LoadLibrary(TEXT("kernelbase.dll"));
+ if (hDll != NULL) {
+ // use VirtualAlloc2FromApp if possible as it is available to Windows store apps
+ pVirtualAlloc2 = (PVirtualAlloc2)(void (*)(void))GetProcAddress(hDll, "VirtualAlloc2FromApp");
+ if (pVirtualAlloc2==NULL) pVirtualAlloc2 = (PVirtualAlloc2)(void (*)(void))GetProcAddress(hDll, "VirtualAlloc2");
+ FreeLibrary(hDll);
+ }
+ // NtAllocateVirtualMemoryEx is used for huge page allocation
+ hDll = LoadLibrary(TEXT("ntdll.dll"));
+ if (hDll != NULL) {
+ pNtAllocateVirtualMemoryEx = (PNtAllocateVirtualMemoryEx)(void (*)(void))GetProcAddress(hDll, "NtAllocateVirtualMemoryEx");
+ FreeLibrary(hDll);
+ }
+ // Try to use Win7+ numa API
+ hDll = LoadLibrary(TEXT("kernel32.dll"));
+ if (hDll != NULL) {
+ pGetCurrentProcessorNumberEx = (PGetCurrentProcessorNumberEx)(void (*)(void))GetProcAddress(hDll, "GetCurrentProcessorNumberEx");
+ pGetNumaProcessorNodeEx = (PGetNumaProcessorNodeEx)(void (*)(void))GetProcAddress(hDll, "GetNumaProcessorNodeEx");
+ pGetNumaNodeProcessorMaskEx = (PGetNumaNodeProcessorMaskEx)(void (*)(void))GetProcAddress(hDll, "GetNumaNodeProcessorMaskEx");
+ FreeLibrary(hDll);
+ }
+ if (mi_option_is_enabled(mi_option_large_os_pages) || mi_option_is_enabled(mi_option_reserve_huge_os_pages)) {
+ mi_win_enable_large_os_pages();
+ }
+}
+#elif defined(__wasi__)
+void _mi_os_init() {
+ os_page_size = 0x10000; // WebAssembly has a fixed page size: 64KB
+ os_alloc_granularity = 16;
+}
+#else
+void _mi_os_init() {
+ // get the page size
+ long result = sysconf(_SC_PAGESIZE);
+ if (result > 0) {
+ os_page_size = (size_t)result;
+ os_alloc_granularity = os_page_size;
+ }
+ large_os_page_size = 2*MiB; // TODO: can we query the OS for this?
+}
+#endif
+
+
+/* -----------------------------------------------------------
+ Raw allocation on Windows (VirtualAlloc) and Unix's (mmap).
+----------------------------------------------------------- */
+
+static bool mi_os_mem_free(void* addr, size_t size, bool was_committed, mi_stats_t* stats)
+{
+ if (addr == NULL || size == 0) return true; // || _mi_os_is_huge_reserved(addr)
+ bool err = false;
+#if defined(_WIN32)
+ err = (VirtualFree(addr, 0, MEM_RELEASE) == 0);
+#elif defined(__wasi__)
+ err = 0; // WebAssembly's heap cannot be shrunk
+#else
+ err = (munmap(addr, size) == -1);
+#endif
+ if (was_committed) _mi_stat_decrease(&stats->committed, size);
+ _mi_stat_decrease(&stats->reserved, size);
+ if (err) {
+ #pragma warning(suppress:4996)
+ _mi_warning_message("munmap failed: %s, addr 0x%8li, size %lu\n", strerror(errno), (size_t)addr, size);
+ return false;
+ }
+ else {
+ return true;
+ }
+}
+
+static void* mi_os_get_aligned_hint(size_t try_alignment, size_t size);
+
+#ifdef _WIN32
+static void* mi_win_virtual_allocx(void* addr, size_t size, size_t try_alignment, DWORD flags) {
+#if (MI_INTPTR_SIZE >= 8)
+ // on 64-bit systems, try to use the virtual address area after 4TiB for 4MiB aligned allocations
+ void* hint;
+ if (addr == NULL && (hint = mi_os_get_aligned_hint(try_alignment,size)) != NULL) {
+ void* p = VirtualAlloc(hint, size, flags, PAGE_READWRITE);
+ if (p != NULL) return p;
+ DWORD err = GetLastError();
+ if (err != ERROR_INVALID_ADDRESS && // If linked with multiple instances, we may have tried to allocate at an already allocated area (#210)
+ err != ERROR_INVALID_PARAMETER) { // Windows7 instability (#230)
+ return NULL;
+ }
+ // fall through
+ }
+#endif
+#if defined(MEM_EXTENDED_PARAMETER_TYPE_BITS)
+ // on modern Windows try use VirtualAlloc2 for aligned allocation
+ if (try_alignment > 0 && (try_alignment % _mi_os_page_size()) == 0 && pVirtualAlloc2 != NULL) {
+ MEM_ADDRESS_REQUIREMENTS reqs = { 0, 0, 0 };
+ reqs.Alignment = try_alignment;
+ MEM_EXTENDED_PARAMETER param = { {0, 0}, {0} };
+ param.Type = MemExtendedParameterAddressRequirements;
+ param.Pointer = &reqs;
+ return (*pVirtualAlloc2)(GetCurrentProcess(), addr, size, flags, PAGE_READWRITE, ¶m, 1);
+ }
+#endif
+ // last resort
+ return VirtualAlloc(addr, size, flags, PAGE_READWRITE);
+}
+
+static void* mi_win_virtual_alloc(void* addr, size_t size, size_t try_alignment, DWORD flags, bool large_only, bool allow_large, bool* is_large) {
+ mi_assert_internal(!(large_only && !allow_large));
+ static volatile _Atomic(uintptr_t) large_page_try_ok; // = 0;
+ void* p = NULL;
+ if ((large_only || use_large_os_page(size, try_alignment))
+ && allow_large && (flags&MEM_COMMIT)!=0 && (flags&MEM_RESERVE)!=0) {
+ uintptr_t try_ok = mi_atomic_read(&large_page_try_ok);
+ if (!large_only && try_ok > 0) {
+ // if a large page allocation fails, it seems the calls to VirtualAlloc get very expensive.
+ // therefore, once a large page allocation failed, we don't try again for `large_page_try_ok` times.
+ mi_atomic_cas_weak(&large_page_try_ok, try_ok - 1, try_ok);
+ }
+ else {
+ // large OS pages must always reserve and commit.
+ *is_large = true;
+ p = mi_win_virtual_allocx(addr, size, try_alignment, flags | MEM_LARGE_PAGES);
+ if (large_only) return p;
+ // fall back to non-large page allocation on error (`p == NULL`).
+ if (p == NULL) {
+ mi_atomic_write(&large_page_try_ok,10); // on error, don't try again for the next N allocations
+ }
+ }
+ }
+ if (p == NULL) {
+ *is_large = ((flags&MEM_LARGE_PAGES) != 0);
+ p = mi_win_virtual_allocx(addr, size, try_alignment, flags);
+ }
+ if (p == NULL) {
+ _mi_warning_message("unable to allocate OS memory (%zu bytes, error code: %i, address: %p, large only: %d, allow large: %d)\n", size, GetLastError(), addr, large_only, allow_large);
+ }
+ return p;
+}
+
+#elif defined(__wasi__)
+static void* mi_wasm_heap_grow(size_t size, size_t try_alignment) {
+ uintptr_t base = __builtin_wasm_memory_size(0) * _mi_os_page_size();
+ uintptr_t aligned_base = _mi_align_up(base, (uintptr_t) try_alignment);
+ size_t alloc_size = _mi_align_up( aligned_base - base + size, _mi_os_page_size());
+ mi_assert(alloc_size >= size && (alloc_size % _mi_os_page_size()) == 0);
+ if (alloc_size < size) return NULL;
+ if (__builtin_wasm_memory_grow(0, alloc_size / _mi_os_page_size()) == SIZE_MAX) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return (void*)aligned_base;
+}
+#else
+#define MI_OS_USE_MMAP
+static void* mi_unix_mmapx(void* addr, size_t size, size_t try_alignment, int protect_flags, int flags, int fd) {
+ void* p = NULL;
+ #if (MI_INTPTR_SIZE >= 8) && !defined(MAP_ALIGNED)
+ // on 64-bit systems, use the virtual address area after 4TiB for 4MiB aligned allocations
+ void* hint;
+ if (addr == NULL && (hint = mi_os_get_aligned_hint(try_alignment, size)) != NULL) {
+ p = mmap(hint,size,protect_flags,flags,fd,0);
+ if (p==MAP_FAILED) p = NULL; // fall back to regular mmap
+ }
+ #else
+ UNUSED(try_alignment);
+ UNUSED(mi_os_get_aligned_hint);
+ #endif
+ if (p==NULL) {
+ p = mmap(addr,size,protect_flags,flags,fd,0);
+ if (p==MAP_FAILED) p = NULL;
+ }
+ return p;
+}
+
+static void* mi_unix_mmap(void* addr, size_t size, size_t try_alignment, int protect_flags, bool large_only, bool allow_large, bool* is_large) {
+ void* p = NULL;
+ #if !defined(MAP_ANONYMOUS)
+ #define MAP_ANONYMOUS MAP_ANON
+ #endif
+ #if !defined(MAP_NORESERVE)
+ #define MAP_NORESERVE 0
+ #endif
+ int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
+ int fd = -1;
+ #if defined(MAP_ALIGNED) // BSD
+ if (try_alignment > 0) {
+ size_t n = _mi_bsr(try_alignment);
+ if (((size_t)1 << n) == try_alignment && n >= 12 && n <= 30) { // alignment is a power of 2 and 4096 <= alignment <= 1GiB
+ flags |= MAP_ALIGNED(n);
+ }
+ }
+ #endif
+ #if defined(PROT_MAX)
+ protect_flags |= PROT_MAX(PROT_READ | PROT_WRITE); // BSD
+ #endif
+ #if defined(VM_MAKE_TAG)
+ // macOS: tracking anonymous page with a specific ID. (All up to 98 are taken officially but LLVM sanitizers had taken 99)
+ int os_tag = (int)mi_option_get(mi_option_os_tag);
+ if (os_tag < 100 || os_tag > 255) os_tag = 100;
+ fd = VM_MAKE_TAG(os_tag);
+ #endif
+ if ((large_only || use_large_os_page(size, try_alignment)) && allow_large) {
+ static volatile _Atomic(uintptr_t) large_page_try_ok; // = 0;
+ uintptr_t try_ok = mi_atomic_read(&large_page_try_ok);
+ if (!large_only && try_ok > 0) {
+ // If the OS is not configured for large OS pages, or the user does not have
+ // enough permission, the `mmap` will always fail (but it might also fail for other reasons).
+ // Therefore, once a large page allocation failed, we don't try again for `large_page_try_ok` times
+ // to avoid too many failing calls to mmap.
+ mi_atomic_cas_weak(&large_page_try_ok, try_ok - 1, try_ok);
+ }
+ else {
+ int lflags = flags & ~MAP_NORESERVE; // using NORESERVE on huge pages seems to fail on Linux
+ int lfd = fd;
+ #ifdef MAP_ALIGNED_SUPER
+ lflags |= MAP_ALIGNED_SUPER;
+ #endif
+ #ifdef MAP_HUGETLB
+ lflags |= MAP_HUGETLB;
+ #endif
+ #ifdef MAP_HUGE_1GB
+ static bool mi_huge_pages_available = true;
+ if ((size % GiB) == 0 && mi_huge_pages_available) {
+ lflags |= MAP_HUGE_1GB;
+ }
+ else
+ #endif
+ {
+ #ifdef MAP_HUGE_2MB
+ lflags |= MAP_HUGE_2MB;
+ #endif
+ }
+ #ifdef VM_FLAGS_SUPERPAGE_SIZE_2MB
+ lfd |= VM_FLAGS_SUPERPAGE_SIZE_2MB;
+ #endif
+ if (large_only || lflags != flags) {
+ // try large OS page allocation
+ *is_large = true;
+ p = mi_unix_mmapx(addr, size, try_alignment, protect_flags, lflags, lfd);
+ #ifdef MAP_HUGE_1GB
+ if (p == NULL && (lflags & MAP_HUGE_1GB) != 0) {
+ mi_huge_pages_available = false; // don't try huge 1GiB pages again
+ _mi_warning_message("unable to allocate huge (1GiB) page, trying large (2MiB) pages instead (error %i)\n", errno);
+ lflags = ((lflags & ~MAP_HUGE_1GB) | MAP_HUGE_2MB);
+ p = mi_unix_mmapx(addr, size, try_alignment, protect_flags, lflags, lfd);
+ }
+ #endif
+ if (large_only) return p;
+ if (p == NULL) {
+ mi_atomic_write(&large_page_try_ok, 10); // on error, don't try again for the next N allocations
+ }
+ }
+ }
+ }
+ if (p == NULL) {
+ *is_large = false;
+ p = mi_unix_mmapx(addr, size, try_alignment, protect_flags, flags, fd);
+ #if defined(MADV_HUGEPAGE)
+ // Many Linux systems don't allow MAP_HUGETLB but they support instead
+ // transparent huge pages (THP). It is not required to call `madvise` with MADV_HUGE
+ // though since properly aligned allocations will already use large pages if available
+ // in that case -- in particular for our large regions (in `memory.c`).
+ // However, some systems only allow THP if called with explicit `madvise`, so
+ // when large OS pages are enabled for mimalloc, we call `madvice` anyways.
+ if (allow_large && use_large_os_page(size, try_alignment)) {
+ if (madvise(p, size, MADV_HUGEPAGE) == 0) {
+ *is_large = true; // possibly
+ };
+ }
+ #endif
+ #if defined(__sun)
+ if (allow_large && use_large_os_page(size, try_alignment)) {
+ struct memcntl_mha cmd = {0};
+ cmd.mha_pagesize = large_os_page_size;
+ cmd.mha_cmd = MHA_MAPSIZE_VA;
+ if (memcntl(p, size, MC_HAT_ADVISE, (caddr_t)&cmd, 0, 0) == 0) {
+ *is_large = true;
+ }
+ }
+ #endif
+ }
+ if (p == NULL) {
+ _mi_warning_message("unable to allocate OS memory (%zu bytes, error code: %i, address: %p, large only: %d, allow large: %d)\n", size, errno, addr, large_only, allow_large);
+ }
+ return p;
+}
+#endif
+
+// On 64-bit systems, we can do efficient aligned allocation by using
+// the 4TiB to 30TiB area to allocate them.
+#if (MI_INTPTR_SIZE >= 8) && (defined(_WIN32) || (defined(MI_OS_USE_MMAP) && !defined(MAP_ALIGNED)))
+static volatile mi_decl_cache_align _Atomic(uintptr_t) aligned_base;
+
+// Return a 4MiB aligned address that is probably available
+static void* mi_os_get_aligned_hint(size_t try_alignment, size_t size) {
+ if (try_alignment == 0 || try_alignment > MI_SEGMENT_SIZE) return NULL;
+ if ((size%MI_SEGMENT_SIZE) != 0) return NULL;
+ uintptr_t hint = mi_atomic_add(&aligned_base, size);
+ if (hint == 0 || hint > ((intptr_t)30<<40)) { // try to wrap around after 30TiB (area after 32TiB is used for huge OS pages)
+ uintptr_t init = ((uintptr_t)4 << 40); // start at 4TiB area
+ #if (MI_SECURE>0 || MI_DEBUG==0) // security: randomize start of aligned allocations unless in debug mode
+ uintptr_t r = _mi_heap_random_next(mi_get_default_heap());
+ init = init + (MI_SEGMENT_SIZE * ((r>>17) & 0xFFFFF)); // (randomly 20 bits)*4MiB == 0 to 4TiB
+ #endif
+ mi_atomic_cas_strong(&aligned_base, init, hint + size);
+ hint = mi_atomic_add(&aligned_base, size); // this may still give 0 or > 30TiB but that is ok, it is a hint after all
+ }
+ if (hint%try_alignment != 0) return NULL;
+ return (void*)hint;
+}
+#else
+static void* mi_os_get_aligned_hint(size_t try_alignment, size_t size) {
+ UNUSED(try_alignment); UNUSED(size);
+ return NULL;
+}
+#endif
+
+
+// Primitive allocation from the OS.
+// Note: the `try_alignment` is just a hint and the returned pointer is not guaranteed to be aligned.
+static void* mi_os_mem_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, mi_stats_t* stats) {
+ mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0);
+ if (size == 0) return NULL;
+ if (!commit) allow_large = false;
+
+ void* p = NULL;
+ /*
+ if (commit && allow_large) {
+ p = _mi_os_try_alloc_from_huge_reserved(size, try_alignment);
+ if (p != NULL) {
+ *is_large = true;
+ return p;
+ }
+ }
+ */
+
+ #if defined(_WIN32)
+ int flags = MEM_RESERVE;
+ if (commit) flags |= MEM_COMMIT;
+ p = mi_win_virtual_alloc(NULL, size, try_alignment, flags, false, allow_large, is_large);
+ #elif defined(__wasi__)
+ *is_large = false;
+ p = mi_wasm_heap_grow(size, try_alignment);
+ #else
+ int protect_flags = (commit ? (PROT_WRITE | PROT_READ) : PROT_NONE);
+ p = mi_unix_mmap(NULL, size, try_alignment, protect_flags, false, allow_large, is_large);
+ #endif
+ mi_stat_counter_increase(stats->mmap_calls, 1);
+ if (p != NULL) {
+ _mi_stat_increase(&stats->reserved, size);
+ if (commit) { _mi_stat_increase(&stats->committed, size); }
+ }
+ return p;
+}
+
+
+// Primitive aligned allocation from the OS.
+// This function guarantees the allocated memory is aligned.
+static void* mi_os_mem_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, bool* is_large, mi_stats_t* stats) {
+ mi_assert_internal(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0));
+ mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0);
+ if (!commit) allow_large = false;
+ if (!(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0))) return NULL;
+ size = _mi_align_up(size, _mi_os_page_size());
+
+ // try first with a hint (this will be aligned directly on Win 10+ or BSD)
+ void* p = mi_os_mem_alloc(size, alignment, commit, allow_large, is_large, stats);
+ if (p == NULL) return NULL;
+
+ // if not aligned, free it, overallocate, and unmap around it
+ if (((uintptr_t)p % alignment != 0)) {
+ mi_os_mem_free(p, size, commit, stats);
+ if (size >= (SIZE_MAX - alignment)) return NULL; // overflow
+ size_t over_size = size + alignment;
+
+#if _WIN32
+ // over-allocate and than re-allocate exactly at an aligned address in there.
+ // this may fail due to threads allocating at the same time so we
+ // retry this at most 3 times before giving up.
+ // (we can not decommit around the overallocation on Windows, because we can only
+ // free the original pointer, not one pointing inside the area)
+ int flags = MEM_RESERVE;
+ if (commit) flags |= MEM_COMMIT;
+ for (int tries = 0; tries < 3; tries++) {
+ // over-allocate to determine a virtual memory range
+ p = mi_os_mem_alloc(over_size, alignment, commit, false, is_large, stats);
+ if (p == NULL) return NULL; // error
+ if (((uintptr_t)p % alignment) == 0) {
+ // if p happens to be aligned, just decommit the left-over area
+ _mi_os_decommit((uint8_t*)p + size, over_size - size, stats);
+ break;
+ }
+ else {
+ // otherwise free and allocate at an aligned address in there
+ mi_os_mem_free(p, over_size, commit, stats);
+ void* aligned_p = mi_align_up_ptr(p, alignment);
+ p = mi_win_virtual_alloc(aligned_p, size, alignment, flags, false, allow_large, is_large);
+ if (p == aligned_p) break; // success!
+ if (p != NULL) { // should not happen?
+ mi_os_mem_free(p, size, commit, stats);
+ p = NULL;
+ }
+ }
+ }
+#else
+ // overallocate...
+ p = mi_os_mem_alloc(over_size, alignment, commit, false, is_large, stats);
+ if (p == NULL) return NULL;
+ // and selectively unmap parts around the over-allocated area.
+ void* aligned_p = mi_align_up_ptr(p, alignment);
+ size_t pre_size = (uint8_t*)aligned_p - (uint8_t*)p;
+ size_t mid_size = _mi_align_up(size, _mi_os_page_size());
+ size_t post_size = over_size - pre_size - mid_size;
+ mi_assert_internal(pre_size < over_size && post_size < over_size && mid_size >= size);
+ if (pre_size > 0) mi_os_mem_free(p, pre_size, commit, stats);
+ if (post_size > 0) mi_os_mem_free((uint8_t*)aligned_p + mid_size, post_size, commit, stats);
+ // we can return the aligned pointer on `mmap` systems
+ p = aligned_p;
+#endif
+ }
+
+ mi_assert_internal(p == NULL || (p != NULL && ((uintptr_t)p % alignment) == 0));
+ return p;
+}
+
+/* -----------------------------------------------------------
+ OS API: alloc, free, alloc_aligned
+----------------------------------------------------------- */
+
+void* _mi_os_alloc(size_t size, mi_stats_t* stats) {
+ if (size == 0) return NULL;
+ size = _mi_os_good_alloc_size(size);
+ bool is_large = false;
+ return mi_os_mem_alloc(size, 0, true, false, &is_large, stats);
+}
+
+void _mi_os_free_ex(void* p, size_t size, bool was_committed, mi_stats_t* stats) {
+ if (size == 0 || p == NULL) return;
+ size = _mi_os_good_alloc_size(size);
+ mi_os_mem_free(p, size, was_committed, stats);
+}
+
+void _mi_os_free(void* p, size_t size, mi_stats_t* stats) {
+ _mi_os_free_ex(p, size, true, stats);
+}
+
+void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool* large, mi_os_tld_t* tld)
+{
+ if (size == 0) return NULL;
+ size = _mi_os_good_alloc_size(size);
+ alignment = _mi_align_up(alignment, _mi_os_page_size());
+ bool allow_large = false;
+ if (large != NULL) {
+ allow_large = *large;
+ *large = false;
+ }
+ return mi_os_mem_alloc_aligned(size, alignment, commit, allow_large, (large!=NULL?large:&allow_large), tld->stats);
+}
+
+
+
+/* -----------------------------------------------------------
+ OS memory API: reset, commit, decommit, protect, unprotect.
+----------------------------------------------------------- */
+
+
+// OS page align within a given area, either conservative (pages inside the area only),
+// or not (straddling pages outside the area is possible)
+static void* mi_os_page_align_areax(bool conservative, void* addr, size_t size, size_t* newsize) {
+ mi_assert(addr != NULL && size > 0);
+ if (newsize != NULL) *newsize = 0;
+ if (size == 0 || addr == NULL) return NULL;
+
+ // page align conservatively within the range
+ void* start = (conservative ? mi_align_up_ptr(addr, _mi_os_page_size())
+ : mi_align_down_ptr(addr, _mi_os_page_size()));
+ void* end = (conservative ? mi_align_down_ptr((uint8_t*)addr + size, _mi_os_page_size())
+ : mi_align_up_ptr((uint8_t*)addr + size, _mi_os_page_size()));
+ ptrdiff_t diff = (uint8_t*)end - (uint8_t*)start;
+ if (diff <= 0) return NULL;
+
+ mi_assert_internal((conservative && (size_t)diff <= size) || (!conservative && (size_t)diff >= size));
+ if (newsize != NULL) *newsize = (size_t)diff;
+ return start;
+}
+
+static void* mi_os_page_align_area_conservative(void* addr, size_t size, size_t* newsize) {
+ return mi_os_page_align_areax(true, addr, size, newsize);
+}
+
+static void mi_mprotect_hint(int err) {
+#if defined(MI_OS_USE_MMAP) && (MI_SECURE>=2) // guard page around every mimalloc page
+ if (err == ENOMEM) {
+ _mi_warning_message("the previous warning may have been caused by a low memory map limit.\n"
+ " On Linux this is controlled by the vm.max_map_count. For example:\n"
+ " > sudo sysctl -w vm.max_map_count=262144\n");
+ }
+#else
+ UNUSED(err);
+#endif
+}
+
+// Commit/Decommit memory.
+// Usually commit is aligned liberal, while decommit is aligned conservative.
+// (but not for the reset version where we want commit to be conservative as well)
+static bool mi_os_commitx(void* addr, size_t size, bool commit, bool conservative, bool* is_zero, mi_stats_t* stats) {
+ // page align in the range, commit liberally, decommit conservative
+ if (is_zero != NULL) { *is_zero = false; }
+ size_t csize;
+ void* start = mi_os_page_align_areax(conservative, addr, size, &csize);
+ if (csize == 0) return true; // || _mi_os_is_huge_reserved(addr))
+ int err = 0;
+ if (commit) {
+ _mi_stat_increase(&stats->committed, csize);
+ _mi_stat_counter_increase(&stats->commit_calls, 1);
+ }
+ else {
+ _mi_stat_decrease(&stats->committed, csize);
+ }
+
+ #if defined(_WIN32)
+ if (commit) {
+ // if the memory was already committed, the call succeeds but it is not zero'd
+ // *is_zero = true;
+ void* p = VirtualAlloc(start, csize, MEM_COMMIT, PAGE_READWRITE);
+ err = (p == start ? 0 : GetLastError());
+ }
+ else {
+ BOOL ok = VirtualFree(start, csize, MEM_DECOMMIT);
+ err = (ok ? 0 : GetLastError());
+ }
+ #elif defined(__wasi__)
+ // WebAssembly guests can't control memory protection
+ #elif defined(MAP_FIXED)
+ if (!commit) {
+ // use mmap with MAP_FIXED to discard the existing memory (and reduce commit charge)
+ void* p = mmap(start, csize, PROT_NONE, (MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE), -1, 0);
+ if (p != start) { err = errno; }
+ }
+ else {
+ // for commit, just change the protection
+ err = mprotect(start, csize, (PROT_READ | PROT_WRITE));
+ if (err != 0) { err = errno; }
+ }
+ #else
+ err = mprotect(start, csize, (commit ? (PROT_READ | PROT_WRITE) : PROT_NONE));
+ if (err != 0) { err = errno; }
+ #endif
+ if (err != 0) {
+ _mi_warning_message("%s error: start: %p, csize: 0x%x, err: %i\n", commit ? "commit" : "decommit", start, csize, err);
+ mi_mprotect_hint(err);
+ }
+ mi_assert_internal(err == 0);
+ return (err == 0);
+}
+
+bool _mi_os_commit(void* addr, size_t size, bool* is_zero, mi_stats_t* stats) {
+ return mi_os_commitx(addr, size, true, false /* liberal */, is_zero, stats);
+}
+
+bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* stats) {
+ bool is_zero;
+ return mi_os_commitx(addr, size, false, true /* conservative */, &is_zero, stats);
+}
+
+bool _mi_os_commit_unreset(void* addr, size_t size, bool* is_zero, mi_stats_t* stats) {
+ return mi_os_commitx(addr, size, true, true /* conservative */, is_zero, stats);
+}
+
+// Signal to the OS that the address range is no longer in use
+// but may be used later again. This will release physical memory
+// pages and reduce swapping while keeping the memory committed.
+// We page align to a conservative area inside the range to reset.
+static bool mi_os_resetx(void* addr, size_t size, bool reset, mi_stats_t* stats) {
+ // page align conservatively within the range
+ size_t csize;
+ void* start = mi_os_page_align_area_conservative(addr, size, &csize);
+ if (csize == 0) return true; // || _mi_os_is_huge_reserved(addr)
+ if (reset) _mi_stat_increase(&stats->reset, csize);
+ else _mi_stat_decrease(&stats->reset, csize);
+ if (!reset) return true; // nothing to do on unreset!
+
+ #if (MI_DEBUG>1)
+ if (MI_SECURE==0) {
+ memset(start, 0, csize); // pretend it is eagerly reset
+ }
+ #endif
+
+#if defined(_WIN32)
+ // Testing shows that for us (on `malloc-large`) MEM_RESET is 2x faster than DiscardVirtualMemory
+ void* p = VirtualAlloc(start, csize, MEM_RESET, PAGE_READWRITE);
+ mi_assert_internal(p == start);
+ #if 1
+ if (p == start && start != NULL) {
+ VirtualUnlock(start,csize); // VirtualUnlock after MEM_RESET removes the memory from the working set
+ }
+ #endif
+ if (p != start) return false;
+#else
+#if defined(MADV_FREE)
+ static int advice = MADV_FREE;
+ int err = madvise(start, csize, advice);
+ if (err != 0 && errno == EINVAL && advice == MADV_FREE) {
+ // if MADV_FREE is not supported, fall back to MADV_DONTNEED from now on
+ advice = MADV_DONTNEED;
+ err = madvise(start, csize, advice);
+ }
+#elif defined(__wasi__)
+ int err = 0;
+#else
+ int err = madvise(start, csize, MADV_DONTNEED);
+#endif
+ if (err != 0) {
+ _mi_warning_message("madvise reset error: start: %p, csize: 0x%x, errno: %i\n", start, csize, errno);
+ }
+ //mi_assert(err == 0);
+ if (err != 0) return false;
+#endif
+ return true;
+}
+
+// Signal to the OS that the address range is no longer in use
+// but may be used later again. This will release physical memory
+// pages and reduce swapping while keeping the memory committed.
+// We page align to a conservative area inside the range to reset.
+bool _mi_os_reset(void* addr, size_t size, mi_stats_t* stats) {
+ if (mi_option_is_enabled(mi_option_reset_decommits)) {
+ return _mi_os_decommit(addr, size, stats);
+ }
+ else {
+ return mi_os_resetx(addr, size, true, stats);
+ }
+}
+
+bool _mi_os_unreset(void* addr, size_t size, bool* is_zero, mi_stats_t* stats) {
+ if (mi_option_is_enabled(mi_option_reset_decommits)) {
+ return _mi_os_commit_unreset(addr, size, is_zero, stats); // re-commit it (conservatively!)
+ }
+ else {
+ *is_zero = false;
+ return mi_os_resetx(addr, size, false, stats);
+ }
+}
+
+
+// Protect a region in memory to be not accessible.
+static bool mi_os_protectx(void* addr, size_t size, bool protect) {
+ // page align conservatively within the range
+ size_t csize = 0;
+ void* start = mi_os_page_align_area_conservative(addr, size, &csize);
+ if (csize == 0) return false;
+ /*
+ if (_mi_os_is_huge_reserved(addr)) {
+ _mi_warning_message("cannot mprotect memory allocated in huge OS pages\n");
+ }
+ */
+ int err = 0;
+#ifdef _WIN32
+ DWORD oldprotect = 0;
+ BOOL ok = VirtualProtect(start, csize, protect ? PAGE_NOACCESS : PAGE_READWRITE, &oldprotect);
+ err = (ok ? 0 : GetLastError());
+#elif defined(__wasi__)
+ err = 0;
+#else
+ err = mprotect(start, csize, protect ? PROT_NONE : (PROT_READ | PROT_WRITE));
+ if (err != 0) { err = errno; }
+#endif
+ if (err != 0) {
+ _mi_warning_message("mprotect error: start: %p, csize: 0x%x, err: %i\n", start, csize, err);
+ mi_mprotect_hint(err);
+ }
+ return (err == 0);
+}
+
+bool _mi_os_protect(void* addr, size_t size) {
+ return mi_os_protectx(addr, size, true);
+}
+
+bool _mi_os_unprotect(void* addr, size_t size) {
+ return mi_os_protectx(addr, size, false);
+}
+
+
+
+bool _mi_os_shrink(void* p, size_t oldsize, size_t newsize, mi_stats_t* stats) {
+ // page align conservatively within the range
+ mi_assert_internal(oldsize > newsize && p != NULL);
+ if (oldsize < newsize || p == NULL) return false;
+ if (oldsize == newsize) return true;
+
+ // oldsize and newsize should be page aligned or we cannot shrink precisely
+ void* addr = (uint8_t*)p + newsize;
+ size_t size = 0;
+ void* start = mi_os_page_align_area_conservative(addr, oldsize - newsize, &size);
+ if (size == 0 || start != addr) return false;
+
+#ifdef _WIN32
+ // we cannot shrink on windows, but we can decommit
+ return _mi_os_decommit(start, size, stats);
+#else
+ return mi_os_mem_free(start, size, true, stats);
+#endif
+}
+
+
+/* ----------------------------------------------------------------------------
+Support for allocating huge OS pages (1Gib) that are reserved up-front
+and possibly associated with a specific NUMA node. (use `numa_node>=0`)
+-----------------------------------------------------------------------------*/
+#define MI_HUGE_OS_PAGE_SIZE (GiB)
+
+#if defined(_WIN32) && (MI_INTPTR_SIZE >= 8)
+static void* mi_os_alloc_huge_os_pagesx(void* addr, size_t size, int numa_node)
+{
+ mi_assert_internal(size%GiB == 0);
+ mi_assert_internal(addr != NULL);
+ const DWORD flags = MEM_LARGE_PAGES | MEM_COMMIT | MEM_RESERVE;
+
+ mi_win_enable_large_os_pages();
+
+ #if defined(MEM_EXTENDED_PARAMETER_TYPE_BITS)
+ MEM_EXTENDED_PARAMETER params[3] = { {{0,0},{0}},{{0,0},{0}},{{0,0},{0}} };
+ // on modern Windows try use NtAllocateVirtualMemoryEx for 1GiB huge pages
+ static bool mi_huge_pages_available = true;
+ if (pNtAllocateVirtualMemoryEx != NULL && mi_huge_pages_available) {
+ #ifndef MEM_EXTENDED_PARAMETER_NONPAGED_HUGE
+ #define MEM_EXTENDED_PARAMETER_NONPAGED_HUGE (0x10)
+ #endif
+ params[0].Type = 5; // == MemExtendedParameterAttributeFlags;
+ params[0].ULong64 = MEM_EXTENDED_PARAMETER_NONPAGED_HUGE;
+ ULONG param_count = 1;
+ if (numa_node >= 0) {
+ param_count++;
+ params[1].Type = MemExtendedParameterNumaNode;
+ params[1].ULong = (unsigned)numa_node;
+ }
+ SIZE_T psize = size;
+ void* base = addr;
+ NTSTATUS err = (*pNtAllocateVirtualMemoryEx)(GetCurrentProcess(), &base, &psize, flags, PAGE_READWRITE, params, param_count);
+ if (err == 0 && base != NULL) {
+ return base;
+ }
+ else {
+ // fall back to regular large pages
+ mi_huge_pages_available = false; // don't try further huge pages
+ _mi_warning_message("unable to allocate using huge (1gb) pages, trying large (2mb) pages instead (status 0x%lx)\n", err);
+ }
+ }
+ // on modern Windows try use VirtualAlloc2 for numa aware large OS page allocation
+ if (pVirtualAlloc2 != NULL && numa_node >= 0) {
+ params[0].Type = MemExtendedParameterNumaNode;
+ params[0].ULong = (unsigned)numa_node;
+ return (*pVirtualAlloc2)(GetCurrentProcess(), addr, size, flags, PAGE_READWRITE, params, 1);
+ }
+ #else
+ UNUSED(numa_node);
+ #endif
+ // otherwise use regular virtual alloc on older windows
+ return VirtualAlloc(addr, size, flags, PAGE_READWRITE);
+}
+
+#elif defined(MI_OS_USE_MMAP) && (MI_INTPTR_SIZE >= 8) && !defined(__HAIKU__)
+#include <sys/syscall.h>
+#ifndef MPOL_PREFERRED
+#define MPOL_PREFERRED 1
+#endif
+#if defined(SYS_mbind)
+static long mi_os_mbind(void* start, unsigned long len, unsigned long mode, const unsigned long* nmask, unsigned long maxnode, unsigned flags) {
+ return syscall(SYS_mbind, start, len, mode, nmask, maxnode, flags);
+}
+#else
+static long mi_os_mbind(void* start, unsigned long len, unsigned long mode, const unsigned long* nmask, unsigned long maxnode, unsigned flags) {
+ UNUSED(start); UNUSED(len); UNUSED(mode); UNUSED(nmask); UNUSED(maxnode); UNUSED(flags);
+ return 0;
+}
+#endif
+static void* mi_os_alloc_huge_os_pagesx(void* addr, size_t size, int numa_node) {
+ mi_assert_internal(size%GiB == 0);
+ bool is_large = true;
+ void* p = mi_unix_mmap(addr, size, MI_SEGMENT_SIZE, PROT_READ | PROT_WRITE, true, true, &is_large);
+ if (p == NULL) return NULL;
+ if (numa_node >= 0 && numa_node < 8*MI_INTPTR_SIZE) { // at most 64 nodes
+ uintptr_t numa_mask = (1UL << numa_node);
+ // TODO: does `mbind` work correctly for huge OS pages? should we
+ // use `set_mempolicy` before calling mmap instead?
+ // see: <https://lkml.org/lkml/2017/2/9/875>
+ long err = mi_os_mbind(p, size, MPOL_PREFERRED, &numa_mask, 8*MI_INTPTR_SIZE, 0);
+ if (err != 0) {
+ _mi_warning_message("failed to bind huge (1gb) pages to numa node %d: %s\n", numa_node, strerror(errno));
+ }
+ }
+ return p;
+}
+#else
+static void* mi_os_alloc_huge_os_pagesx(void* addr, size_t size, int numa_node) {
+ UNUSED(addr); UNUSED(size); UNUSED(numa_node);
+ return NULL;
+}
+#endif
+
+#if (MI_INTPTR_SIZE >= 8)
+// To ensure proper alignment, use our own area for huge OS pages
+static mi_decl_cache_align _Atomic(uintptr_t) mi_huge_start; // = 0
+
+// Claim an aligned address range for huge pages
+static uint8_t* mi_os_claim_huge_pages(size_t pages, size_t* total_size) {
+ if (total_size != NULL) *total_size = 0;
+ const size_t size = pages * MI_HUGE_OS_PAGE_SIZE;
+
+ uintptr_t start = 0;
+ uintptr_t end = 0;
+ uintptr_t expected;
+ do {
+ start = expected = mi_atomic_read_relaxed(&mi_huge_start);
+ if (start == 0) {
+ // Initialize the start address after the 32TiB area
+ start = ((uintptr_t)32 << 40); // 32TiB virtual start address
+#if (MI_SECURE>0 || MI_DEBUG==0) // security: randomize start of huge pages unless in debug mode
+ uintptr_t r = _mi_heap_random_next(mi_get_default_heap());
+ start = start + ((uintptr_t)MI_HUGE_OS_PAGE_SIZE * ((r>>17) & 0x0FFF)); // (randomly 12bits)*1GiB == between 0 to 4TiB
+#endif
+ }
+ end = start + size;
+ mi_assert_internal(end % MI_SEGMENT_SIZE == 0);
+ } while (!mi_atomic_cas_strong(&mi_huge_start, end, expected));
+
+ if (total_size != NULL) *total_size = size;
+ return (uint8_t*)start;
+}
+#else
+static uint8_t* mi_os_claim_huge_pages(size_t pages, size_t* total_size) {
+ UNUSED(pages);
+ if (total_size != NULL) *total_size = 0;
+ return NULL;
+}
+#endif
+
+// Allocate MI_SEGMENT_SIZE aligned huge pages
+void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_msecs, size_t* pages_reserved, size_t* psize) {
+ if (psize != NULL) *psize = 0;
+ if (pages_reserved != NULL) *pages_reserved = 0;
+ size_t size = 0;
+ uint8_t* start = mi_os_claim_huge_pages(pages, &size);
+ if (start == NULL) return NULL; // or 32-bit systems
+
+ // Allocate one page at the time but try to place them contiguously
+ // We allocate one page at the time to be able to abort if it takes too long
+ // or to at least allocate as many as available on the system.
+ mi_msecs_t start_t = _mi_clock_start();
+ size_t page;
+ for (page = 0; page < pages; page++) {
+ // allocate a page
+ void* addr = start + (page * MI_HUGE_OS_PAGE_SIZE);
+ void* p = mi_os_alloc_huge_os_pagesx(addr, MI_HUGE_OS_PAGE_SIZE, numa_node);
+
+ // Did we succeed at a contiguous address?
+ if (p != addr) {
+ // no success, issue a warning and break
+ if (p != NULL) {
+ _mi_warning_message("could not allocate contiguous huge page %zu at %p\n", page, addr);
+ _mi_os_free(p, MI_HUGE_OS_PAGE_SIZE, &_mi_stats_main);
+ }
+ break;
+ }
+
+ // success, record it
+ _mi_stat_increase(&_mi_stats_main.committed, MI_HUGE_OS_PAGE_SIZE);
+ _mi_stat_increase(&_mi_stats_main.reserved, MI_HUGE_OS_PAGE_SIZE);
+
+ // check for timeout
+ if (max_msecs > 0) {
+ mi_msecs_t elapsed = _mi_clock_end(start_t);
+ if (page >= 1) {
+ mi_msecs_t estimate = ((elapsed / (page+1)) * pages);
+ if (estimate > 2*max_msecs) { // seems like we are going to timeout, break
+ elapsed = max_msecs + 1;
+ }
+ }
+ if (elapsed > max_msecs) {
+ _mi_warning_message("huge page allocation timed out\n");
+ break;
+ }
+ }
+ }
+ mi_assert_internal(page*MI_HUGE_OS_PAGE_SIZE <= size);
+ if (pages_reserved != NULL) *pages_reserved = page;
+ if (psize != NULL) *psize = page * MI_HUGE_OS_PAGE_SIZE;
+ return (page == 0 ? NULL : start);
+}
+
+// free every huge page in a range individually (as we allocated per page)
+// note: needed with VirtualAlloc but could potentially be done in one go on mmap'd systems.
+void _mi_os_free_huge_pages(void* p, size_t size, mi_stats_t* stats) {
+ if (p==NULL || size==0) return;
+ uint8_t* base = (uint8_t*)p;
+ while (size >= MI_HUGE_OS_PAGE_SIZE) {
+ _mi_os_free(base, MI_HUGE_OS_PAGE_SIZE, stats);
+ size -= MI_HUGE_OS_PAGE_SIZE;
+ }
+}
+
+/* ----------------------------------------------------------------------------
+Support NUMA aware allocation
+-----------------------------------------------------------------------------*/
+#ifdef _WIN32
+static size_t mi_os_numa_nodex() {
+ USHORT numa_node = 0;
+ if (pGetCurrentProcessorNumberEx != NULL && pGetNumaProcessorNodeEx != NULL) {
+ // Extended API is supported
+ PROCESSOR_NUMBER pnum;
+ (*pGetCurrentProcessorNumberEx)(&pnum);
+ USHORT nnode = 0;
+ BOOL ok = (*pGetNumaProcessorNodeEx)(&pnum, &nnode);
+ if (ok) numa_node = nnode;
+ }
+ else {
+ // Vista or earlier, use older API that is limited to 64 processors. Issue #277
+ DWORD pnum = GetCurrentProcessorNumber();
+ UCHAR nnode = 0;
+ BOOL ok = GetNumaProcessorNode((UCHAR)pnum, &nnode);
+ if (ok) numa_node = nnode;
+ }
+ return numa_node;
+}
+
+static size_t mi_os_numa_node_countx(void) {
+ ULONG numa_max = 0;
+ GetNumaHighestNodeNumber(&numa_max);
+ // find the highest node number that has actual processors assigned to it. Issue #282
+ while(numa_max > 0) {
+ if (pGetNumaNodeProcessorMaskEx != NULL) {
+ // Extended API is supported
+ GROUP_AFFINITY affinity;
+ if ((*pGetNumaNodeProcessorMaskEx)((USHORT)numa_max, &affinity)) {
+ if (affinity.Mask != 0) break; // found the maximum non-empty node
+ }
+ }
+ else {
+ // Vista or earlier, use older API that is limited to 64 processors.
+ ULONGLONG mask;
+ if (GetNumaNodeProcessorMask((UCHAR)numa_max, &mask)) {
+ if (mask != 0) break; // found the maximum non-empty node
+ };
+ }
+ // max node was invalid or had no processor assigned, try again
+ numa_max--;
+ }
+ return ((size_t)numa_max + 1);
+}
+#elif defined(__linux__)
+#include <sys/syscall.h> // getcpu
+#include <stdio.h> // access
+
+static size_t mi_os_numa_nodex(void) {
+#ifdef SYS_getcpu
+ unsigned long node = 0;
+ unsigned long ncpu = 0;
+ long err = syscall(SYS_getcpu, &ncpu, &node, NULL);
+ if (err != 0) return 0;
+ return node;
+#else
+ return 0;
+#endif
+}
+static size_t mi_os_numa_node_countx(void) {
+ char buf[128];
+ unsigned node = 0;
+ for(node = 0; node < 256; node++) {
+ // enumerate node entries -- todo: it there a more efficient way to do this? (but ensure there is no allocation)
+ snprintf(buf, 127, "/sys/devices/system/node/node%u", node + 1);
+ if (access(buf,R_OK) != 0) break;
+ }
+ return (node+1);
+}
+#else
+static size_t mi_os_numa_nodex(void) {
+ return 0;
+}
+static size_t mi_os_numa_node_countx(void) {
+ return 1;
+}
+#endif
+
+size_t _mi_numa_node_count = 0; // cache the node count
+
+size_t _mi_os_numa_node_count_get(void) {
+ if (mi_unlikely(_mi_numa_node_count <= 0)) {
+ long ncount = mi_option_get(mi_option_use_numa_nodes); // given explicitly?
+ if (ncount <= 0) ncount = (long)mi_os_numa_node_countx(); // or detect dynamically
+ _mi_numa_node_count = (size_t)(ncount <= 0 ? 1 : ncount);
+ _mi_verbose_message("using %zd numa regions\n", _mi_numa_node_count);
+ }
+ mi_assert_internal(_mi_numa_node_count >= 1);
+ return _mi_numa_node_count;
+}
+
+int _mi_os_numa_node_get(mi_os_tld_t* tld) {
+ UNUSED(tld);
+ size_t numa_count = _mi_os_numa_node_count();
+ if (numa_count<=1) return 0; // optimize on single numa node systems: always node 0
+ // never more than the node count and >= 0
+ size_t numa_node = mi_os_numa_nodex();
+ if (numa_node >= numa_count) { numa_node = numa_node % numa_count; }
+ return (int)numa_node;
+}
--- /dev/null
+/*----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+/* -----------------------------------------------------------
+ Definition of page queues for each block size
+----------------------------------------------------------- */
+
+#ifndef MI_IN_PAGE_C
+#error "this file should be included from 'page.c'"
+#endif
+
+/* -----------------------------------------------------------
+ Minimal alignment in machine words (i.e. `sizeof(void*)`)
+----------------------------------------------------------- */
+
+#if (MI_MAX_ALIGN_SIZE > 4*MI_INTPTR_SIZE)
+ #error "define alignment for more than 4x word size for this platform"
+#elif (MI_MAX_ALIGN_SIZE > 2*MI_INTPTR_SIZE)
+ #define MI_ALIGN4W // 4 machine words minimal alignment
+#elif (MI_MAX_ALIGN_SIZE > MI_INTPTR_SIZE)
+ #define MI_ALIGN2W // 2 machine words minimal alignment
+#else
+ // ok, default alignment is 1 word
+#endif
+
+
+/* -----------------------------------------------------------
+ Queue query
+----------------------------------------------------------- */
+
+
+static inline bool mi_page_queue_is_huge(const mi_page_queue_t* pq) {
+ return (pq->block_size == (MI_LARGE_OBJ_SIZE_MAX+sizeof(uintptr_t)));
+}
+
+static inline bool mi_page_queue_is_full(const mi_page_queue_t* pq) {
+ return (pq->block_size == (MI_LARGE_OBJ_SIZE_MAX+(2*sizeof(uintptr_t))));
+}
+
+static inline bool mi_page_queue_is_special(const mi_page_queue_t* pq) {
+ return (pq->block_size > MI_LARGE_OBJ_SIZE_MAX);
+}
+
+/* -----------------------------------------------------------
+ Bins
+----------------------------------------------------------- */
+
+// Bit scan reverse: return the index of the highest bit.
+static inline uint8_t mi_bsr32(uint32_t x);
+
+#if defined(_MSC_VER)
+#include <intrin.h>
+static inline uint8_t mi_bsr32(uint32_t x) {
+ uint32_t idx;
+ _BitScanReverse((DWORD*)&idx, x);
+ return (uint8_t)idx;
+}
+#elif defined(__GNUC__) || defined(__clang__)
+static inline uint8_t mi_bsr32(uint32_t x) {
+ return (31 - __builtin_clz(x));
+}
+#else
+static inline uint8_t mi_bsr32(uint32_t x) {
+ // de Bruijn multiplication, see <http://supertech.csail.mit.edu/papers/debruijn.pdf>
+ static const uint8_t debruijn[32] = {
+ 31, 0, 22, 1, 28, 23, 18, 2, 29, 26, 24, 10, 19, 7, 3, 12,
+ 30, 21, 27, 17, 25, 9, 6, 11, 20, 16, 8, 5, 15, 4, 14, 13,
+ };
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ x++;
+ return debruijn[(x*0x076be629) >> 27];
+}
+#endif
+
+// Bit scan reverse: return the index of the highest bit.
+uint8_t _mi_bsr(uintptr_t x) {
+ if (x == 0) return 0;
+#if MI_INTPTR_SIZE==8
+ uint32_t hi = (x >> 32);
+ return (hi == 0 ? mi_bsr32((uint32_t)x) : 32 + mi_bsr32(hi));
+#elif MI_INTPTR_SIZE==4
+ return mi_bsr32(x);
+#else
+# error "define bsr for non-32 or 64-bit platforms"
+#endif
+}
+
+// Return the bin for a given field size.
+// Returns MI_BIN_HUGE if the size is too large.
+// We use `wsize` for the size in "machine word sizes",
+// i.e. byte size == `wsize*sizeof(void*)`.
+extern inline uint8_t _mi_bin(size_t size) {
+ size_t wsize = _mi_wsize_from_size(size);
+ uint8_t bin;
+ if (wsize <= 1) {
+ bin = 1;
+ }
+ #if defined(MI_ALIGN4W)
+ else if (wsize <= 4) {
+ bin = (uint8_t)((wsize+1)&~1); // round to double word sizes
+ }
+ #elif defined(MI_ALIGN2W)
+ else if (wsize <= 8) {
+ bin = (uint8_t)((wsize+1)&~1); // round to double word sizes
+ }
+ #else
+ else if (wsize <= 8) {
+ bin = (uint8_t)wsize;
+ }
+ #endif
+ else if (wsize > MI_LARGE_OBJ_WSIZE_MAX) {
+ bin = MI_BIN_HUGE;
+ }
+ else {
+ #if defined(MI_ALIGN4W)
+ if (wsize <= 16) { wsize = (wsize+3)&~3; } // round to 4x word sizes
+ #endif
+ wsize--;
+ // find the highest bit
+ uint8_t b = mi_bsr32((uint32_t)wsize);
+ // and use the top 3 bits to determine the bin (~12.5% worst internal fragmentation).
+ // - adjust with 3 because we use do not round the first 8 sizes
+ // which each get an exact bin
+ bin = ((b << 2) + (uint8_t)((wsize >> (b - 2)) & 0x03)) - 3;
+ mi_assert_internal(bin < MI_BIN_HUGE);
+ }
+ mi_assert_internal(bin > 0 && bin <= MI_BIN_HUGE);
+ return bin;
+}
+
+
+
+/* -----------------------------------------------------------
+ Queue of pages with free blocks
+----------------------------------------------------------- */
+
+size_t _mi_bin_size(uint8_t bin) {
+ return _mi_heap_empty.pages[bin].block_size;
+}
+
+// Good size for allocation
+size_t mi_good_size(size_t size) mi_attr_noexcept {
+ if (size <= MI_LARGE_OBJ_SIZE_MAX) {
+ return _mi_bin_size(_mi_bin(size));
+ }
+ else {
+ return _mi_align_up(size,_mi_os_page_size());
+ }
+}
+
+#if (MI_DEBUG>1)
+static bool mi_page_queue_contains(mi_page_queue_t* queue, const mi_page_t* page) {
+ mi_assert_internal(page != NULL);
+ mi_page_t* list = queue->first;
+ while (list != NULL) {
+ mi_assert_internal(list->next == NULL || list->next->prev == list);
+ mi_assert_internal(list->prev == NULL || list->prev->next == list);
+ if (list == page) break;
+ list = list->next;
+ }
+ return (list == page);
+}
+
+#endif
+
+#if (MI_DEBUG>1)
+static bool mi_heap_contains_queue(const mi_heap_t* heap, const mi_page_queue_t* pq) {
+ return (pq >= &heap->pages[0] && pq <= &heap->pages[MI_BIN_FULL]);
+}
+#endif
+
+static mi_page_queue_t* mi_page_queue_of(const mi_page_t* page) {
+ uint8_t bin = (mi_page_is_in_full(page) ? MI_BIN_FULL : _mi_bin(page->xblock_size));
+ mi_heap_t* heap = mi_page_heap(page);
+ mi_assert_internal(heap != NULL && bin <= MI_BIN_FULL);
+ mi_page_queue_t* pq = &heap->pages[bin];
+ mi_assert_internal(bin >= MI_BIN_HUGE || page->xblock_size == pq->block_size);
+ mi_assert_expensive(mi_page_queue_contains(pq, page));
+ return pq;
+}
+
+static mi_page_queue_t* mi_heap_page_queue_of(mi_heap_t* heap, const mi_page_t* page) {
+ uint8_t bin = (mi_page_is_in_full(page) ? MI_BIN_FULL : _mi_bin(page->xblock_size));
+ mi_assert_internal(bin <= MI_BIN_FULL);
+ mi_page_queue_t* pq = &heap->pages[bin];
+ mi_assert_internal(mi_page_is_in_full(page) || page->xblock_size == pq->block_size);
+ return pq;
+}
+
+// The current small page array is for efficiency and for each
+// small size (up to 256) it points directly to the page for that
+// size without having to compute the bin. This means when the
+// current free page queue is updated for a small bin, we need to update a
+// range of entries in `_mi_page_small_free`.
+static inline void mi_heap_queue_first_update(mi_heap_t* heap, const mi_page_queue_t* pq) {
+ mi_assert_internal(mi_heap_contains_queue(heap,pq));
+ size_t size = pq->block_size;
+ if (size > MI_SMALL_SIZE_MAX) return;
+
+ mi_page_t* page = pq->first;
+ if (pq->first == NULL) page = (mi_page_t*)&_mi_page_empty;
+
+ // find index in the right direct page array
+ size_t start;
+ size_t idx = _mi_wsize_from_size(size);
+ mi_page_t** pages_free = heap->pages_free_direct;
+
+ if (pages_free[idx] == page) return; // already set
+
+ // find start slot
+ if (idx<=1) {
+ start = 0;
+ }
+ else {
+ // find previous size; due to minimal alignment upto 3 previous bins may need to be skipped
+ uint8_t bin = _mi_bin(size);
+ const mi_page_queue_t* prev = pq - 1;
+ while( bin == _mi_bin(prev->block_size) && prev > &heap->pages[0]) {
+ prev--;
+ }
+ start = 1 + _mi_wsize_from_size(prev->block_size);
+ if (start > idx) start = idx;
+ }
+
+ // set size range to the right page
+ mi_assert(start <= idx);
+ for (size_t sz = start; sz <= idx; sz++) {
+ pages_free[sz] = page;
+ }
+}
+
+/*
+static bool mi_page_queue_is_empty(mi_page_queue_t* queue) {
+ return (queue->first == NULL);
+}
+*/
+
+static void mi_page_queue_remove(mi_page_queue_t* queue, mi_page_t* page) {
+ mi_assert_internal(page != NULL);
+ mi_assert_expensive(mi_page_queue_contains(queue, page));
+ mi_assert_internal(page->xblock_size == queue->block_size || (page->xblock_size > MI_LARGE_OBJ_SIZE_MAX && mi_page_queue_is_huge(queue)) || (mi_page_is_in_full(page) && mi_page_queue_is_full(queue)));
+ mi_heap_t* heap = mi_page_heap(page);
+ if (page->prev != NULL) page->prev->next = page->next;
+ if (page->next != NULL) page->next->prev = page->prev;
+ if (page == queue->last) queue->last = page->prev;
+ if (page == queue->first) {
+ queue->first = page->next;
+ // update first
+ mi_assert_internal(mi_heap_contains_queue(heap, queue));
+ mi_heap_queue_first_update(heap,queue);
+ }
+ heap->page_count--;
+ page->next = NULL;
+ page->prev = NULL;
+ // mi_atomic_write_ptr(mi_atomic_cast(void*, &page->heap), NULL);
+ mi_page_set_in_full(page,false);
+}
+
+
+static void mi_page_queue_push(mi_heap_t* heap, mi_page_queue_t* queue, mi_page_t* page) {
+ mi_assert_internal(mi_page_heap(page) == heap);
+ mi_assert_internal(!mi_page_queue_contains(queue, page));
+ mi_assert_internal(_mi_page_segment(page)->page_kind != MI_PAGE_HUGE);
+ mi_assert_internal(page->xblock_size == queue->block_size ||
+ (page->xblock_size > MI_LARGE_OBJ_SIZE_MAX && mi_page_queue_is_huge(queue)) ||
+ (mi_page_is_in_full(page) && mi_page_queue_is_full(queue)));
+
+ mi_page_set_in_full(page, mi_page_queue_is_full(queue));
+ // mi_atomic_write_ptr(mi_atomic_cast(void*, &page->heap), heap);
+ page->next = queue->first;
+ page->prev = NULL;
+ if (queue->first != NULL) {
+ mi_assert_internal(queue->first->prev == NULL);
+ queue->first->prev = page;
+ queue->first = page;
+ }
+ else {
+ queue->first = queue->last = page;
+ }
+
+ // update direct
+ mi_heap_queue_first_update(heap, queue);
+ heap->page_count++;
+}
+
+
+static void mi_page_queue_enqueue_from(mi_page_queue_t* to, mi_page_queue_t* from, mi_page_t* page) {
+ mi_assert_internal(page != NULL);
+ mi_assert_expensive(mi_page_queue_contains(from, page));
+ mi_assert_expensive(!mi_page_queue_contains(to, page));
+ mi_assert_internal((page->xblock_size == to->block_size && page->xblock_size == from->block_size) ||
+ (page->xblock_size == to->block_size && mi_page_queue_is_full(from)) ||
+ (page->xblock_size == from->block_size && mi_page_queue_is_full(to)) ||
+ (page->xblock_size > MI_LARGE_OBJ_SIZE_MAX && mi_page_queue_is_huge(to)) ||
+ (page->xblock_size > MI_LARGE_OBJ_SIZE_MAX && mi_page_queue_is_full(to)));
+
+ mi_heap_t* heap = mi_page_heap(page);
+ if (page->prev != NULL) page->prev->next = page->next;
+ if (page->next != NULL) page->next->prev = page->prev;
+ if (page == from->last) from->last = page->prev;
+ if (page == from->first) {
+ from->first = page->next;
+ // update first
+ mi_assert_internal(mi_heap_contains_queue(heap, from));
+ mi_heap_queue_first_update(heap, from);
+ }
+
+ page->prev = to->last;
+ page->next = NULL;
+ if (to->last != NULL) {
+ mi_assert_internal(heap == mi_page_heap(to->last));
+ to->last->next = page;
+ to->last = page;
+ }
+ else {
+ to->first = page;
+ to->last = page;
+ mi_heap_queue_first_update(heap, to);
+ }
+
+ mi_page_set_in_full(page, mi_page_queue_is_full(to));
+}
+
+// Only called from `mi_heap_absorb`.
+size_t _mi_page_queue_append(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_queue_t* append) {
+ mi_assert_internal(mi_heap_contains_queue(heap,pq));
+ mi_assert_internal(pq->block_size == append->block_size);
+
+ if (append->first==NULL) return 0;
+
+ // set append pages to new heap and count
+ size_t count = 0;
+ for (mi_page_t* page = append->first; page != NULL; page = page->next) {
+ // inline `mi_page_set_heap` to avoid wrong assertion during absorption;
+ // in this case it is ok to be delayed freeing since both "to" and "from" heap are still alive.
+ mi_atomic_write(&page->xheap, (uintptr_t)heap);
+ // set the flag to delayed free (not overriding NEVER_DELAYED_FREE) which has as a
+ // side effect that it spins until any DELAYED_FREEING is finished. This ensures
+ // that after appending only the new heap will be used for delayed free operations.
+ _mi_page_use_delayed_free(page, MI_USE_DELAYED_FREE, false);
+ count++;
+ }
+
+ if (pq->last==NULL) {
+ // take over afresh
+ mi_assert_internal(pq->first==NULL);
+ pq->first = append->first;
+ pq->last = append->last;
+ mi_heap_queue_first_update(heap, pq);
+ }
+ else {
+ // append to end
+ mi_assert_internal(pq->last!=NULL);
+ mi_assert_internal(append->first!=NULL);
+ pq->last->next = append->first;
+ append->first->prev = pq->last;
+ pq->last = append->last;
+ }
+ return count;
+}
--- /dev/null
+/*----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+/* -----------------------------------------------------------
+ The core of the allocator. Every segment contains
+ pages of a {certain block size. The main function
+ exported is `mi_malloc_generic`.
+----------------------------------------------------------- */
+
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+#include "mimalloc-atomic.h"
+
+/* -----------------------------------------------------------
+ Definition of page queues for each block size
+----------------------------------------------------------- */
+
+#define MI_IN_PAGE_C
+#include "page-queue.c"
+#undef MI_IN_PAGE_C
+
+
+/* -----------------------------------------------------------
+ Page helpers
+----------------------------------------------------------- */
+
+// Index a block in a page
+static inline mi_block_t* mi_page_block_at(const mi_page_t* page, void* page_start, size_t block_size, size_t i) {
+ UNUSED(page);
+ mi_assert_internal(page != NULL);
+ mi_assert_internal(i <= page->reserved);
+ return (mi_block_t*)((uint8_t*)page_start + (i * block_size));
+}
+
+static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t size, mi_tld_t* tld);
+static void mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_tld_t* tld);
+
+#if (MI_DEBUG>=3)
+static size_t mi_page_list_count(mi_page_t* page, mi_block_t* head) {
+ size_t count = 0;
+ while (head != NULL) {
+ mi_assert_internal(page == _mi_ptr_page(head));
+ count++;
+ head = mi_block_next(page, head);
+ }
+ return count;
+}
+
+/*
+// Start of the page available memory
+static inline uint8_t* mi_page_area(const mi_page_t* page) {
+ return _mi_page_start(_mi_page_segment(page), page, NULL);
+}
+*/
+
+static bool mi_page_list_is_valid(mi_page_t* page, mi_block_t* p) {
+ size_t psize;
+ uint8_t* page_area = _mi_page_start(_mi_page_segment(page), page, &psize);
+ mi_block_t* start = (mi_block_t*)page_area;
+ mi_block_t* end = (mi_block_t*)(page_area + psize);
+ while(p != NULL) {
+ if (p < start || p >= end) return false;
+ p = mi_block_next(page, p);
+ }
+ return true;
+}
+
+static bool mi_page_is_valid_init(mi_page_t* page) {
+ mi_assert_internal(page->xblock_size > 0);
+ mi_assert_internal(page->used <= page->capacity);
+ mi_assert_internal(page->capacity <= page->reserved);
+
+ const size_t bsize = mi_page_block_size(page);
+ mi_segment_t* segment = _mi_page_segment(page);
+ uint8_t* start = _mi_page_start(segment,page,NULL);
+ mi_assert_internal(start == _mi_segment_page_start(segment,page,bsize,NULL,NULL));
+ //mi_assert_internal(start + page->capacity*page->block_size == page->top);
+
+ mi_assert_internal(mi_page_list_is_valid(page,page->free));
+ mi_assert_internal(mi_page_list_is_valid(page,page->local_free));
+
+ #if MI_DEBUG>3 // generally too expensive to check this
+ if (page->flags.is_zero) {
+ for(mi_block_t* block = page->free; block != NULL; mi_block_next(page,block)) {
+ mi_assert_expensive(mi_mem_is_zero(block + 1, page->block_size - sizeof(mi_block_t)));
+ }
+ }
+ #endif
+
+ mi_block_t* tfree = mi_page_thread_free(page);
+ mi_assert_internal(mi_page_list_is_valid(page, tfree));
+ //size_t tfree_count = mi_page_list_count(page, tfree);
+ //mi_assert_internal(tfree_count <= page->thread_freed + 1);
+
+ size_t free_count = mi_page_list_count(page, page->free) + mi_page_list_count(page, page->local_free);
+ mi_assert_internal(page->used + free_count == page->capacity);
+
+ return true;
+}
+
+bool _mi_page_is_valid(mi_page_t* page) {
+ mi_assert_internal(mi_page_is_valid_init(page));
+ #if MI_SECURE
+ mi_assert_internal(page->keys[0] != 0);
+ #endif
+ if (mi_page_heap(page)!=NULL) {
+ mi_segment_t* segment = _mi_page_segment(page);
+ mi_assert_internal(!_mi_process_is_initialized || segment->thread_id == mi_page_heap(page)->thread_id || segment->thread_id==0);
+ if (segment->page_kind != MI_PAGE_HUGE) {
+ mi_page_queue_t* pq = mi_page_queue_of(page);
+ mi_assert_internal(mi_page_queue_contains(pq, page));
+ mi_assert_internal(pq->block_size==mi_page_block_size(page) || mi_page_block_size(page) > MI_LARGE_OBJ_SIZE_MAX || mi_page_is_in_full(page));
+ mi_assert_internal(mi_heap_contains_queue(mi_page_heap(page),pq));
+ }
+ }
+ return true;
+}
+#endif
+
+void _mi_page_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never) {
+ mi_thread_free_t tfree;
+ mi_thread_free_t tfreex;
+ mi_delayed_t old_delay;
+ do {
+ tfree = mi_atomic_read(&page->xthread_free); // note: must acquire as we can break this loop and not do a CAS
+ tfreex = mi_tf_set_delayed(tfree, delay);
+ old_delay = mi_tf_delayed(tfree);
+ if (mi_unlikely(old_delay == MI_DELAYED_FREEING)) {
+ mi_atomic_yield(); // delay until outstanding MI_DELAYED_FREEING are done.
+ // tfree = mi_tf_set_delayed(tfree, MI_NO_DELAYED_FREE); // will cause CAS to busy fail
+ }
+ else if (delay == old_delay) {
+ break; // avoid atomic operation if already equal
+ }
+ else if (!override_never && old_delay == MI_NEVER_DELAYED_FREE) {
+ break; // leave never-delayed flag set
+ }
+ } while ((old_delay == MI_DELAYED_FREEING) ||
+ !mi_atomic_cas_weak(&page->xthread_free, tfreex, tfree));
+}
+
+/* -----------------------------------------------------------
+ Page collect the `local_free` and `thread_free` lists
+----------------------------------------------------------- */
+
+// Collect the local `thread_free` list using an atomic exchange.
+// Note: The exchange must be done atomically as this is used right after
+// moving to the full list in `mi_page_collect_ex` and we need to
+// ensure that there was no race where the page became unfull just before the move.
+static void _mi_page_thread_free_collect(mi_page_t* page)
+{
+ mi_block_t* head;
+ mi_thread_free_t tfree;
+ mi_thread_free_t tfreex;
+ do {
+ tfree = mi_atomic_read_relaxed(&page->xthread_free);
+ head = mi_tf_block(tfree);
+ tfreex = mi_tf_set_block(tfree,NULL);
+ } while (!mi_atomic_cas_weak(&page->xthread_free, tfreex, tfree));
+
+ // return if the list is empty
+ if (head == NULL) return;
+
+ // find the tail -- also to get a proper count (without data races)
+ uint32_t max_count = page->capacity; // cannot collect more than capacity
+ uint32_t count = 1;
+ mi_block_t* tail = head;
+ mi_block_t* next;
+ while ((next = mi_block_next(page,tail)) != NULL && count <= max_count) {
+ count++;
+ tail = next;
+ }
+ // if `count > max_count` there was a memory corruption (possibly infinite list due to double multi-threaded free)
+ if (count > max_count) {
+ _mi_error_message(EFAULT, "corrupted thread-free list\n");
+ return; // the thread-free items cannot be freed
+ }
+
+ // and append the current local free list
+ mi_block_set_next(page,tail, page->local_free);
+ page->local_free = head;
+
+ // update counts now
+ page->used -= count;
+}
+
+void _mi_page_free_collect(mi_page_t* page, bool force) {
+ mi_assert_internal(page!=NULL);
+
+ // collect the thread free list
+ if (force || mi_page_thread_free(page) != NULL) { // quick test to avoid an atomic operation
+ _mi_page_thread_free_collect(page);
+ }
+
+ // and the local free list
+ if (page->local_free != NULL) {
+ if (mi_likely(page->free == NULL)) {
+ // usual case
+ page->free = page->local_free;
+ page->local_free = NULL;
+ page->is_zero = false;
+ }
+ else if (force) {
+ // append -- only on shutdown (force) as this is a linear operation
+ mi_block_t* tail = page->local_free;
+ mi_block_t* next;
+ while ((next = mi_block_next(page, tail)) != NULL) {
+ tail = next;
+ }
+ mi_block_set_next(page, tail, page->free);
+ page->free = page->local_free;
+ page->local_free = NULL;
+ page->is_zero = false;
+ }
+ }
+
+ mi_assert_internal(!force || page->local_free == NULL);
+}
+
+
+
+/* -----------------------------------------------------------
+ Page fresh and retire
+----------------------------------------------------------- */
+
+// called from segments when reclaiming abandoned pages
+void _mi_page_reclaim(mi_heap_t* heap, mi_page_t* page) {
+ mi_assert_expensive(mi_page_is_valid_init(page));
+ mi_assert_internal(mi_page_heap(page) == heap);
+ mi_assert_internal(mi_page_thread_free_flag(page) != MI_NEVER_DELAYED_FREE);
+ mi_assert_internal(_mi_page_segment(page)->page_kind != MI_PAGE_HUGE);
+ mi_assert_internal(!page->is_reset);
+ // TODO: push on full queue immediately if it is full?
+ mi_page_queue_t* pq = mi_page_queue(heap, mi_page_block_size(page));
+ mi_page_queue_push(heap, pq, page);
+ mi_assert_expensive(_mi_page_is_valid(page));
+}
+
+// allocate a fresh page from a segment
+static mi_page_t* mi_page_fresh_alloc(mi_heap_t* heap, mi_page_queue_t* pq, size_t block_size) {
+ mi_assert_internal(pq==NULL||mi_heap_contains_queue(heap, pq));
+ mi_assert_internal(pq==NULL||block_size == pq->block_size);
+ mi_page_t* page = _mi_segment_page_alloc(heap, block_size, &heap->tld->segments, &heap->tld->os);
+ if (page == NULL) {
+ // this may be out-of-memory, or an abandoned page was reclaimed (and in our queue)
+ return NULL;
+ }
+ // a fresh page was found, initialize it
+ mi_assert_internal(pq==NULL || _mi_page_segment(page)->page_kind != MI_PAGE_HUGE);
+ mi_page_init(heap, page, block_size, heap->tld);
+ _mi_stat_increase(&heap->tld->stats.pages, 1);
+ if (pq!=NULL) mi_page_queue_push(heap, pq, page); // huge pages use pq==NULL
+ mi_assert_expensive(_mi_page_is_valid(page));
+ return page;
+}
+
+// Get a fresh page to use
+static mi_page_t* mi_page_fresh(mi_heap_t* heap, mi_page_queue_t* pq) {
+ mi_assert_internal(mi_heap_contains_queue(heap, pq));
+ mi_page_t* page = mi_page_fresh_alloc(heap, pq, pq->block_size);
+ if (page==NULL) return NULL;
+ mi_assert_internal(pq->block_size==mi_page_block_size(page));
+ mi_assert_internal(pq==mi_page_queue(heap, mi_page_block_size(page)));
+ return page;
+}
+
+/* -----------------------------------------------------------
+ Do any delayed frees
+ (put there by other threads if they deallocated in a full page)
+----------------------------------------------------------- */
+void _mi_heap_delayed_free(mi_heap_t* heap) {
+ // take over the list (note: no atomic exchange is it is often NULL)
+ mi_block_t* block;
+ do {
+ block = mi_atomic_read_ptr_relaxed(mi_block_t,&heap->thread_delayed_free);
+ } while (block != NULL && !mi_atomic_cas_ptr_weak(mi_block_t,&heap->thread_delayed_free, NULL, block));
+
+ // and free them all
+ while(block != NULL) {
+ mi_block_t* next = mi_block_nextx(heap,block, heap->keys);
+ // use internal free instead of regular one to keep stats etc correct
+ if (!_mi_free_delayed_block(block)) {
+ // we might already start delayed freeing while another thread has not yet
+ // reset the delayed_freeing flag; in that case delay it further by reinserting.
+ mi_block_t* dfree;
+ do {
+ dfree = mi_atomic_read_ptr_relaxed(mi_block_t,&heap->thread_delayed_free);
+ mi_block_set_nextx(heap, block, dfree, heap->keys);
+ } while (!mi_atomic_cas_ptr_weak(mi_block_t,&heap->thread_delayed_free, block, dfree));
+ }
+ block = next;
+ }
+}
+
+/* -----------------------------------------------------------
+ Unfull, abandon, free and retire
+----------------------------------------------------------- */
+
+// Move a page from the full list back to a regular list
+void _mi_page_unfull(mi_page_t* page) {
+ mi_assert_internal(page != NULL);
+ mi_assert_expensive(_mi_page_is_valid(page));
+ mi_assert_internal(mi_page_is_in_full(page));
+ if (!mi_page_is_in_full(page)) return;
+
+ mi_heap_t* heap = mi_page_heap(page);
+ mi_page_queue_t* pqfull = &heap->pages[MI_BIN_FULL];
+ mi_page_set_in_full(page, false); // to get the right queue
+ mi_page_queue_t* pq = mi_heap_page_queue_of(heap, page);
+ mi_page_set_in_full(page, true);
+ mi_page_queue_enqueue_from(pq, pqfull, page);
+}
+
+static void mi_page_to_full(mi_page_t* page, mi_page_queue_t* pq) {
+ mi_assert_internal(pq == mi_page_queue_of(page));
+ mi_assert_internal(!mi_page_immediate_available(page));
+ mi_assert_internal(!mi_page_is_in_full(page));
+
+ if (mi_page_is_in_full(page)) return;
+ mi_page_queue_enqueue_from(&mi_page_heap(page)->pages[MI_BIN_FULL], pq, page);
+ _mi_page_free_collect(page,false); // try to collect right away in case another thread freed just before MI_USE_DELAYED_FREE was set
+}
+
+
+// Abandon a page with used blocks at the end of a thread.
+// Note: only call if it is ensured that no references exist from
+// the `page->heap->thread_delayed_free` into this page.
+// Currently only called through `mi_heap_collect_ex` which ensures this.
+void _mi_page_abandon(mi_page_t* page, mi_page_queue_t* pq) {
+ mi_assert_internal(page != NULL);
+ mi_assert_expensive(_mi_page_is_valid(page));
+ mi_assert_internal(pq == mi_page_queue_of(page));
+ mi_assert_internal(mi_page_heap(page) != NULL);
+
+ mi_heap_t* pheap = mi_page_heap(page);
+
+ // remove from our page list
+ mi_segments_tld_t* segments_tld = &pheap->tld->segments;
+ mi_page_queue_remove(pq, page);
+
+ // page is no longer associated with our heap
+ mi_assert_internal(mi_page_thread_free_flag(page)==MI_NEVER_DELAYED_FREE);
+ mi_page_set_heap(page, NULL);
+
+#if MI_DEBUG>1
+ // check there are no references left..
+ for (mi_block_t* block = (mi_block_t*)pheap->thread_delayed_free; block != NULL; block = mi_block_nextx(pheap, block, pheap->keys)) {
+ mi_assert_internal(_mi_ptr_page(block) != page);
+ }
+#endif
+
+ // and abandon it
+ mi_assert_internal(mi_page_heap(page) == NULL);
+ _mi_segment_page_abandon(page,segments_tld);
+}
+
+
+// Free a page with no more free blocks
+void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force) {
+ mi_assert_internal(page != NULL);
+ mi_assert_expensive(_mi_page_is_valid(page));
+ mi_assert_internal(pq == mi_page_queue_of(page));
+ mi_assert_internal(mi_page_all_free(page));
+ mi_assert_internal(mi_page_thread_free_flag(page)!=MI_DELAYED_FREEING);
+
+ // no more aligned blocks in here
+ mi_page_set_has_aligned(page, false);
+
+ // remove from the page list
+ // (no need to do _mi_heap_delayed_free first as all blocks are already free)
+ mi_segments_tld_t* segments_tld = &mi_page_heap(page)->tld->segments;
+ mi_page_queue_remove(pq, page);
+
+ // and free it
+ mi_page_set_heap(page,NULL);
+ _mi_segment_page_free(page, force, segments_tld);
+}
+
+#define MI_MAX_RETIRE_SIZE MI_LARGE_OBJ_SIZE_MAX
+#define MI_RETIRE_CYCLES (8)
+
+// Retire a page with no more used blocks
+// Important to not retire too quickly though as new
+// allocations might coming.
+// Note: called from `mi_free` and benchmarks often
+// trigger this due to freeing everything and then
+// allocating again so careful when changing this.
+void _mi_page_retire(mi_page_t* page) {
+ mi_assert_internal(page != NULL);
+ mi_assert_expensive(_mi_page_is_valid(page));
+ mi_assert_internal(mi_page_all_free(page));
+
+ mi_page_set_has_aligned(page, false);
+
+ // don't retire too often..
+ // (or we end up retiring and re-allocating most of the time)
+ // NOTE: refine this more: we should not retire if this
+ // is the only page left with free blocks. It is not clear
+ // how to check this efficiently though...
+ // for now, we don't retire if it is the only page left of this size class.
+ mi_page_queue_t* pq = mi_page_queue_of(page);
+ if (mi_likely(page->xblock_size <= MI_MAX_RETIRE_SIZE && !mi_page_is_in_full(page))) {
+ if (pq->last==page && pq->first==page) { // the only page in the queue?
+ mi_stat_counter_increase(_mi_stats_main.page_no_retire,1);
+ page->retire_expire = (page->xblock_size <= MI_SMALL_OBJ_SIZE_MAX ? MI_RETIRE_CYCLES : MI_RETIRE_CYCLES/4);
+ mi_heap_t* heap = mi_page_heap(page);
+ mi_assert_internal(pq >= heap->pages);
+ const size_t index = pq - heap->pages;
+ mi_assert_internal(index < MI_BIN_FULL && index < MI_BIN_HUGE);
+ if (index < heap->page_retired_min) heap->page_retired_min = index;
+ if (index > heap->page_retired_max) heap->page_retired_max = index;
+ mi_assert_internal(mi_page_all_free(page));
+ return; // dont't free after all
+ }
+ }
+
+ _mi_page_free(page, pq, false);
+}
+
+// free retired pages: we don't need to look at the entire queues
+// since we only retire pages that are at the head position in a queue.
+void _mi_heap_collect_retired(mi_heap_t* heap, bool force) {
+ size_t min = MI_BIN_FULL;
+ size_t max = 0;
+ for(size_t bin = heap->page_retired_min; bin <= heap->page_retired_max; bin++) {
+ mi_page_queue_t* pq = &heap->pages[bin];
+ mi_page_t* page = pq->first;
+ if (page != NULL && page->retire_expire != 0) {
+ if (mi_page_all_free(page)) {
+ page->retire_expire--;
+ if (force || page->retire_expire == 0) {
+ _mi_page_free(pq->first, pq, force);
+ }
+ else {
+ // keep retired, update min/max
+ if (bin < min) min = bin;
+ if (bin > max) max = bin;
+ }
+ }
+ else {
+ page->retire_expire = 0;
+ }
+ }
+ }
+ heap->page_retired_min = min;
+ heap->page_retired_max = max;
+}
+
+
+/* -----------------------------------------------------------
+ Initialize the initial free list in a page.
+ In secure mode we initialize a randomized list by
+ alternating between slices.
+----------------------------------------------------------- */
+
+#define MI_MAX_SLICE_SHIFT (6) // at most 64 slices
+#define MI_MAX_SLICES (1UL << MI_MAX_SLICE_SHIFT)
+#define MI_MIN_SLICES (2)
+
+static void mi_page_free_list_extend_secure(mi_heap_t* const heap, mi_page_t* const page, const size_t bsize, const size_t extend, mi_stats_t* const stats) {
+ UNUSED(stats);
+ #if (MI_SECURE<=2)
+ mi_assert_internal(page->free == NULL);
+ mi_assert_internal(page->local_free == NULL);
+ #endif
+ mi_assert_internal(page->capacity + extend <= page->reserved);
+ mi_assert_internal(bsize == mi_page_block_size(page));
+ void* const page_area = _mi_page_start(_mi_page_segment(page), page, NULL);
+
+ // initialize a randomized free list
+ // set up `slice_count` slices to alternate between
+ size_t shift = MI_MAX_SLICE_SHIFT;
+ while ((extend >> shift) == 0) {
+ shift--;
+ }
+ const size_t slice_count = (size_t)1U << shift;
+ const size_t slice_extend = extend / slice_count;
+ mi_assert_internal(slice_extend >= 1);
+ mi_block_t* blocks[MI_MAX_SLICES]; // current start of the slice
+ size_t counts[MI_MAX_SLICES]; // available objects in the slice
+ for (size_t i = 0; i < slice_count; i++) {
+ blocks[i] = mi_page_block_at(page, page_area, bsize, page->capacity + i*slice_extend);
+ counts[i] = slice_extend;
+ }
+ counts[slice_count-1] += (extend % slice_count); // final slice holds the modulus too (todo: distribute evenly?)
+
+ // and initialize the free list by randomly threading through them
+ // set up first element
+ const uintptr_t r = _mi_heap_random_next(heap);
+ size_t current = r % slice_count;
+ counts[current]--;
+ mi_block_t* const free_start = blocks[current];
+ // and iterate through the rest; use `random_shuffle` for performance
+ uintptr_t rnd = _mi_random_shuffle(r|1); // ensure not 0
+ for (size_t i = 1; i < extend; i++) {
+ // call random_shuffle only every INTPTR_SIZE rounds
+ const size_t round = i%MI_INTPTR_SIZE;
+ if (round == 0) rnd = _mi_random_shuffle(rnd);
+ // select a random next slice index
+ size_t next = ((rnd >> 8*round) & (slice_count-1));
+ while (counts[next]==0) { // ensure it still has space
+ next++;
+ if (next==slice_count) next = 0;
+ }
+ // and link the current block to it
+ counts[next]--;
+ mi_block_t* const block = blocks[current];
+ blocks[current] = (mi_block_t*)((uint8_t*)block + bsize); // bump to the following block
+ mi_block_set_next(page, block, blocks[next]); // and set next; note: we may have `current == next`
+ current = next;
+ }
+ // prepend to the free list (usually NULL)
+ mi_block_set_next(page, blocks[current], page->free); // end of the list
+ page->free = free_start;
+}
+
+static mi_decl_noinline void mi_page_free_list_extend( mi_page_t* const page, const size_t bsize, const size_t extend, mi_stats_t* const stats)
+{
+ UNUSED(stats);
+ #if (MI_SECURE <= 2)
+ mi_assert_internal(page->free == NULL);
+ mi_assert_internal(page->local_free == NULL);
+ #endif
+ mi_assert_internal(page->capacity + extend <= page->reserved);
+ mi_assert_internal(bsize == mi_page_block_size(page));
+ void* const page_area = _mi_page_start(_mi_page_segment(page), page, NULL );
+
+ mi_block_t* const start = mi_page_block_at(page, page_area, bsize, page->capacity);
+
+ // initialize a sequential free list
+ mi_block_t* const last = mi_page_block_at(page, page_area, bsize, page->capacity + extend - 1);
+ mi_block_t* block = start;
+ while(block <= last) {
+ mi_block_t* next = (mi_block_t*)((uint8_t*)block + bsize);
+ mi_block_set_next(page,block,next);
+ block = next;
+ }
+ // prepend to free list (usually `NULL`)
+ mi_block_set_next(page, last, page->free);
+ page->free = start;
+}
+
+/* -----------------------------------------------------------
+ Page initialize and extend the capacity
+----------------------------------------------------------- */
+
+#define MI_MAX_EXTEND_SIZE (4*1024) // heuristic, one OS page seems to work well.
+#if (MI_SECURE>0)
+#define MI_MIN_EXTEND (8*MI_SECURE) // extend at least by this many
+#else
+#define MI_MIN_EXTEND (1)
+#endif
+
+// Extend the capacity (up to reserved) by initializing a free list
+// We do at most `MI_MAX_EXTEND` to avoid touching too much memory
+// Note: we also experimented with "bump" allocation on the first
+// allocations but this did not speed up any benchmark (due to an
+// extra test in malloc? or cache effects?)
+static void mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_tld_t* tld) {
+ mi_assert_expensive(mi_page_is_valid_init(page));
+ #if (MI_SECURE<=2)
+ mi_assert(page->free == NULL);
+ mi_assert(page->local_free == NULL);
+ if (page->free != NULL) return;
+ #endif
+ if (page->capacity >= page->reserved) return;
+
+ size_t page_size;
+ //uint8_t* page_start =
+ _mi_page_start(_mi_page_segment(page), page, &page_size);
+ mi_stat_counter_increase(tld->stats.pages_extended, 1);
+
+ // calculate the extend count
+ const size_t bsize = (page->xblock_size < MI_HUGE_BLOCK_SIZE ? page->xblock_size : page_size);
+ size_t extend = page->reserved - page->capacity;
+ size_t max_extend = (bsize >= MI_MAX_EXTEND_SIZE ? MI_MIN_EXTEND : MI_MAX_EXTEND_SIZE/(uint32_t)bsize);
+ if (max_extend < MI_MIN_EXTEND) max_extend = MI_MIN_EXTEND;
+
+ if (extend > max_extend) {
+ // ensure we don't touch memory beyond the page to reduce page commit.
+ // the `lean` benchmark tests this. Going from 1 to 8 increases rss by 50%.
+ extend = (max_extend==0 ? 1 : max_extend);
+ }
+
+ mi_assert_internal(extend > 0 && extend + page->capacity <= page->reserved);
+ mi_assert_internal(extend < (1UL<<16));
+
+ // and append the extend the free list
+ if (extend < MI_MIN_SLICES || MI_SECURE==0) { //!mi_option_is_enabled(mi_option_secure)) {
+ mi_page_free_list_extend(page, bsize, extend, &tld->stats );
+ }
+ else {
+ mi_page_free_list_extend_secure(heap, page, bsize, extend, &tld->stats);
+ }
+ // enable the new free list
+ page->capacity += (uint16_t)extend;
+ mi_stat_increase(tld->stats.page_committed, extend * bsize);
+
+ // extension into zero initialized memory preserves the zero'd free list
+ if (!page->is_zero_init) {
+ page->is_zero = false;
+ }
+ mi_assert_expensive(mi_page_is_valid_init(page));
+}
+
+// Initialize a fresh page
+static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t block_size, mi_tld_t* tld) {
+ mi_assert(page != NULL);
+ mi_segment_t* segment = _mi_page_segment(page);
+ mi_assert(segment != NULL);
+ mi_assert_internal(block_size > 0);
+ // set fields
+ mi_page_set_heap(page, heap);
+ size_t page_size;
+ _mi_segment_page_start(segment, page, block_size, &page_size, NULL);
+ page->xblock_size = (block_size < MI_HUGE_BLOCK_SIZE ? (uint32_t)block_size : MI_HUGE_BLOCK_SIZE);
+ mi_assert_internal(page_size / block_size < (1L<<16));
+ page->reserved = (uint16_t)(page_size / block_size);
+ #ifdef MI_ENCODE_FREELIST
+ page->keys[0] = _mi_heap_random_next(heap);
+ page->keys[1] = _mi_heap_random_next(heap);
+ #endif
+ page->is_zero = page->is_zero_init;
+
+ mi_assert_internal(page->capacity == 0);
+ mi_assert_internal(page->free == NULL);
+ mi_assert_internal(page->used == 0);
+ mi_assert_internal(page->xthread_free == 0);
+ mi_assert_internal(page->next == NULL);
+ mi_assert_internal(page->prev == NULL);
+ mi_assert_internal(page->retire_expire == 0);
+ mi_assert_internal(!mi_page_has_aligned(page));
+ #if (MI_ENCODE_FREELIST)
+ mi_assert_internal(page->keys[0] != 0);
+ mi_assert_internal(page->keys[1] != 0);
+ #endif
+ mi_assert_expensive(mi_page_is_valid_init(page));
+
+ // initialize an initial free list
+ mi_page_extend_free(heap,page,tld);
+ mi_assert(mi_page_immediate_available(page));
+}
+
+
+/* -----------------------------------------------------------
+ Find pages with free blocks
+-------------------------------------------------------------*/
+
+// Find a page with free blocks of `page->block_size`.
+static mi_page_t* mi_page_queue_find_free_ex(mi_heap_t* heap, mi_page_queue_t* pq, bool first_try)
+{
+ // search through the pages in "next fit" order
+ size_t count = 0;
+ mi_page_t* page = pq->first;
+ while (page != NULL)
+ {
+ mi_page_t* next = page->next; // remember next
+ count++;
+
+ // 0. collect freed blocks by us and other threads
+ _mi_page_free_collect(page, false);
+
+ // 1. if the page contains free blocks, we are done
+ if (mi_page_immediate_available(page)) {
+ break; // pick this one
+ }
+
+ // 2. Try to extend
+ if (page->capacity < page->reserved) {
+ mi_page_extend_free(heap, page, heap->tld);
+ mi_assert_internal(mi_page_immediate_available(page));
+ break;
+ }
+
+ // 3. If the page is completely full, move it to the `mi_pages_full`
+ // queue so we don't visit long-lived pages too often.
+ mi_assert_internal(!mi_page_is_in_full(page) && !mi_page_immediate_available(page));
+ mi_page_to_full(page, pq);
+
+ page = next;
+ } // for each page
+
+ mi_stat_counter_increase(heap->tld->stats.searches, count);
+
+ if (page == NULL) {
+ _mi_heap_collect_retired(heap, false); // perhaps make a page available
+ page = mi_page_fresh(heap, pq);
+ if (page == NULL && first_try) {
+ // out-of-memory _or_ an abandoned page with free blocks was reclaimed, try once again
+ page = mi_page_queue_find_free_ex(heap, pq, false);
+ }
+ }
+ else {
+ mi_assert(pq->first == page);
+ page->retire_expire = 0;
+ }
+ mi_assert_internal(page == NULL || mi_page_immediate_available(page));
+ return page;
+}
+
+
+
+// Find a page with free blocks of `size`.
+static inline mi_page_t* mi_find_free_page(mi_heap_t* heap, size_t size) {
+ mi_page_queue_t* pq = mi_page_queue(heap,size);
+ mi_page_t* page = pq->first;
+ if (page != NULL) {
+ if ((MI_SECURE >= 3) && page->capacity < page->reserved && ((_mi_heap_random_next(heap) & 1) == 1)) {
+ // in secure mode, we extend half the time to increase randomness
+ mi_page_extend_free(heap, page, heap->tld);
+ mi_assert_internal(mi_page_immediate_available(page));
+ }
+ else {
+ _mi_page_free_collect(page,false);
+ }
+ if (mi_page_immediate_available(page)) {
+ page->retire_expire = 0;
+ return page; // fast path
+ }
+ }
+ return mi_page_queue_find_free_ex(heap, pq, true);
+}
+
+
+/* -----------------------------------------------------------
+ Users can register a deferred free function called
+ when the `free` list is empty. Since the `local_free`
+ is separate this is deterministically called after
+ a certain number of allocations.
+----------------------------------------------------------- */
+
+static mi_deferred_free_fun* volatile deferred_free = NULL;
+static volatile _Atomic(void*) deferred_arg; // = NULL
+
+void _mi_deferred_free(mi_heap_t* heap, bool force) {
+ heap->tld->heartbeat++;
+ if (deferred_free != NULL && !heap->tld->recurse) {
+ heap->tld->recurse = true;
+ deferred_free(force, heap->tld->heartbeat, mi_atomic_read_ptr_relaxed(void,&deferred_arg));
+ heap->tld->recurse = false;
+ }
+}
+
+void mi_register_deferred_free(mi_deferred_free_fun* fn, void* arg) mi_attr_noexcept {
+ deferred_free = fn;
+ mi_atomic_write_ptr(void,&deferred_arg, arg);
+}
+
+
+/* -----------------------------------------------------------
+ General allocation
+----------------------------------------------------------- */
+
+// A huge page is allocated directly without being in a queue.
+// Because huge pages contain just one block, and the segment contains
+// just that page, we always treat them as abandoned and any thread
+// that frees the block can free the whole page and segment directly.
+static mi_page_t* mi_huge_page_alloc(mi_heap_t* heap, size_t size) {
+ size_t block_size = _mi_os_good_alloc_size(size);
+ mi_assert_internal(_mi_bin(block_size) == MI_BIN_HUGE);
+ mi_page_t* page = mi_page_fresh_alloc(heap,NULL,block_size);
+ if (page != NULL) {
+ const size_t bsize = mi_page_block_size(page); // note: not `mi_page_usable_block_size` as `size` includes padding already
+ mi_assert_internal(bsize >= size);
+ mi_assert_internal(mi_page_immediate_available(page));
+ mi_assert_internal(_mi_page_segment(page)->page_kind==MI_PAGE_HUGE);
+ mi_assert_internal(_mi_page_segment(page)->used==1);
+ mi_assert_internal(_mi_page_segment(page)->thread_id==0); // abandoned, not in the huge queue
+ mi_page_set_heap(page, NULL);
+
+ if (bsize > MI_HUGE_OBJ_SIZE_MAX) {
+ _mi_stat_increase(&heap->tld->stats.giant, bsize);
+ _mi_stat_counter_increase(&heap->tld->stats.giant_count, 1);
+ }
+ else {
+ _mi_stat_increase(&heap->tld->stats.huge, bsize);
+ _mi_stat_counter_increase(&heap->tld->stats.huge_count, 1);
+ }
+ }
+ return page;
+}
+
+
+// Allocate a page
+// Note: in debug mode the size includes MI_PADDING_SIZE and might have overflowed.
+static mi_page_t* mi_find_page(mi_heap_t* heap, size_t size) mi_attr_noexcept {
+ // huge allocation?
+ const size_t req_size = size - MI_PADDING_SIZE; // correct for padding_size in case of an overflow on `size`
+ if (mi_unlikely(req_size > (MI_LARGE_OBJ_SIZE_MAX - MI_PADDING_SIZE) )) {
+ if (mi_unlikely(req_size > PTRDIFF_MAX)) { // we don't allocate more than PTRDIFF_MAX (see <https://sourceware.org/ml/libc-announce/2019/msg00001.html>)
+ _mi_error_message(EOVERFLOW, "allocation request is too large (%zu bytes)\n", req_size);
+ return NULL;
+ }
+ else {
+ return mi_huge_page_alloc(heap,size);
+ }
+ }
+ else {
+ // otherwise find a page with free blocks in our size segregated queues
+ mi_assert_internal(size >= MI_PADDING_SIZE);
+ return mi_find_free_page(heap, size);
+ }
+}
+
+// Generic allocation routine if the fast path (`alloc.c:mi_page_malloc`) does not succeed.
+// Note: in debug mode the size includes MI_PADDING_SIZE and might have overflowed.
+void* _mi_malloc_generic(mi_heap_t* heap, size_t size) mi_attr_noexcept
+{
+ mi_assert_internal(heap != NULL);
+
+ // initialize if necessary
+ if (mi_unlikely(!mi_heap_is_initialized(heap))) {
+ mi_thread_init(); // calls `_mi_heap_init` in turn
+ heap = mi_get_default_heap();
+ if (mi_unlikely(!mi_heap_is_initialized(heap))) { return NULL; }
+ }
+ mi_assert_internal(mi_heap_is_initialized(heap));
+
+ // call potential deferred free routines
+ _mi_deferred_free(heap, false);
+
+ // free delayed frees from other threads
+ _mi_heap_delayed_free(heap);
+
+ // find (or allocate) a page of the right size
+ mi_page_t* page = mi_find_page(heap, size);
+ if (mi_unlikely(page == NULL)) { // first time out of memory, try to collect and retry the allocation once more
+ mi_heap_collect(heap, true /* force */);
+ page = mi_find_page(heap, size);
+ }
+
+ if (mi_unlikely(page == NULL)) { // out of memory
+ const size_t req_size = size - MI_PADDING_SIZE; // correct for padding_size in case of an overflow on `size`
+ _mi_error_message(ENOMEM, "unable to allocate memory (%zu bytes)\n", req_size);
+ return NULL;
+ }
+
+ mi_assert_internal(mi_page_immediate_available(page));
+ mi_assert_internal(mi_page_block_size(page) >= size);
+
+ // and try again, this time succeeding! (i.e. this should never recurse)
+ return _mi_page_malloc(heap, page, size);
+}
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2019, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+
+#include <string.h> // memset
+
+/* ----------------------------------------------------------------------------
+We use our own PRNG to keep predictable performance of random number generation
+and to avoid implementations that use a lock. We only use the OS provided
+random source to initialize the initial seeds. Since we do not need ultimate
+performance but we do rely on the security (for secret cookies in secure mode)
+we use a cryptographically secure generator (chacha20).
+-----------------------------------------------------------------------------*/
+
+#define MI_CHACHA_ROUNDS (20) // perhaps use 12 for better performance?
+
+
+/* ----------------------------------------------------------------------------
+Chacha20 implementation as the original algorithm with a 64-bit nonce
+and counter: https://en.wikipedia.org/wiki/Salsa20
+The input matrix has sixteen 32-bit values:
+Position 0 to 3: constant key
+Position 4 to 11: the key
+Position 12 to 13: the counter.
+Position 14 to 15: the nonce.
+
+The implementation uses regular C code which compiles very well on modern compilers.
+(gcc x64 has no register spills, and clang 6+ uses SSE instructions)
+-----------------------------------------------------------------------------*/
+
+static inline uint32_t rotl(uint32_t x, uint32_t shift) {
+ return (x << shift) | (x >> (32 - shift));
+}
+
+static inline void qround(uint32_t x[16], size_t a, size_t b, size_t c, size_t d) {
+ x[a] += x[b]; x[d] = rotl(x[d] ^ x[a], 16);
+ x[c] += x[d]; x[b] = rotl(x[b] ^ x[c], 12);
+ x[a] += x[b]; x[d] = rotl(x[d] ^ x[a], 8);
+ x[c] += x[d]; x[b] = rotl(x[b] ^ x[c], 7);
+}
+
+static void chacha_block(mi_random_ctx_t* ctx)
+{
+ // scramble into `x`
+ uint32_t x[16];
+ for (size_t i = 0; i < 16; i++) {
+ x[i] = ctx->input[i];
+ }
+ for (size_t i = 0; i < MI_CHACHA_ROUNDS; i += 2) {
+ qround(x, 0, 4, 8, 12);
+ qround(x, 1, 5, 9, 13);
+ qround(x, 2, 6, 10, 14);
+ qround(x, 3, 7, 11, 15);
+ qround(x, 0, 5, 10, 15);
+ qround(x, 1, 6, 11, 12);
+ qround(x, 2, 7, 8, 13);
+ qround(x, 3, 4, 9, 14);
+ }
+
+ // add scrambled data to the initial state
+ for (size_t i = 0; i < 16; i++) {
+ ctx->output[i] = x[i] + ctx->input[i];
+ }
+ ctx->output_available = 16;
+
+ // increment the counter for the next round
+ ctx->input[12] += 1;
+ if (ctx->input[12] == 0) {
+ ctx->input[13] += 1;
+ if (ctx->input[13] == 0) { // and keep increasing into the nonce
+ ctx->input[14] += 1;
+ }
+ }
+}
+
+static uint32_t chacha_next32(mi_random_ctx_t* ctx) {
+ if (ctx->output_available <= 0) {
+ chacha_block(ctx);
+ ctx->output_available = 16; // (assign again to suppress static analysis warning)
+ }
+ const uint32_t x = ctx->output[16 - ctx->output_available];
+ ctx->output[16 - ctx->output_available] = 0; // reset once the data is handed out
+ ctx->output_available--;
+ return x;
+}
+
+static inline uint32_t read32(const uint8_t* p, size_t idx32) {
+ const size_t i = 4*idx32;
+ return ((uint32_t)p[i+0] | (uint32_t)p[i+1] << 8 | (uint32_t)p[i+2] << 16 | (uint32_t)p[i+3] << 24);
+}
+
+static void chacha_init(mi_random_ctx_t* ctx, const uint8_t key[32], uint64_t nonce)
+{
+ // since we only use chacha for randomness (and not encryption) we
+ // do not _need_ to read 32-bit values as little endian but we do anyways
+ // just for being compatible :-)
+ memset(ctx, 0, sizeof(*ctx));
+ for (size_t i = 0; i < 4; i++) {
+ const uint8_t* sigma = (uint8_t*)"expand 32-byte k";
+ ctx->input[i] = read32(sigma,i);
+ }
+ for (size_t i = 0; i < 8; i++) {
+ ctx->input[i + 4] = read32(key,i);
+ }
+ ctx->input[12] = 0;
+ ctx->input[13] = 0;
+ ctx->input[14] = (uint32_t)nonce;
+ ctx->input[15] = (uint32_t)(nonce >> 32);
+}
+
+static void chacha_split(mi_random_ctx_t* ctx, uint64_t nonce, mi_random_ctx_t* ctx_new) {
+ memset(ctx_new, 0, sizeof(*ctx_new));
+ memcpy(ctx_new->input, ctx->input, sizeof(ctx_new->input));
+ ctx_new->input[12] = 0;
+ ctx_new->input[13] = 0;
+ ctx_new->input[14] = (uint32_t)nonce;
+ ctx_new->input[15] = (uint32_t)(nonce >> 32);
+ mi_assert_internal(ctx->input[14] != ctx_new->input[14] || ctx->input[15] != ctx_new->input[15]); // do not reuse nonces!
+ chacha_block(ctx_new);
+}
+
+
+/* ----------------------------------------------------------------------------
+Random interface
+-----------------------------------------------------------------------------*/
+
+#if MI_DEBUG>1
+static bool mi_random_is_initialized(mi_random_ctx_t* ctx) {
+ return (ctx != NULL && ctx->input[0] != 0);
+}
+#endif
+
+void _mi_random_split(mi_random_ctx_t* ctx, mi_random_ctx_t* ctx_new) {
+ mi_assert_internal(mi_random_is_initialized(ctx));
+ mi_assert_internal(ctx != ctx_new);
+ chacha_split(ctx, (uintptr_t)ctx_new /*nonce*/, ctx_new);
+}
+
+uintptr_t _mi_random_next(mi_random_ctx_t* ctx) {
+ mi_assert_internal(mi_random_is_initialized(ctx));
+ #if MI_INTPTR_SIZE <= 4
+ return chacha_next32(ctx);
+ #elif MI_INTPTR_SIZE == 8
+ return (((uintptr_t)chacha_next32(ctx) << 32) | chacha_next32(ctx));
+ #else
+ # error "define mi_random_next for this platform"
+ #endif
+}
+
+
+/* ----------------------------------------------------------------------------
+To initialize a fresh random context we rely on the OS:
+- Windows : BCryptGenRandom
+- osX,bsd,wasi: arc4random_buf
+- Linux : getrandom,/dev/urandom
+If we cannot get good randomness, we fall back to weak randomness based on a timer and ASLR.
+-----------------------------------------------------------------------------*/
+
+#if defined(_WIN32)
+#pragma comment (lib,"bcrypt.lib")
+#include <bcrypt.h>
+static bool os_random_buf(void* buf, size_t buf_len) {
+ return (BCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)buf_len, BCRYPT_USE_SYSTEM_PREFERRED_RNG) >= 0);
+}
+/*
+#define SystemFunction036 NTAPI SystemFunction036
+#include <NTSecAPI.h>
+#undef SystemFunction036
+static bool os_random_buf(void* buf, size_t buf_len) {
+ RtlGenRandom(buf, (ULONG)buf_len);
+ return true;
+}
+*/
+#elif defined(ANDROID) || defined(XP_DARWIN) || defined(__APPLE__) || defined(__DragonFly__) || \
+ defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
+ defined(__sun) || defined(__wasi__)
+#include <stdlib.h>
+static bool os_random_buf(void* buf, size_t buf_len) {
+ arc4random_buf(buf, buf_len);
+ return true;
+}
+#elif defined(__linux__)
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+static bool os_random_buf(void* buf, size_t buf_len) {
+ // Modern Linux provides `getrandom` but different distributions either use `sys/random.h` or `linux/random.h`
+ // and for the latter the actual `getrandom` call is not always defined.
+ // (see <https://stackoverflow.com/questions/45237324/why-doesnt-getrandom-compile>)
+ // We therefore use a syscall directly and fall back dynamically to /dev/urandom when needed.
+#ifdef SYS_getrandom
+ #ifndef GRND_NONBLOCK
+ #define GRND_NONBLOCK (1)
+ #endif
+ static volatile _Atomic(uintptr_t) no_getrandom; // = 0
+ if (mi_atomic_read(&no_getrandom)==0) {
+ ssize_t ret = syscall(SYS_getrandom, buf, buf_len, GRND_NONBLOCK);
+ if (ret >= 0) return (buf_len == (size_t)ret);
+ if (ret != ENOSYS) return false;
+ mi_atomic_write(&no_getrandom,1); // don't call again, and fall back to /dev/urandom
+ }
+#endif
+ int flags = O_RDONLY;
+ #if defined(O_CLOEXEC)
+ flags |= O_CLOEXEC;
+ #endif
+ int fd = open("/dev/urandom", flags, 0);
+ if (fd < 0) return false;
+ size_t count = 0;
+ while(count < buf_len) {
+ ssize_t ret = read(fd, (char*)buf + count, buf_len - count);
+ if (ret<=0) {
+ if (errno!=EAGAIN && errno!=EINTR) break;
+ }
+ else {
+ count += ret;
+ }
+ }
+ close(fd);
+ return (count==buf_len);
+}
+#else
+static bool os_random_buf(void* buf, size_t buf_len) {
+ return false;
+}
+#endif
+
+#if defined(_WIN32)
+#include <windows.h>
+#elif defined(__APPLE__)
+#include <mach/mach_time.h>
+#else
+#include <time.h>
+#endif
+
+uintptr_t _os_random_weak(uintptr_t extra_seed) {
+ uintptr_t x = (uintptr_t)&_os_random_weak ^ extra_seed; // ASLR makes the address random
+ #if defined(_WIN32)
+ LARGE_INTEGER pcount;
+ QueryPerformanceCounter(&pcount);
+ x ^= (uintptr_t)(pcount.QuadPart);
+ #elif defined(__APPLE__)
+ x ^= (uintptr_t)mach_absolute_time();
+ #else
+ struct timespec time;
+ clock_gettime(CLOCK_MONOTONIC, &time);
+ x ^= (uintptr_t)time.tv_sec;
+ x ^= (uintptr_t)time.tv_nsec;
+ #endif
+ // and do a few randomization steps
+ uintptr_t max = ((x ^ (x >> 17)) & 0x0F) + 1;
+ for (uintptr_t i = 0; i < max; i++) {
+ x = _mi_random_shuffle(x);
+ }
+ mi_assert_internal(x != 0);
+ return x;
+}
+
+void _mi_random_init(mi_random_ctx_t* ctx) {
+ uint8_t key[32];
+ if (!os_random_buf(key, sizeof(key))) {
+ // if we fail to get random data from the OS, we fall back to a
+ // weak random source based on the current time
+ _mi_warning_message("unable to use secure randomness\n");
+ uintptr_t x = _os_random_weak(0);
+ for (size_t i = 0; i < 8; i++) { // key is eight 32-bit words.
+ x = _mi_random_shuffle(x);
+ ((uint32_t*)key)[i] = (uint32_t)x;
+ }
+ }
+ chacha_init(ctx, key, (uintptr_t)ctx /*nonce*/ );
+}
+
+/* --------------------------------------------------------
+test vectors from <https://tools.ietf.org/html/rfc8439>
+----------------------------------------------------------- */
+/*
+static bool array_equals(uint32_t* x, uint32_t* y, size_t n) {
+ for (size_t i = 0; i < n; i++) {
+ if (x[i] != y[i]) return false;
+ }
+ return true;
+}
+static void chacha_test(void)
+{
+ uint32_t x[4] = { 0x11111111, 0x01020304, 0x9b8d6f43, 0x01234567 };
+ uint32_t x_out[4] = { 0xea2a92f4, 0xcb1cf8ce, 0x4581472e, 0x5881c4bb };
+ qround(x, 0, 1, 2, 3);
+ mi_assert_internal(array_equals(x, x_out, 4));
+
+ uint32_t y[16] = {
+ 0x879531e0, 0xc5ecf37d, 0x516461b1, 0xc9a62f8a,
+ 0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0x2a5f714c,
+ 0x53372767, 0xb00a5631, 0x974c541a, 0x359e9963,
+ 0x5c971061, 0x3d631689, 0x2098d9d6, 0x91dbd320 };
+ uint32_t y_out[16] = {
+ 0x879531e0, 0xc5ecf37d, 0xbdb886dc, 0xc9a62f8a,
+ 0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0xcfacafd2,
+ 0xe46bea80, 0xb00a5631, 0x974c541a, 0x359e9963,
+ 0x5c971061, 0xccc07c79, 0x2098d9d6, 0x91dbd320 };
+ qround(y, 2, 7, 8, 13);
+ mi_assert_internal(array_equals(y, y_out, 16));
+
+ mi_random_ctx_t r = {
+ { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
+ 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c,
+ 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c,
+ 0x00000001, 0x09000000, 0x4a000000, 0x00000000 },
+ {0},
+ 0
+ };
+ uint32_t r_out[16] = {
+ 0xe4e7f110, 0x15593bd1, 0x1fdd0f50, 0xc47120a3,
+ 0xc7f4d1c7, 0x0368c033, 0x9aaa2204, 0x4e6cd4c3,
+ 0x466482d2, 0x09aa9f07, 0x05d7c214, 0xa2028bd9,
+ 0xd19c12b5, 0xb94e16de, 0xe883d0cb, 0x4e3c50a2 };
+ chacha_block(&r);
+ mi_assert_internal(array_equals(r.output, r_out, 16));
+}
+*/
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2019, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+/* ----------------------------------------------------------------------------
+This implements a layer between the raw OS memory (VirtualAlloc/mmap/sbrk/..)
+and the segment and huge object allocation by mimalloc. There may be multiple
+implementations of this (one could be the identity going directly to the OS,
+another could be a simple cache etc), but the current one uses large "regions".
+In contrast to the rest of mimalloc, the "regions" are shared between threads and
+need to be accessed using atomic operations.
+We need this memory layer between the raw OS calls because of:
+1. on `sbrk` like systems (like WebAssembly) we need our own memory maps in order
+ to reuse memory effectively.
+2. It turns out that for large objects, between 1MiB and 32MiB (?), the cost of
+ an OS allocation/free is still (much) too expensive relative to the accesses
+ in that object :-( (`malloc-large` tests this). This means we need a cheaper
+ way to reuse memory.
+3. This layer allows for NUMA aware allocation.
+
+Possible issues:
+- (2) can potentially be addressed too with a small cache per thread which is much
+ simpler. Generally though that requires shrinking of huge pages, and may overuse
+ memory per thread. (and is not compatible with `sbrk`).
+- Since the current regions are per-process, we need atomic operations to
+ claim blocks which may be contended
+- In the worst case, we need to search the whole region map (16KiB for 256GiB)
+ linearly. At what point will direct OS calls be faster? Is there a way to
+ do this better without adding too much complexity?
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+#include "mimalloc-atomic.h"
+
+#include <string.h> // memset
+
+#include "bitmap.inc.c"
+
+// Internal raw OS interface
+size_t _mi_os_large_page_size();
+bool _mi_os_protect(void* addr, size_t size);
+bool _mi_os_unprotect(void* addr, size_t size);
+bool _mi_os_commit(void* p, size_t size, bool* is_zero, mi_stats_t* stats);
+bool _mi_os_decommit(void* p, size_t size, mi_stats_t* stats);
+bool _mi_os_reset(void* p, size_t size, mi_stats_t* stats);
+bool _mi_os_unreset(void* p, size_t size, bool* is_zero, mi_stats_t* stats);
+
+// arena.c
+void _mi_arena_free(void* p, size_t size, size_t memid, bool all_committed, mi_stats_t* stats);
+void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_zero, size_t* memid, mi_os_tld_t* tld);
+void* _mi_arena_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_zero, size_t* memid, mi_os_tld_t* tld);
+
+
+
+// Constants
+#if (MI_INTPTR_SIZE==8)
+#define MI_HEAP_REGION_MAX_SIZE (256 * GiB) // 64KiB for the region map
+#elif (MI_INTPTR_SIZE==4)
+#define MI_HEAP_REGION_MAX_SIZE (3 * GiB) // ~ KiB for the region map
+#else
+#error "define the maximum heap space allowed for regions on this platform"
+#endif
+
+#define MI_SEGMENT_ALIGN MI_SEGMENT_SIZE
+
+#define MI_REGION_MAX_BLOCKS MI_BITMAP_FIELD_BITS
+#define MI_REGION_SIZE (MI_SEGMENT_SIZE * MI_BITMAP_FIELD_BITS) // 256MiB (64MiB on 32 bits)
+#define MI_REGION_MAX (MI_HEAP_REGION_MAX_SIZE / MI_REGION_SIZE) // 1024 (48 on 32 bits)
+#define MI_REGION_MAX_OBJ_BLOCKS (MI_REGION_MAX_BLOCKS/4) // 64MiB
+#define MI_REGION_MAX_OBJ_SIZE (MI_REGION_MAX_OBJ_BLOCKS*MI_SEGMENT_SIZE)
+
+// Region info
+typedef union mi_region_info_u {
+ uintptr_t value;
+ struct {
+ bool valid; // initialized?
+ bool is_large; // allocated in fixed large/huge OS pages
+ short numa_node; // the associated NUMA node (where -1 means no associated node)
+ } x;
+} mi_region_info_t;
+
+
+// A region owns a chunk of REGION_SIZE (256MiB) (virtual) memory with
+// a bit map with one bit per MI_SEGMENT_SIZE (4MiB) block.
+typedef struct mem_region_s {
+ volatile _Atomic(uintptr_t) info; // mi_region_info_t.value
+ volatile _Atomic(void*) start; // start of the memory area
+ mi_bitmap_field_t in_use; // bit per in-use block
+ mi_bitmap_field_t dirty; // track if non-zero per block
+ mi_bitmap_field_t commit; // track if committed per block
+ mi_bitmap_field_t reset; // track if reset per block
+ volatile _Atomic(uintptr_t) arena_memid; // if allocated from a (huge page) arena
+ uintptr_t padding; // round to 8 fields
+} mem_region_t;
+
+// The region map
+static mem_region_t regions[MI_REGION_MAX];
+
+// Allocated regions
+static volatile _Atomic(uintptr_t) regions_count; // = 0;
+
+
+/* ----------------------------------------------------------------------------
+Utility functions
+-----------------------------------------------------------------------------*/
+
+// Blocks (of 4MiB) needed for the given size.
+static size_t mi_region_block_count(size_t size) {
+ return _mi_divide_up(size, MI_SEGMENT_SIZE);
+}
+
+/*
+// Return a rounded commit/reset size such that we don't fragment large OS pages into small ones.
+static size_t mi_good_commit_size(size_t size) {
+ if (size > (SIZE_MAX - _mi_os_large_page_size())) return size;
+ return _mi_align_up(size, _mi_os_large_page_size());
+}
+*/
+
+// Return if a pointer points into a region reserved by us.
+bool mi_is_in_heap_region(const void* p) mi_attr_noexcept {
+ if (p==NULL) return false;
+ size_t count = mi_atomic_read_relaxed(®ions_count);
+ for (size_t i = 0; i < count; i++) {
+ uint8_t* start = mi_atomic_read_ptr_relaxed(uint8_t,®ions[i].start);
+ if (start != NULL && (uint8_t*)p >= start && (uint8_t*)p < start + MI_REGION_SIZE) return true;
+ }
+ return false;
+}
+
+
+static void* mi_region_blocks_start(const mem_region_t* region, mi_bitmap_index_t bit_idx) {
+ uint8_t* start = mi_atomic_read_ptr(uint8_t,®ion->start);
+ mi_assert_internal(start != NULL);
+ return (start + (bit_idx * MI_SEGMENT_SIZE));
+}
+
+static size_t mi_memid_create(mem_region_t* region, mi_bitmap_index_t bit_idx) {
+ mi_assert_internal(bit_idx < MI_BITMAP_FIELD_BITS);
+ size_t idx = region - regions;
+ mi_assert_internal(®ions[idx] == region);
+ return (idx*MI_BITMAP_FIELD_BITS + bit_idx)<<1;
+}
+
+static size_t mi_memid_create_from_arena(size_t arena_memid) {
+ return (arena_memid << 1) | 1;
+}
+
+
+static bool mi_memid_is_arena(size_t id, mem_region_t** region, mi_bitmap_index_t* bit_idx, size_t* arena_memid) {
+ if ((id&1)==1) {
+ if (arena_memid != NULL) *arena_memid = (id>>1);
+ return true;
+ }
+ else {
+ size_t idx = (id >> 1) / MI_BITMAP_FIELD_BITS;
+ *bit_idx = (mi_bitmap_index_t)(id>>1) % MI_BITMAP_FIELD_BITS;
+ *region = ®ions[idx];
+ return false;
+ }
+}
+
+
+/* ----------------------------------------------------------------------------
+ Allocate a region is allocated from the OS (or an arena)
+-----------------------------------------------------------------------------*/
+
+static bool mi_region_try_alloc_os(size_t blocks, bool commit, bool allow_large, mem_region_t** region, mi_bitmap_index_t* bit_idx, mi_os_tld_t* tld)
+{
+ // not out of regions yet?
+ if (mi_atomic_read_relaxed(®ions_count) >= MI_REGION_MAX - 1) return false;
+
+ // try to allocate a fresh region from the OS
+ bool region_commit = (commit && mi_option_is_enabled(mi_option_eager_region_commit));
+ bool region_large = (commit && allow_large);
+ bool is_zero = false;
+ size_t arena_memid = 0;
+ void* const start = _mi_arena_alloc_aligned(MI_REGION_SIZE, MI_SEGMENT_ALIGN, ®ion_commit, ®ion_large, &is_zero, &arena_memid, tld);
+ if (start == NULL) return false;
+ mi_assert_internal(!(region_large && !allow_large));
+ mi_assert_internal(!region_large || region_commit);
+
+ // claim a fresh slot
+ const uintptr_t idx = mi_atomic_increment(®ions_count);
+ if (idx >= MI_REGION_MAX) {
+ mi_atomic_decrement(®ions_count);
+ _mi_arena_free(start, MI_REGION_SIZE, arena_memid, region_commit, tld->stats);
+ _mi_warning_message("maximum regions used: %zu GiB (perhaps recompile with a larger setting for MI_HEAP_REGION_MAX_SIZE)", _mi_divide_up(MI_HEAP_REGION_MAX_SIZE, GiB));
+ return false;
+ }
+
+ // allocated, initialize and claim the initial blocks
+ mem_region_t* r = ®ions[idx];
+ r->arena_memid = arena_memid;
+ mi_atomic_write(&r->in_use, 0);
+ mi_atomic_write(&r->dirty, (is_zero ? 0 : MI_BITMAP_FIELD_FULL));
+ mi_atomic_write(&r->commit, (region_commit ? MI_BITMAP_FIELD_FULL : 0));
+ mi_atomic_write(&r->reset, 0);
+ *bit_idx = 0;
+ mi_bitmap_claim(&r->in_use, 1, blocks, *bit_idx, NULL);
+ mi_atomic_write_ptr(uint8_t*,&r->start, start);
+
+ // and share it
+ mi_region_info_t info;
+ info.value = 0; // initialize the full union to zero
+ info.x.valid = true;
+ info.x.is_large = region_large;
+ info.x.numa_node = (short)_mi_os_numa_node(tld);
+ mi_atomic_write(&r->info, info.value); // now make it available to others
+ *region = r;
+ return true;
+}
+
+/* ----------------------------------------------------------------------------
+ Try to claim blocks in suitable regions
+-----------------------------------------------------------------------------*/
+
+static bool mi_region_is_suitable(const mem_region_t* region, int numa_node, bool allow_large ) {
+ // initialized at all?
+ mi_region_info_t info;
+ info.value = mi_atomic_read_relaxed(®ion->info);
+ if (info.value==0) return false;
+
+ // numa correct
+ if (numa_node >= 0) { // use negative numa node to always succeed
+ int rnode = info.x.numa_node;
+ if (rnode >= 0 && rnode != numa_node) return false;
+ }
+
+ // check allow-large
+ if (!allow_large && info.x.is_large) return false;
+
+ return true;
+}
+
+
+static bool mi_region_try_claim(int numa_node, size_t blocks, bool allow_large, mem_region_t** region, mi_bitmap_index_t* bit_idx, mi_os_tld_t* tld)
+{
+ // try all regions for a free slot
+ const size_t count = mi_atomic_read(®ions_count);
+ size_t idx = tld->region_idx; // Or start at 0 to reuse low addresses? Starting at 0 seems to increase latency though
+ for (size_t visited = 0; visited < count; visited++, idx++) {
+ if (idx >= count) idx = 0; // wrap around
+ mem_region_t* r = ®ions[idx];
+ // if this region suits our demand (numa node matches, large OS page matches)
+ if (mi_region_is_suitable(r, numa_node, allow_large)) {
+ // then try to atomically claim a segment(s) in this region
+ if (mi_bitmap_try_find_claim_field(&r->in_use, 0, blocks, bit_idx)) {
+ tld->region_idx = idx; // remember the last found position
+ *region = r;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+static void* mi_region_try_alloc(size_t blocks, bool* commit, bool* is_large, bool* is_zero, size_t* memid, mi_os_tld_t* tld)
+{
+ mi_assert_internal(blocks <= MI_BITMAP_FIELD_BITS);
+ mem_region_t* region;
+ mi_bitmap_index_t bit_idx;
+ const int numa_node = (_mi_os_numa_node_count() <= 1 ? -1 : _mi_os_numa_node(tld));
+ // try to claim in existing regions
+ if (!mi_region_try_claim(numa_node, blocks, *is_large, ®ion, &bit_idx, tld)) {
+ // otherwise try to allocate a fresh region and claim in there
+ if (!mi_region_try_alloc_os(blocks, *commit, *is_large, ®ion, &bit_idx, tld)) {
+ // out of regions or memory
+ return NULL;
+ }
+ }
+
+ // ------------------------------------------------
+ // found a region and claimed `blocks` at `bit_idx`, initialize them now
+ mi_assert_internal(region != NULL);
+ mi_assert_internal(mi_bitmap_is_claimed(®ion->in_use, 1, blocks, bit_idx));
+
+ mi_region_info_t info;
+ info.value = mi_atomic_read(®ion->info);
+ uint8_t* start = mi_atomic_read_ptr(uint8_t,®ion->start);
+ mi_assert_internal(!(info.x.is_large && !*is_large));
+ mi_assert_internal(start != NULL);
+
+ *is_zero = mi_bitmap_claim(®ion->dirty, 1, blocks, bit_idx, NULL);
+ *is_large = info.x.is_large;
+ *memid = mi_memid_create(region, bit_idx);
+ void* p = start + (mi_bitmap_index_bit_in_field(bit_idx) * MI_SEGMENT_SIZE);
+
+ // commit
+ if (*commit) {
+ // ensure commit
+ bool any_uncommitted;
+ mi_bitmap_claim(®ion->commit, 1, blocks, bit_idx, &any_uncommitted);
+ if (any_uncommitted) {
+ mi_assert_internal(!info.x.is_large);
+ bool commit_zero;
+ _mi_mem_commit(p, blocks * MI_SEGMENT_SIZE, &commit_zero, tld);
+ if (commit_zero) *is_zero = true;
+ }
+ }
+ else {
+ // no need to commit, but check if already fully committed
+ *commit = mi_bitmap_is_claimed(®ion->commit, 1, blocks, bit_idx);
+ }
+ mi_assert_internal(!*commit || mi_bitmap_is_claimed(®ion->commit, 1, blocks, bit_idx));
+
+ // unreset reset blocks
+ if (mi_bitmap_is_any_claimed(®ion->reset, 1, blocks, bit_idx)) {
+ // some blocks are still reset
+ mi_assert_internal(!info.x.is_large);
+ mi_assert_internal(!mi_option_is_enabled(mi_option_eager_commit) || *commit || mi_option_get(mi_option_eager_commit_delay) > 0);
+ mi_bitmap_unclaim(®ion->reset, 1, blocks, bit_idx);
+ if (*commit || !mi_option_is_enabled(mi_option_reset_decommits)) { // only if needed
+ bool reset_zero = false;
+ _mi_mem_unreset(p, blocks * MI_SEGMENT_SIZE, &reset_zero, tld);
+ if (reset_zero) *is_zero = true;
+ }
+ }
+ mi_assert_internal(!mi_bitmap_is_any_claimed(®ion->reset, 1, blocks, bit_idx));
+
+ #if (MI_DEBUG>=2)
+ if (*commit) { ((uint8_t*)p)[0] = 0; }
+ #endif
+
+ // and return the allocation
+ mi_assert_internal(p != NULL);
+ return p;
+}
+
+
+/* ----------------------------------------------------------------------------
+ Allocation
+-----------------------------------------------------------------------------*/
+
+// Allocate `size` memory aligned at `alignment`. Return non NULL on success, with a given memory `id`.
+// (`id` is abstract, but `id = idx*MI_REGION_MAP_BITS + bitidx`)
+void* _mi_mem_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_zero, size_t* memid, mi_os_tld_t* tld)
+{
+ mi_assert_internal(memid != NULL && tld != NULL);
+ mi_assert_internal(size > 0);
+ *memid = 0;
+ *is_zero = false;
+ bool default_large = false;
+ if (large==NULL) large = &default_large; // ensure `large != NULL`
+ if (size == 0) return NULL;
+ size = _mi_align_up(size, _mi_os_page_size());
+
+ // allocate from regions if possible
+ void* p = NULL;
+ size_t arena_memid;
+ const size_t blocks = mi_region_block_count(size);
+ if (blocks <= MI_REGION_MAX_OBJ_BLOCKS && alignment <= MI_SEGMENT_ALIGN) {
+ p = mi_region_try_alloc(blocks, commit, large, is_zero, memid, tld);
+ if (p == NULL) {
+ _mi_warning_message("unable to allocate from region: size %zu\n", size);
+ }
+ }
+ if (p == NULL) {
+ // and otherwise fall back to the OS
+ p = _mi_arena_alloc_aligned(size, alignment, commit, large, is_zero, &arena_memid, tld);
+ *memid = mi_memid_create_from_arena(arena_memid);
+ }
+
+ if (p != NULL) {
+ mi_assert_internal((uintptr_t)p % alignment == 0);
+#if (MI_DEBUG>=2)
+ if (*commit) { ((uint8_t*)p)[0] = 0; } // ensure the memory is committed
+#endif
+ }
+ return p;
+}
+
+
+
+/* ----------------------------------------------------------------------------
+Free
+-----------------------------------------------------------------------------*/
+
+// Free previously allocated memory with a given id.
+void _mi_mem_free(void* p, size_t size, size_t id, bool full_commit, bool any_reset, mi_os_tld_t* tld) {
+ mi_assert_internal(size > 0 && tld != NULL);
+ if (p==NULL) return;
+ if (size==0) return;
+ size = _mi_align_up(size, _mi_os_page_size());
+
+ size_t arena_memid = 0;
+ mi_bitmap_index_t bit_idx;
+ mem_region_t* region;
+ if (mi_memid_is_arena(id,®ion,&bit_idx,&arena_memid)) {
+ // was a direct arena allocation, pass through
+ _mi_arena_free(p, size, arena_memid, full_commit, tld->stats);
+ }
+ else {
+ // allocated in a region
+ mi_assert_internal(size <= MI_REGION_MAX_OBJ_SIZE); if (size > MI_REGION_MAX_OBJ_SIZE) return;
+ const size_t blocks = mi_region_block_count(size);
+ mi_assert_internal(blocks + bit_idx <= MI_BITMAP_FIELD_BITS);
+ mi_region_info_t info;
+ info.value = mi_atomic_read(®ion->info);
+ mi_assert_internal(info.value != 0);
+ void* blocks_start = mi_region_blocks_start(region, bit_idx);
+ mi_assert_internal(blocks_start == p); // not a pointer in our area?
+ mi_assert_internal(bit_idx + blocks <= MI_BITMAP_FIELD_BITS);
+ if (blocks_start != p || bit_idx + blocks > MI_BITMAP_FIELD_BITS) return; // or `abort`?
+
+ // committed?
+ if (full_commit && (size % MI_SEGMENT_SIZE) == 0) {
+ mi_bitmap_claim(®ion->commit, 1, blocks, bit_idx, NULL);
+ }
+
+ if (any_reset) {
+ // set the is_reset bits if any pages were reset
+ mi_bitmap_claim(®ion->reset, 1, blocks, bit_idx, NULL);
+ }
+
+ // reset the blocks to reduce the working set.
+ if (!info.x.is_large && mi_option_is_enabled(mi_option_segment_reset)
+ && (mi_option_is_enabled(mi_option_eager_commit) ||
+ mi_option_is_enabled(mi_option_reset_decommits))) // cannot reset halfway committed segments, use only `option_page_reset` instead
+ {
+ bool any_unreset;
+ mi_bitmap_claim(®ion->reset, 1, blocks, bit_idx, &any_unreset);
+ if (any_unreset) {
+ _mi_abandoned_await_readers(); // ensure no more pending write (in case reset = decommit)
+ _mi_mem_reset(p, blocks * MI_SEGMENT_SIZE, tld);
+ }
+ }
+
+ // and unclaim
+ bool all_unclaimed = mi_bitmap_unclaim(®ion->in_use, 1, blocks, bit_idx);
+ mi_assert_internal(all_unclaimed); UNUSED(all_unclaimed);
+ }
+}
+
+
+/* ----------------------------------------------------------------------------
+ collection
+-----------------------------------------------------------------------------*/
+void _mi_mem_collect(mi_os_tld_t* tld) {
+ // free every region that has no segments in use.
+ uintptr_t rcount = mi_atomic_read_relaxed(®ions_count);
+ for (size_t i = 0; i < rcount; i++) {
+ mem_region_t* region = ®ions[i];
+ if (mi_atomic_read_relaxed(®ion->info) != 0) {
+ // if no segments used, try to claim the whole region
+ uintptr_t m;
+ do {
+ m = mi_atomic_read_relaxed(®ion->in_use);
+ } while(m == 0 && !mi_atomic_cas_weak(®ion->in_use, MI_BITMAP_FIELD_FULL, 0 ));
+ if (m == 0) {
+ // on success, free the whole region
+ uint8_t* start = mi_atomic_read_ptr(uint8_t,®ions[i].start);
+ size_t arena_memid = mi_atomic_read_relaxed(®ions[i].arena_memid);
+ uintptr_t commit = mi_atomic_read_relaxed(®ions[i].commit);
+ memset(®ions[i], 0, sizeof(mem_region_t));
+ // and release the whole region
+ mi_atomic_write(®ion->info, 0);
+ if (start != NULL) { // && !_mi_os_is_huge_reserved(start)) {
+ _mi_abandoned_await_readers(); // ensure no pending reads
+ _mi_arena_free(start, MI_REGION_SIZE, arena_memid, (~commit == 0), tld->stats);
+ }
+ }
+ }
+ }
+}
+
+
+/* ----------------------------------------------------------------------------
+ Other
+-----------------------------------------------------------------------------*/
+
+bool _mi_mem_reset(void* p, size_t size, mi_os_tld_t* tld) {
+ return _mi_os_reset(p, size, tld->stats);
+}
+
+bool _mi_mem_unreset(void* p, size_t size, bool* is_zero, mi_os_tld_t* tld) {
+ return _mi_os_unreset(p, size, is_zero, tld->stats);
+}
+
+bool _mi_mem_commit(void* p, size_t size, bool* is_zero, mi_os_tld_t* tld) {
+ return _mi_os_commit(p, size, is_zero, tld->stats);
+}
+
+bool _mi_mem_decommit(void* p, size_t size, mi_os_tld_t* tld) {
+ return _mi_os_decommit(p, size, tld->stats);
+}
+
+bool _mi_mem_protect(void* p, size_t size) {
+ return _mi_os_protect(p, size);
+}
+
+bool _mi_mem_unprotect(void* p, size_t size) {
+ return _mi_os_unprotect(p, size);
+}
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+#include "mimalloc-atomic.h"
+
+#include <string.h> // memset
+#include <stdio.h>
+
+#define MI_PAGE_HUGE_ALIGN (256*1024)
+
+static uint8_t* mi_segment_raw_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size);
+
+/* --------------------------------------------------------------------------------
+ Segment allocation
+ We allocate pages inside bigger "segments" (4mb on 64-bit). This is to avoid
+ splitting VMA's on Linux and reduce fragmentation on other OS's.
+ Each thread owns its own segments.
+
+ Currently we have:
+ - small pages (64kb), 64 in one segment
+ - medium pages (512kb), 8 in one segment
+ - large pages (4mb), 1 in one segment
+ - huge blocks > MI_LARGE_OBJ_SIZE_MAX become large segment with 1 page
+
+ In any case the memory for a segment is virtual and usually committed on demand.
+ (i.e. we are careful to not touch the memory until we actually allocate a block there)
+
+ If a thread ends, it "abandons" pages with used blocks
+ and there is an abandoned segment list whose segments can
+ be reclaimed by still running threads, much like work-stealing.
+-------------------------------------------------------------------------------- */
+
+
+/* -----------------------------------------------------------
+ Queue of segments containing free pages
+----------------------------------------------------------- */
+
+#if (MI_DEBUG>=3)
+static bool mi_segment_queue_contains(const mi_segment_queue_t* queue, const mi_segment_t* segment) {
+ mi_assert_internal(segment != NULL);
+ mi_segment_t* list = queue->first;
+ while (list != NULL) {
+ if (list == segment) break;
+ mi_assert_internal(list->next==NULL || list->next->prev == list);
+ mi_assert_internal(list->prev==NULL || list->prev->next == list);
+ list = list->next;
+ }
+ return (list == segment);
+}
+#endif
+
+static bool mi_segment_queue_is_empty(const mi_segment_queue_t* queue) {
+ return (queue->first == NULL);
+}
+
+static void mi_segment_queue_remove(mi_segment_queue_t* queue, mi_segment_t* segment) {
+ mi_assert_expensive(mi_segment_queue_contains(queue, segment));
+ if (segment->prev != NULL) segment->prev->next = segment->next;
+ if (segment->next != NULL) segment->next->prev = segment->prev;
+ if (segment == queue->first) queue->first = segment->next;
+ if (segment == queue->last) queue->last = segment->prev;
+ segment->next = NULL;
+ segment->prev = NULL;
+}
+
+static void mi_segment_enqueue(mi_segment_queue_t* queue, mi_segment_t* segment) {
+ mi_assert_expensive(!mi_segment_queue_contains(queue, segment));
+ segment->next = NULL;
+ segment->prev = queue->last;
+ if (queue->last != NULL) {
+ mi_assert_internal(queue->last->next == NULL);
+ queue->last->next = segment;
+ queue->last = segment;
+ }
+ else {
+ queue->last = queue->first = segment;
+ }
+}
+
+static mi_segment_queue_t* mi_segment_free_queue_of_kind(mi_page_kind_t kind, mi_segments_tld_t* tld) {
+ if (kind == MI_PAGE_SMALL) return &tld->small_free;
+ else if (kind == MI_PAGE_MEDIUM) return &tld->medium_free;
+ else return NULL;
+}
+
+static mi_segment_queue_t* mi_segment_free_queue(const mi_segment_t* segment, mi_segments_tld_t* tld) {
+ return mi_segment_free_queue_of_kind(segment->page_kind, tld);
+}
+
+// remove from free queue if it is in one
+static void mi_segment_remove_from_free_queue(mi_segment_t* segment, mi_segments_tld_t* tld) {
+ mi_segment_queue_t* queue = mi_segment_free_queue(segment, tld); // may be NULL
+ bool in_queue = (queue!=NULL && (segment->next != NULL || segment->prev != NULL || queue->first == segment));
+ if (in_queue) {
+ mi_segment_queue_remove(queue, segment);
+ }
+}
+
+static void mi_segment_insert_in_free_queue(mi_segment_t* segment, mi_segments_tld_t* tld) {
+ mi_segment_enqueue(mi_segment_free_queue(segment, tld), segment);
+}
+
+
+/* -----------------------------------------------------------
+ Invariant checking
+----------------------------------------------------------- */
+
+#if (MI_DEBUG>=2)
+static bool mi_segment_is_in_free_queue(const mi_segment_t* segment, mi_segments_tld_t* tld) {
+ mi_segment_queue_t* queue = mi_segment_free_queue(segment, tld);
+ bool in_queue = (queue!=NULL && (segment->next != NULL || segment->prev != NULL || queue->first == segment));
+ if (in_queue) {
+ mi_assert_expensive(mi_segment_queue_contains(queue, segment));
+ }
+ return in_queue;
+}
+#endif
+
+static size_t mi_segment_page_size(const mi_segment_t* segment) {
+ if (segment->capacity > 1) {
+ mi_assert_internal(segment->page_kind <= MI_PAGE_MEDIUM);
+ return ((size_t)1 << segment->page_shift);
+ }
+ else {
+ mi_assert_internal(segment->page_kind >= MI_PAGE_LARGE);
+ return segment->segment_size;
+ }
+}
+
+
+#if (MI_DEBUG>=2)
+static bool mi_pages_reset_contains(const mi_page_t* page, mi_segments_tld_t* tld) {
+ mi_page_t* p = tld->pages_reset.first;
+ while (p != NULL) {
+ if (p == page) return true;
+ p = p->next;
+ }
+ return false;
+}
+#endif
+
+#if (MI_DEBUG>=3)
+static bool mi_segment_is_valid(const mi_segment_t* segment, mi_segments_tld_t* tld) {
+ mi_assert_internal(segment != NULL);
+ mi_assert_internal(_mi_ptr_cookie(segment) == segment->cookie);
+ mi_assert_internal(segment->used <= segment->capacity);
+ mi_assert_internal(segment->abandoned <= segment->used);
+ size_t nfree = 0;
+ for (size_t i = 0; i < segment->capacity; i++) {
+ const mi_page_t* const page = &segment->pages[i];
+ if (!page->segment_in_use) {
+ nfree++;
+ }
+ if (page->segment_in_use || page->is_reset) {
+ mi_assert_expensive(!mi_pages_reset_contains(page, tld));
+ }
+ }
+ mi_assert_internal(nfree + segment->used == segment->capacity);
+ // mi_assert_internal(segment->thread_id == _mi_thread_id() || (segment->thread_id==0)); // or 0
+ mi_assert_internal(segment->page_kind == MI_PAGE_HUGE ||
+ (mi_segment_page_size(segment) * segment->capacity == segment->segment_size));
+ return true;
+}
+#endif
+
+static bool mi_page_not_in_queue(const mi_page_t* page, mi_segments_tld_t* tld) {
+ mi_assert_internal(page != NULL);
+ if (page->next != NULL || page->prev != NULL) {
+ mi_assert_internal(mi_pages_reset_contains(page, tld));
+ return false;
+ }
+ else {
+ // both next and prev are NULL, check for singleton list
+ return (tld->pages_reset.first != page && tld->pages_reset.last != page);
+ }
+}
+
+
+/* -----------------------------------------------------------
+ Guard pages
+----------------------------------------------------------- */
+
+static void mi_segment_protect_range(void* p, size_t size, bool protect) {
+ if (protect) {
+ _mi_mem_protect(p, size);
+ }
+ else {
+ _mi_mem_unprotect(p, size);
+ }
+}
+
+static void mi_segment_protect(mi_segment_t* segment, bool protect, mi_os_tld_t* tld) {
+ // add/remove guard pages
+ if (MI_SECURE != 0) {
+ // in secure mode, we set up a protected page in between the segment info and the page data
+ const size_t os_page_size = _mi_os_page_size();
+ mi_assert_internal((segment->segment_info_size - os_page_size) >= (sizeof(mi_segment_t) + ((segment->capacity - 1) * sizeof(mi_page_t))));
+ mi_assert_internal(((uintptr_t)segment + segment->segment_info_size) % os_page_size == 0);
+ mi_segment_protect_range((uint8_t*)segment + segment->segment_info_size - os_page_size, os_page_size, protect);
+ if (MI_SECURE <= 1 || segment->capacity == 1) {
+ // and protect the last (or only) page too
+ mi_assert_internal(MI_SECURE <= 1 || segment->page_kind >= MI_PAGE_LARGE);
+ uint8_t* start = (uint8_t*)segment + segment->segment_size - os_page_size;
+ if (protect && !segment->mem_is_committed) {
+ // ensure secure page is committed
+ _mi_mem_commit(start, os_page_size, NULL, tld);
+ }
+ mi_segment_protect_range(start, os_page_size, protect);
+ }
+ else {
+ // or protect every page
+ const size_t page_size = mi_segment_page_size(segment);
+ for (size_t i = 0; i < segment->capacity; i++) {
+ if (segment->pages[i].is_committed) {
+ mi_segment_protect_range((uint8_t*)segment + (i+1)*page_size - os_page_size, os_page_size, protect);
+ }
+ }
+ }
+ }
+}
+
+/* -----------------------------------------------------------
+ Page reset
+----------------------------------------------------------- */
+
+static void mi_page_reset(mi_segment_t* segment, mi_page_t* page, size_t size, mi_segments_tld_t* tld) {
+ mi_assert_internal(page->is_committed);
+ if (!mi_option_is_enabled(mi_option_page_reset)) return;
+ if (segment->mem_is_fixed || page->segment_in_use || !page->is_committed || page->is_reset) return;
+ size_t psize;
+ void* start = mi_segment_raw_page_start(segment, page, &psize);
+ page->is_reset = true;
+ mi_assert_internal(size <= psize);
+ size_t reset_size = ((size == 0 || size > psize) ? psize : size);
+ if (reset_size > 0) _mi_mem_reset(start, reset_size, tld->os);
+}
+
+static bool mi_page_unreset(mi_segment_t* segment, mi_page_t* page, size_t size, mi_segments_tld_t* tld)
+{
+ mi_assert_internal(page->is_reset);
+ mi_assert_internal(page->is_committed);
+ mi_assert_internal(!segment->mem_is_fixed);
+ if (segment->mem_is_fixed || !page->is_committed || !page->is_reset) return true;
+ page->is_reset = false;
+ size_t psize;
+ uint8_t* start = mi_segment_raw_page_start(segment, page, &psize);
+ size_t unreset_size = (size == 0 || size > psize ? psize : size);
+ bool is_zero = false;
+ bool ok = true;
+ if (unreset_size > 0) {
+ ok = _mi_mem_unreset(start, unreset_size, &is_zero, tld->os);
+ }
+ if (is_zero) page->is_zero_init = true;
+ return ok;
+}
+
+
+/* -----------------------------------------------------------
+ The free page queue
+----------------------------------------------------------- */
+
+// we re-use the `used` field for the expiration counter. Since this is a
+// a 32-bit field while the clock is always 64-bit we need to guard
+// against overflow, we use substraction to check for expiry which work
+// as long as the reset delay is under (2^30 - 1) milliseconds (~12 days)
+static void mi_page_reset_set_expire(mi_page_t* page) {
+ uint32_t expire = (uint32_t)_mi_clock_now() + mi_option_get(mi_option_reset_delay);
+ page->used = expire;
+}
+
+static bool mi_page_reset_is_expired(mi_page_t* page, mi_msecs_t now) {
+ int32_t expire = (int32_t)(page->used);
+ return (((int32_t)now - expire) >= 0);
+}
+
+static void mi_pages_reset_add(mi_segment_t* segment, mi_page_t* page, mi_segments_tld_t* tld) {
+ mi_assert_internal(!page->segment_in_use || !page->is_committed);
+ mi_assert_internal(mi_page_not_in_queue(page,tld));
+ mi_assert_expensive(!mi_pages_reset_contains(page, tld));
+ mi_assert_internal(_mi_page_segment(page)==segment);
+ if (!mi_option_is_enabled(mi_option_page_reset)) return;
+ if (segment->mem_is_fixed || page->segment_in_use || !page->is_committed || page->is_reset) return;
+
+ if (mi_option_get(mi_option_reset_delay) == 0) {
+ // reset immediately?
+ mi_page_reset(segment, page, 0, tld);
+ }
+ else {
+ // otherwise push on the delayed page reset queue
+ mi_page_queue_t* pq = &tld->pages_reset;
+ // push on top
+ mi_page_reset_set_expire(page);
+ page->next = pq->first;
+ page->prev = NULL;
+ if (pq->first == NULL) {
+ mi_assert_internal(pq->last == NULL);
+ pq->first = pq->last = page;
+ }
+ else {
+ pq->first->prev = page;
+ pq->first = page;
+ }
+ }
+}
+
+static void mi_pages_reset_remove(mi_page_t* page, mi_segments_tld_t* tld) {
+ if (mi_page_not_in_queue(page,tld)) return;
+
+ mi_page_queue_t* pq = &tld->pages_reset;
+ mi_assert_internal(pq!=NULL);
+ mi_assert_internal(!page->segment_in_use);
+ mi_assert_internal(mi_pages_reset_contains(page, tld));
+ if (page->prev != NULL) page->prev->next = page->next;
+ if (page->next != NULL) page->next->prev = page->prev;
+ if (page == pq->last) pq->last = page->prev;
+ if (page == pq->first) pq->first = page->next;
+ page->next = page->prev = NULL;
+ page->used = 0;
+}
+
+static void mi_pages_reset_remove_all_in_segment(mi_segment_t* segment, bool force_reset, mi_segments_tld_t* tld) {
+ if (segment->mem_is_fixed) return; // never reset in huge OS pages
+ for (size_t i = 0; i < segment->capacity; i++) {
+ mi_page_t* page = &segment->pages[i];
+ if (!page->segment_in_use && page->is_committed && !page->is_reset) {
+ mi_pages_reset_remove(page, tld);
+ if (force_reset) {
+ mi_page_reset(segment, page, 0, tld);
+ }
+ }
+ else {
+ mi_assert_internal(mi_page_not_in_queue(page,tld));
+ }
+ }
+}
+
+static void mi_reset_delayed(mi_segments_tld_t* tld) {
+ if (!mi_option_is_enabled(mi_option_page_reset)) return;
+ mi_msecs_t now = _mi_clock_now();
+ mi_page_queue_t* pq = &tld->pages_reset;
+ // from oldest up to the first that has not expired yet
+ mi_page_t* page = pq->last;
+ while (page != NULL && mi_page_reset_is_expired(page,now)) {
+ mi_page_t* const prev = page->prev; // save previous field
+ mi_page_reset(_mi_page_segment(page), page, 0, tld);
+ page->used = 0;
+ page->prev = page->next = NULL;
+ page = prev;
+ }
+ // discard the reset pages from the queue
+ pq->last = page;
+ if (page != NULL){
+ page->next = NULL;
+ }
+ else {
+ pq->first = NULL;
+ }
+}
+
+
+/* -----------------------------------------------------------
+ Segment size calculations
+----------------------------------------------------------- */
+
+// Raw start of the page available memory; can be used on uninitialized pages (only `segment_idx` must be set)
+// The raw start is not taking aligned block allocation into consideration.
+static uint8_t* mi_segment_raw_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size) {
+ size_t psize = (segment->page_kind == MI_PAGE_HUGE ? segment->segment_size : (size_t)1 << segment->page_shift);
+ uint8_t* p = (uint8_t*)segment + page->segment_idx * psize;
+
+ if (page->segment_idx == 0) {
+ // the first page starts after the segment info (and possible guard page)
+ p += segment->segment_info_size;
+ psize -= segment->segment_info_size;
+ }
+
+ if (MI_SECURE > 1 || (MI_SECURE == 1 && page->segment_idx == segment->capacity - 1)) {
+ // secure == 1: the last page has an os guard page at the end
+ // secure > 1: every page has an os guard page
+ psize -= _mi_os_page_size();
+ }
+
+ if (page_size != NULL) *page_size = psize;
+ mi_assert_internal(page->xblock_size == 0 || _mi_ptr_page(p) == page);
+ mi_assert_internal(_mi_ptr_segment(p) == segment);
+ return p;
+}
+
+// Start of the page available memory; can be used on uninitialized pages (only `segment_idx` must be set)
+uint8_t* _mi_segment_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t block_size, size_t* page_size, size_t* pre_size)
+{
+ size_t psize;
+ uint8_t* p = mi_segment_raw_page_start(segment, page, &psize);
+ if (pre_size != NULL) *pre_size = 0;
+ if (page->segment_idx == 0 && block_size > 0 && segment->page_kind <= MI_PAGE_MEDIUM) {
+ // for small and medium objects, ensure the page start is aligned with the block size (PR#66 by kickunderscore)
+ size_t adjust = block_size - ((uintptr_t)p % block_size);
+ if (adjust < block_size) {
+ p += adjust;
+ psize -= adjust;
+ if (pre_size != NULL) *pre_size = adjust;
+ }
+ mi_assert_internal((uintptr_t)p % block_size == 0);
+ }
+
+ if (page_size != NULL) *page_size = psize;
+ mi_assert_internal(page->xblock_size==0 || _mi_ptr_page(p) == page);
+ mi_assert_internal(_mi_ptr_segment(p) == segment);
+ return p;
+}
+
+static size_t mi_segment_size(size_t capacity, size_t required, size_t* pre_size, size_t* info_size)
+{
+ const size_t minsize = sizeof(mi_segment_t) + ((capacity - 1) * sizeof(mi_page_t)) + 16 /* padding */;
+ size_t guardsize = 0;
+ size_t isize = 0;
+
+ if (MI_SECURE == 0) {
+ // normally no guard pages
+ isize = _mi_align_up(minsize, 16 * MI_MAX_ALIGN_SIZE);
+ }
+ else {
+ // in secure mode, we set up a protected page in between the segment info
+ // and the page data (and one at the end of the segment)
+ const size_t page_size = _mi_os_page_size();
+ isize = _mi_align_up(minsize, page_size);
+ guardsize = page_size;
+ required = _mi_align_up(required, page_size);
+ }
+
+ if (info_size != NULL) *info_size = isize;
+ if (pre_size != NULL) *pre_size = isize + guardsize;
+ return (required==0 ? MI_SEGMENT_SIZE : _mi_align_up( required + isize + 2*guardsize, MI_PAGE_HUGE_ALIGN) );
+}
+
+
+/* ----------------------------------------------------------------------------
+Segment caches
+We keep a small segment cache per thread to increase local
+reuse and avoid setting/clearing guard pages in secure mode.
+------------------------------------------------------------------------------- */
+
+static void mi_segments_track_size(long segment_size, mi_segments_tld_t* tld) {
+ if (segment_size>=0) _mi_stat_increase(&tld->stats->segments,1);
+ else _mi_stat_decrease(&tld->stats->segments,1);
+ tld->count += (segment_size >= 0 ? 1 : -1);
+ if (tld->count > tld->peak_count) tld->peak_count = tld->count;
+ tld->current_size += segment_size;
+ if (tld->current_size > tld->peak_size) tld->peak_size = tld->current_size;
+}
+
+static void mi_segment_os_free(mi_segment_t* segment, size_t segment_size, mi_segments_tld_t* tld) {
+ segment->thread_id = 0;
+ mi_segments_track_size(-((long)segment_size),tld);
+ if (MI_SECURE != 0) {
+ mi_assert_internal(!segment->mem_is_fixed);
+ mi_segment_protect(segment, false, tld->os); // ensure no more guard pages are set
+ }
+
+ bool any_reset = false;
+ bool fully_committed = true;
+ for (size_t i = 0; i < segment->capacity; i++) {
+ mi_page_t* page = &segment->pages[i];
+ if (!page->is_committed) { fully_committed = false; }
+ if (page->is_reset) { any_reset = true; }
+ }
+ if (any_reset && mi_option_is_enabled(mi_option_reset_decommits)) {
+ fully_committed = false;
+ }
+
+ _mi_mem_free(segment, segment_size, segment->memid, fully_committed, any_reset, tld->os);
+}
+
+
+// The thread local segment cache is limited to be at most 1/8 of the peak size of segments in use,
+#define MI_SEGMENT_CACHE_FRACTION (8)
+
+// note: returned segment may be partially reset
+static mi_segment_t* mi_segment_cache_pop(size_t segment_size, mi_segments_tld_t* tld) {
+ if (segment_size != 0 && segment_size != MI_SEGMENT_SIZE) return NULL;
+ mi_segment_t* segment = tld->cache;
+ if (segment == NULL) return NULL;
+ tld->cache_count--;
+ tld->cache = segment->next;
+ segment->next = NULL;
+ mi_assert_internal(segment->segment_size == MI_SEGMENT_SIZE);
+ _mi_stat_decrease(&tld->stats->segments_cache, 1);
+ return segment;
+}
+
+static bool mi_segment_cache_full(mi_segments_tld_t* tld)
+{
+ // if (tld->count == 1 && tld->cache_count==0) return false; // always cache at least the final segment of a thread
+ size_t max_cache = mi_option_get(mi_option_segment_cache);
+ if (tld->cache_count < max_cache
+ && tld->cache_count < (1 + (tld->peak_count / MI_SEGMENT_CACHE_FRACTION)) // at least allow a 1 element cache
+ ) {
+ return false;
+ }
+ // take the opportunity to reduce the segment cache if it is too large (now)
+ // TODO: this never happens as we check against peak usage, should we use current usage instead?
+ while (tld->cache_count > max_cache) { //(1 + (tld->peak_count / MI_SEGMENT_CACHE_FRACTION))) {
+ mi_segment_t* segment = mi_segment_cache_pop(0,tld);
+ mi_assert_internal(segment != NULL);
+ if (segment != NULL) mi_segment_os_free(segment, segment->segment_size, tld);
+ }
+ return true;
+}
+
+static bool mi_segment_cache_push(mi_segment_t* segment, mi_segments_tld_t* tld) {
+ mi_assert_internal(!mi_segment_is_in_free_queue(segment, tld));
+ mi_assert_internal(segment->next == NULL);
+ if (segment->segment_size != MI_SEGMENT_SIZE || mi_segment_cache_full(tld)) {
+ return false;
+ }
+ mi_assert_internal(segment->segment_size == MI_SEGMENT_SIZE);
+ segment->next = tld->cache;
+ tld->cache = segment;
+ tld->cache_count++;
+ _mi_stat_increase(&tld->stats->segments_cache,1);
+ return true;
+}
+
+// called by threads that are terminating to free cached segments
+void _mi_segment_thread_collect(mi_segments_tld_t* tld) {
+ mi_segment_t* segment;
+ while ((segment = mi_segment_cache_pop(0,tld)) != NULL) {
+ mi_segment_os_free(segment, segment->segment_size, tld);
+ }
+ mi_assert_internal(tld->cache_count == 0);
+ mi_assert_internal(tld->cache == NULL);
+#if MI_DEBUG>=2
+ if (!_mi_is_main_thread()) {
+ mi_assert_internal(tld->pages_reset.first == NULL);
+ mi_assert_internal(tld->pages_reset.last == NULL);
+ }
+#endif
+}
+
+
+/* -----------------------------------------------------------
+ Segment allocation
+----------------------------------------------------------- */
+
+// Allocate a segment from the OS aligned to `MI_SEGMENT_SIZE` .
+static mi_segment_t* mi_segment_init(mi_segment_t* segment, size_t required, mi_page_kind_t page_kind, size_t page_shift, mi_segments_tld_t* tld, mi_os_tld_t* os_tld)
+{
+ // the segment parameter is non-null if it came from our cache
+ mi_assert_internal(segment==NULL || (required==0 && page_kind <= MI_PAGE_LARGE));
+
+ // calculate needed sizes first
+ size_t capacity;
+ if (page_kind == MI_PAGE_HUGE) {
+ mi_assert_internal(page_shift == MI_SEGMENT_SHIFT && required > 0);
+ capacity = 1;
+ }
+ else {
+ mi_assert_internal(required == 0);
+ size_t page_size = (size_t)1 << page_shift;
+ capacity = MI_SEGMENT_SIZE / page_size;
+ mi_assert_internal(MI_SEGMENT_SIZE % page_size == 0);
+ mi_assert_internal(capacity >= 1 && capacity <= MI_SMALL_PAGES_PER_SEGMENT);
+ }
+ size_t info_size;
+ size_t pre_size;
+ size_t segment_size = mi_segment_size(capacity, required, &pre_size, &info_size);
+ mi_assert_internal(segment_size >= required);
+
+ // Initialize parameters
+ const bool eager_delayed = (page_kind <= MI_PAGE_MEDIUM && tld->count < (size_t)mi_option_get(mi_option_eager_commit_delay));
+ const bool eager = !eager_delayed && mi_option_is_enabled(mi_option_eager_commit);
+ bool commit = eager; // || (page_kind >= MI_PAGE_LARGE);
+ bool pages_still_good = false;
+ bool is_zero = false;
+
+ // Try to get it from our thread local cache first
+ if (segment != NULL) {
+ // came from cache
+ mi_assert_internal(segment->segment_size == segment_size);
+ if (page_kind <= MI_PAGE_MEDIUM && segment->page_kind == page_kind && segment->segment_size == segment_size) {
+ pages_still_good = true;
+ }
+ else
+ {
+ if (MI_SECURE!=0) {
+ mi_assert_internal(!segment->mem_is_fixed);
+ mi_segment_protect(segment, false, tld->os); // reset protection if the page kind differs
+ }
+ // different page kinds; unreset any reset pages, and unprotect
+ // TODO: optimize cache pop to return fitting pages if possible?
+ for (size_t i = 0; i < segment->capacity; i++) {
+ mi_page_t* page = &segment->pages[i];
+ if (page->is_reset) {
+ if (!commit && mi_option_is_enabled(mi_option_reset_decommits)) {
+ page->is_reset = false;
+ }
+ else {
+ mi_page_unreset(segment, page, 0, tld); // todo: only unreset the part that was reset? (instead of the full page)
+ }
+ }
+ }
+ // ensure the initial info is committed
+ if (segment->capacity < capacity) {
+ bool commit_zero = false;
+ _mi_mem_commit(segment, pre_size, &commit_zero, tld->os);
+ if (commit_zero) is_zero = true;
+ }
+ }
+ }
+ else {
+ // Allocate the segment from the OS
+ size_t memid;
+ bool mem_large = (!eager_delayed && (MI_SECURE==0)); // only allow large OS pages once we are no longer lazy
+ segment = (mi_segment_t*)_mi_mem_alloc_aligned(segment_size, MI_SEGMENT_SIZE, &commit, &mem_large, &is_zero, &memid, os_tld);
+ if (segment == NULL) return NULL; // failed to allocate
+ if (!commit) {
+ // ensure the initial info is committed
+ bool commit_zero = false;
+ bool ok = _mi_mem_commit(segment, pre_size, &commit_zero, tld->os);
+ if (commit_zero) is_zero = true;
+ if (!ok) {
+ // commit failed; we cannot touch the memory: free the segment directly and return `NULL`
+ _mi_mem_free(segment, MI_SEGMENT_SIZE, memid, false, false, os_tld);
+ return NULL;
+ }
+ }
+ segment->memid = memid;
+ segment->mem_is_fixed = mem_large;
+ segment->mem_is_committed = commit;
+ mi_segments_track_size((long)segment_size, tld);
+ }
+ mi_assert_internal(segment != NULL && (uintptr_t)segment % MI_SEGMENT_SIZE == 0);
+ mi_assert_internal(segment->mem_is_fixed ? segment->mem_is_committed : true);
+ if (!pages_still_good) {
+ // zero the segment info (but not the `mem` fields)
+ ptrdiff_t ofs = offsetof(mi_segment_t, next);
+ memset((uint8_t*)segment + ofs, 0, info_size - ofs);
+
+ // initialize pages info
+ for (uint8_t i = 0; i < capacity; i++) {
+ segment->pages[i].segment_idx = i;
+ segment->pages[i].is_reset = false;
+ segment->pages[i].is_committed = commit;
+ segment->pages[i].is_zero_init = is_zero;
+ }
+ }
+ else {
+ // zero the segment info but not the pages info (and mem fields)
+ ptrdiff_t ofs = offsetof(mi_segment_t, next);
+ memset((uint8_t*)segment + ofs, 0, offsetof(mi_segment_t,pages) - ofs);
+ }
+
+ // initialize
+ segment->page_kind = page_kind;
+ segment->capacity = capacity;
+ segment->page_shift = page_shift;
+ segment->segment_size = segment_size;
+ segment->segment_info_size = pre_size;
+ segment->thread_id = _mi_thread_id();
+ segment->cookie = _mi_ptr_cookie(segment);
+ // _mi_stat_increase(&tld->stats->page_committed, segment->segment_info_size);
+
+ // set protection
+ mi_segment_protect(segment, true, tld->os);
+
+ // insert in free lists for small and medium pages
+ if (page_kind <= MI_PAGE_MEDIUM) {
+ mi_segment_insert_in_free_queue(segment, tld);
+ }
+
+ //fprintf(stderr,"mimalloc: alloc segment at %p\n", (void*)segment);
+ return segment;
+}
+
+static mi_segment_t* mi_segment_alloc(size_t required, mi_page_kind_t page_kind, size_t page_shift, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) {
+ return mi_segment_init(NULL, required, page_kind, page_shift, tld, os_tld);
+}
+
+static void mi_segment_free(mi_segment_t* segment, bool force, mi_segments_tld_t* tld) {
+ UNUSED(force);
+ mi_assert(segment != NULL);
+ // note: don't reset pages even on abandon as the whole segment is freed? (and ready for reuse)
+ bool force_reset = (force && mi_option_is_enabled(mi_option_abandoned_page_reset));
+ mi_pages_reset_remove_all_in_segment(segment, force_reset, tld);
+ mi_segment_remove_from_free_queue(segment,tld);
+
+ mi_assert_expensive(!mi_segment_queue_contains(&tld->small_free, segment));
+ mi_assert_expensive(!mi_segment_queue_contains(&tld->medium_free, segment));
+ mi_assert(segment->next == NULL);
+ mi_assert(segment->prev == NULL);
+ _mi_stat_decrease(&tld->stats->page_committed, segment->segment_info_size);
+
+ if (!force && mi_segment_cache_push(segment, tld)) {
+ // it is put in our cache
+ }
+ else {
+ // otherwise return it to the OS
+ mi_segment_os_free(segment, segment->segment_size, tld);
+ }
+}
+
+/* -----------------------------------------------------------
+ Free page management inside a segment
+----------------------------------------------------------- */
+
+
+static bool mi_segment_has_free(const mi_segment_t* segment) {
+ return (segment->used < segment->capacity);
+}
+
+static bool mi_segment_page_claim(mi_segment_t* segment, mi_page_t* page, mi_segments_tld_t* tld) {
+ mi_assert_internal(_mi_page_segment(page) == segment);
+ mi_assert_internal(!page->segment_in_use);
+ mi_pages_reset_remove(page, tld);
+ // check commit
+ if (!page->is_committed) {
+ mi_assert_internal(!segment->mem_is_fixed);
+ mi_assert_internal(!page->is_reset);
+ size_t psize;
+ uint8_t* start = mi_segment_raw_page_start(segment, page, &psize);
+ bool is_zero = false;
+ const size_t gsize = (MI_SECURE >= 2 ? _mi_os_page_size() : 0);
+ bool ok = _mi_mem_commit(start, psize + gsize, &is_zero, tld->os);
+ if (!ok) return false; // failed to commit!
+ if (gsize > 0) { mi_segment_protect_range(start + psize, gsize, true); }
+ if (is_zero) { page->is_zero_init = true; }
+ page->is_committed = true;
+ }
+ // set in-use before doing unreset to prevent delayed reset
+ page->segment_in_use = true;
+ segment->used++;
+ // check reset
+ if (page->is_reset) {
+ mi_assert_internal(!segment->mem_is_fixed);
+ bool ok = mi_page_unreset(segment, page, 0, tld);
+ if (!ok) {
+ page->segment_in_use = false;
+ segment->used--;
+ return false;
+ }
+ }
+ mi_assert_internal(page->segment_in_use);
+ mi_assert_internal(segment->used <= segment->capacity);
+ if (segment->used == segment->capacity && segment->page_kind <= MI_PAGE_MEDIUM) {
+ // if no more free pages, remove from the queue
+ mi_assert_internal(!mi_segment_has_free(segment));
+ mi_segment_remove_from_free_queue(segment, tld);
+ }
+ return true;
+}
+
+
+/* -----------------------------------------------------------
+ Free
+----------------------------------------------------------- */
+
+static void mi_segment_abandon(mi_segment_t* segment, mi_segments_tld_t* tld);
+
+// clear page data; can be called on abandoned segments
+static void mi_segment_page_clear(mi_segment_t* segment, mi_page_t* page, bool allow_reset, mi_segments_tld_t* tld)
+{
+ mi_assert_internal(page->segment_in_use);
+ mi_assert_internal(mi_page_all_free(page));
+ mi_assert_internal(page->is_committed);
+ mi_assert_internal(mi_page_not_in_queue(page, tld));
+
+ size_t inuse = page->capacity * mi_page_block_size(page);
+ _mi_stat_decrease(&tld->stats->page_committed, inuse);
+ _mi_stat_decrease(&tld->stats->pages, 1);
+
+ // calculate the used size from the raw (non-aligned) start of the page
+ //size_t pre_size;
+ //_mi_segment_page_start(segment, page, page->block_size, NULL, &pre_size);
+ //size_t used_size = pre_size + (page->capacity * page->block_size);
+
+ page->is_zero_init = false;
+ page->segment_in_use = false;
+
+ // reset the page memory to reduce memory pressure?
+ // note: must come after setting `segment_in_use` to false but before block_size becomes 0
+ //mi_page_reset(segment, page, 0 /*used_size*/, tld);
+
+ // zero the page data, but not the segment fields and capacity, and block_size (for page size calculations)
+ uint32_t block_size = page->xblock_size;
+ uint16_t capacity = page->capacity;
+ uint16_t reserved = page->reserved;
+ ptrdiff_t ofs = offsetof(mi_page_t,capacity);
+ memset((uint8_t*)page + ofs, 0, sizeof(*page) - ofs);
+ page->capacity = capacity;
+ page->reserved = reserved;
+ page->xblock_size = block_size;
+ segment->used--;
+
+ // add to the free page list for reuse/reset
+ if (allow_reset) {
+ mi_pages_reset_add(segment, page, tld);
+ }
+
+ page->capacity = 0; // after reset there can be zero'd now
+ page->reserved = 0;
+}
+
+void _mi_segment_page_free(mi_page_t* page, bool force, mi_segments_tld_t* tld)
+{
+ mi_assert(page != NULL);
+ mi_segment_t* segment = _mi_page_segment(page);
+ mi_assert_expensive(mi_segment_is_valid(segment,tld));
+ mi_reset_delayed(tld);
+
+ // mark it as free now
+ mi_segment_page_clear(segment, page, true, tld);
+
+ if (segment->used == 0) {
+ // no more used pages; remove from the free list and free the segment
+ mi_segment_free(segment, force, tld);
+ }
+ else {
+ if (segment->used == segment->abandoned) {
+ // only abandoned pages; remove from free list and abandon
+ mi_segment_abandon(segment,tld);
+ }
+ else if (segment->used + 1 == segment->capacity) {
+ mi_assert_internal(segment->page_kind <= MI_PAGE_MEDIUM); // for now we only support small and medium pages
+ // move back to segments free list
+ mi_segment_insert_in_free_queue(segment,tld);
+ }
+ }
+}
+
+
+/* -----------------------------------------------------------
+Abandonment
+
+When threads terminate, they can leave segments with
+live blocks (reached through other threads). Such segments
+are "abandoned" and will be reclaimed by other threads to
+reuse their pages and/or free them eventually
+
+We maintain a global list of abandoned segments that are
+reclaimed on demand. Since this is shared among threads
+the implementation needs to avoid the A-B-A problem on
+popping abandoned segments: <https://en.wikipedia.org/wiki/ABA_problem>
+We use tagged pointers to avoid accidentially identifying
+reused segments, much like stamped references in Java.
+Secondly, we maintain a reader counter to avoid resetting
+or decommitting segments that have a pending read operation.
+
+Note: the current implementation is one possible design;
+another way might be to keep track of abandoned segments
+in the regions. This would have the advantage of keeping
+all concurrent code in one place and not needing to deal
+with ABA issues. The drawback is that it is unclear how to
+scan abandoned segments efficiently in that case as they
+would be spread among all other segments in the regions.
+----------------------------------------------------------- */
+
+// Use the bottom 20-bits (on 64-bit) of the aligned segment pointers
+// to put in a tag that increments on update to avoid the A-B-A problem.
+#define MI_TAGGED_MASK MI_SEGMENT_MASK
+typedef uintptr_t mi_tagged_segment_t;
+
+static mi_segment_t* mi_tagged_segment_ptr(mi_tagged_segment_t ts) {
+ return (mi_segment_t*)(ts & ~MI_TAGGED_MASK);
+}
+
+static mi_tagged_segment_t mi_tagged_segment(mi_segment_t* segment, mi_tagged_segment_t ts) {
+ mi_assert_internal(((uintptr_t)segment & MI_TAGGED_MASK) == 0);
+ uintptr_t tag = ((ts & MI_TAGGED_MASK) + 1) & MI_TAGGED_MASK;
+ return ((uintptr_t)segment | tag);
+}
+
+// This is a list of visited abandoned pages that were full at the time.
+// this list migrates to `abandoned` when that becomes NULL. The use of
+// this list reduces contention and the rate at which segments are visited.
+static mi_decl_cache_align volatile _Atomic(mi_segment_t*) abandoned_visited; // = NULL
+
+// The abandoned page list (tagged as it supports pop)
+static mi_decl_cache_align volatile _Atomic(mi_tagged_segment_t) abandoned; // = NULL
+
+// We also maintain a count of current readers of the abandoned list
+// in order to prevent resetting/decommitting segment memory if it might
+// still be read.
+static mi_decl_cache_align volatile _Atomic(uintptr_t) abandoned_readers; // = 0
+
+// Push on the visited list
+static void mi_abandoned_visited_push(mi_segment_t* segment) {
+ mi_assert_internal(segment->thread_id == 0);
+ mi_assert_internal(segment->abandoned_next == NULL);
+ mi_assert_internal(segment->next == NULL && segment->prev == NULL);
+ mi_assert_internal(segment->used > 0);
+ mi_segment_t* anext;
+ do {
+ anext = mi_atomic_read_ptr_relaxed(mi_segment_t, &abandoned_visited);
+ segment->abandoned_next = anext;
+ } while (!mi_atomic_cas_ptr_weak(mi_segment_t, &abandoned_visited, segment, anext));
+}
+
+// Move the visited list to the abandoned list.
+static bool mi_abandoned_visited_revisit(void)
+{
+ // quick check if the visited list is empty
+ if (mi_atomic_read_ptr_relaxed(mi_segment_t,&abandoned_visited)==NULL) return false;
+
+ // grab the whole visited list
+ mi_segment_t* first = mi_atomic_exchange_ptr(mi_segment_t, &abandoned_visited, NULL);
+ if (first == NULL) return false;
+
+ // first try to swap directly if the abandoned list happens to be NULL
+ const mi_tagged_segment_t ts = mi_atomic_read_relaxed(&abandoned);
+ mi_tagged_segment_t afirst;
+ if (mi_tagged_segment_ptr(ts)==NULL) {
+ afirst = mi_tagged_segment(first, ts);
+ if (mi_atomic_cas_strong(&abandoned, afirst, ts)) return true;
+ }
+
+ // find the last element of the visited list: O(n)
+ mi_segment_t* last = first;
+ while (last->abandoned_next != NULL) {
+ last = last->abandoned_next;
+ }
+
+ // and atomically prepend to the abandoned list
+ // (no need to increase the readers as we don't access the abandoned segments)
+ mi_tagged_segment_t anext;
+ do {
+ anext = mi_atomic_read_relaxed(&abandoned);
+ last->abandoned_next = mi_tagged_segment_ptr(anext);
+ afirst = mi_tagged_segment(first, anext);
+ } while (!mi_atomic_cas_weak(&abandoned, afirst, anext));
+ return true;
+}
+
+// Push on the abandoned list.
+static void mi_abandoned_push(mi_segment_t* segment) {
+ mi_assert_internal(segment->thread_id == 0);
+ mi_assert_internal(segment->abandoned_next == NULL);
+ mi_assert_internal(segment->next == NULL && segment->prev == NULL);
+ mi_assert_internal(segment->used > 0);
+ mi_tagged_segment_t ts;
+ mi_tagged_segment_t next;
+ do {
+ ts = mi_atomic_read_relaxed(&abandoned);
+ segment->abandoned_next = mi_tagged_segment_ptr(ts);
+ next = mi_tagged_segment(segment, ts);
+ } while (!mi_atomic_cas_weak(&abandoned, next, ts));
+}
+
+// Wait until there are no more pending reads on segments that used to be in the abandoned list
+void _mi_abandoned_await_readers(void) {
+ uintptr_t n;
+ do {
+ n = mi_atomic_read(&abandoned_readers);
+ if (n != 0) mi_atomic_yield();
+ } while (n != 0);
+}
+
+// Pop from the abandoned list
+static mi_segment_t* mi_abandoned_pop(void) {
+ mi_segment_t* segment;
+ // Check efficiently if it is empty (or if the visited list needs to be moved)
+ mi_tagged_segment_t ts = mi_atomic_read_relaxed(&abandoned);
+ segment = mi_tagged_segment_ptr(ts);
+ if (mi_likely(segment == NULL)) {
+ if (mi_likely(!mi_abandoned_visited_revisit())) { // try to swap in the visited list on NULL
+ return NULL;
+ }
+ }
+
+ // Do a pop. We use a reader count to prevent
+ // a segment to be decommitted while a read is still pending,
+ // and a tagged pointer to prevent A-B-A link corruption.
+ // (this is called from `memory.c:_mi_mem_free` for example)
+ mi_atomic_increment(&abandoned_readers); // ensure no segment gets decommitted
+ mi_tagged_segment_t next = 0;
+ do {
+ ts = mi_atomic_read(&abandoned);
+ segment = mi_tagged_segment_ptr(ts);
+ if (segment != NULL) {
+ next = mi_tagged_segment(segment->abandoned_next, ts); // note: reads the segment's `abandoned_next` field so should not be decommitted
+ }
+ } while (segment != NULL && !mi_atomic_cas_weak(&abandoned, next, ts));
+ mi_atomic_decrement(&abandoned_readers); // release reader lock
+ if (segment != NULL) {
+ segment->abandoned_next = NULL;
+ }
+ return segment;
+}
+
+/* -----------------------------------------------------------
+ Abandon segment/page
+----------------------------------------------------------- */
+
+static void mi_segment_abandon(mi_segment_t* segment, mi_segments_tld_t* tld) {
+ mi_assert_internal(segment->used == segment->abandoned);
+ mi_assert_internal(segment->used > 0);
+ mi_assert_internal(segment->abandoned_next == NULL);
+ mi_assert_expensive(mi_segment_is_valid(segment, tld));
+
+ // remove the segment from the free page queue if needed
+ mi_reset_delayed(tld);
+ mi_pages_reset_remove_all_in_segment(segment, mi_option_is_enabled(mi_option_abandoned_page_reset), tld);
+ mi_segment_remove_from_free_queue(segment, tld);
+ mi_assert_internal(segment->next == NULL && segment->prev == NULL);
+
+ // all pages in the segment are abandoned; add it to the abandoned list
+ _mi_stat_increase(&tld->stats->segments_abandoned, 1);
+ mi_segments_track_size(-((long)segment->segment_size), tld);
+ segment->thread_id = 0;
+ segment->abandoned_next = NULL;
+ segment->abandoned_visits = 0;
+ mi_abandoned_push(segment);
+}
+
+void _mi_segment_page_abandon(mi_page_t* page, mi_segments_tld_t* tld) {
+ mi_assert(page != NULL);
+ mi_assert_internal(mi_page_thread_free_flag(page)==MI_NEVER_DELAYED_FREE);
+ mi_assert_internal(mi_page_heap(page) == NULL);
+ mi_segment_t* segment = _mi_page_segment(page);
+ mi_assert_expensive(!mi_pages_reset_contains(page, tld));
+ mi_assert_expensive(mi_segment_is_valid(segment, tld));
+ segment->abandoned++;
+ _mi_stat_increase(&tld->stats->pages_abandoned, 1);
+ mi_assert_internal(segment->abandoned <= segment->used);
+ if (segment->used == segment->abandoned) {
+ // all pages are abandoned, abandon the entire segment
+ mi_segment_abandon(segment, tld);
+ }
+}
+
+/* -----------------------------------------------------------
+ Reclaim abandoned pages
+----------------------------------------------------------- */
+
+// Possibly clear pages and check if free space is available
+static bool mi_segment_check_free(mi_segment_t* segment, size_t block_size, bool* all_pages_free)
+{
+ mi_assert_internal(block_size < MI_HUGE_BLOCK_SIZE);
+ bool has_page = false;
+ size_t pages_used = 0;
+ size_t pages_used_empty = 0;
+ for (size_t i = 0; i < segment->capacity; i++) {
+ mi_page_t* page = &segment->pages[i];
+ if (page->segment_in_use) {
+ pages_used++;
+ // ensure used count is up to date and collect potential concurrent frees
+ _mi_page_free_collect(page, false);
+ if (mi_page_all_free(page)) {
+ // if everything free already, page can be reused for some block size
+ // note: don't clear the page yet as we can only OS reset it once it is reclaimed
+ pages_used_empty++;
+ has_page = true;
+ }
+ else if (page->xblock_size == block_size && mi_page_has_any_available(page)) {
+ // a page has available free blocks of the right size
+ has_page = true;
+ }
+ }
+ else {
+ // whole empty page
+ has_page = true;
+ }
+ }
+ mi_assert_internal(pages_used == segment->used && pages_used >= pages_used_empty);
+ if (all_pages_free != NULL) {
+ *all_pages_free = ((pages_used - pages_used_empty) == 0);
+ }
+ return has_page;
+}
+
+
+// Reclaim a segment; returns NULL if the segment was freed
+// set `right_page_reclaimed` to `true` if it reclaimed a page of the right `block_size` that was not full.
+static mi_segment_t* mi_segment_reclaim(mi_segment_t* segment, mi_heap_t* heap, size_t requested_block_size, bool* right_page_reclaimed, mi_segments_tld_t* tld) {
+ mi_assert_internal(segment->abandoned_next == NULL);
+ if (right_page_reclaimed != NULL) { *right_page_reclaimed = false; }
+
+ segment->thread_id = _mi_thread_id();
+ segment->abandoned_visits = 0;
+ mi_segments_track_size((long)segment->segment_size, tld);
+ mi_assert_internal(segment->next == NULL && segment->prev == NULL);
+ mi_assert_expensive(mi_segment_is_valid(segment, tld));
+ _mi_stat_decrease(&tld->stats->segments_abandoned, 1);
+
+ for (size_t i = 0; i < segment->capacity; i++) {
+ mi_page_t* page = &segment->pages[i];
+ if (page->segment_in_use) {
+ mi_assert_internal(!page->is_reset);
+ mi_assert_internal(page->is_committed);
+ mi_assert_internal(mi_page_not_in_queue(page, tld));
+ mi_assert_internal(mi_page_thread_free_flag(page)==MI_NEVER_DELAYED_FREE);
+ mi_assert_internal(mi_page_heap(page) == NULL);
+ segment->abandoned--;
+ mi_assert(page->next == NULL);
+ _mi_stat_decrease(&tld->stats->pages_abandoned, 1);
+ // set the heap again and allow heap thread delayed free again.
+ mi_page_set_heap(page, heap);
+ _mi_page_use_delayed_free(page, MI_USE_DELAYED_FREE, true); // override never (after heap is set)
+ // TODO: should we not collect again given that we just collected in `check_free`?
+ _mi_page_free_collect(page, false); // ensure used count is up to date
+ if (mi_page_all_free(page)) {
+ // if everything free already, clear the page directly
+ mi_segment_page_clear(segment, page, true, tld); // reset is ok now
+ }
+ else {
+ // otherwise reclaim it into the heap
+ _mi_page_reclaim(heap, page);
+ if (requested_block_size == page->xblock_size && mi_page_has_any_available(page)) {
+ if (right_page_reclaimed != NULL) { *right_page_reclaimed = true; }
+ }
+ }
+ }
+ else if (page->is_committed && !page->is_reset) { // not in-use, and not reset yet
+ // note: do not reset as this includes pages that were not touched before
+ // mi_pages_reset_add(segment, page, tld);
+ }
+ }
+ mi_assert_internal(segment->abandoned == 0);
+ if (segment->used == 0) {
+ mi_assert_internal(right_page_reclaimed == NULL || !(*right_page_reclaimed));
+ mi_segment_free(segment, false, tld);
+ return NULL;
+ }
+ else {
+ if (segment->page_kind <= MI_PAGE_MEDIUM && mi_segment_has_free(segment)) {
+ mi_segment_insert_in_free_queue(segment, tld);
+ }
+ return segment;
+ }
+}
+
+
+void _mi_abandoned_reclaim_all(mi_heap_t* heap, mi_segments_tld_t* tld) {
+ mi_segment_t* segment;
+ while ((segment = mi_abandoned_pop()) != NULL) {
+ mi_segment_reclaim(segment, heap, 0, NULL, tld);
+ }
+}
+
+static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t block_size, mi_page_kind_t page_kind, bool* reclaimed, mi_segments_tld_t* tld)
+{
+ *reclaimed = false;
+ mi_segment_t* segment;
+ int max_tries = 8; // limit the work to bound allocation times
+ while ((max_tries-- > 0) && ((segment = mi_abandoned_pop()) != NULL)) {
+ segment->abandoned_visits++;
+ bool all_pages_free;
+ bool has_page = mi_segment_check_free(segment,block_size,&all_pages_free); // try to free up pages (due to concurrent frees)
+ if (all_pages_free) {
+ // free the segment (by forced reclaim) to make it available to other threads.
+ // note1: we prefer to free a segment as that might lead to reclaiming another
+ // segment that is still partially used.
+ // note2: we could in principle optimize this by skipping reclaim and directly
+ // freeing but that would violate some invariants temporarily)
+ mi_segment_reclaim(segment, heap, 0, NULL, tld);
+ }
+ else if (has_page && segment->page_kind == page_kind) {
+ // found a free page of the right kind, or page of the right block_size with free space
+ // we return the result of reclaim (which is usually `segment`) as it might free
+ // the segment due to concurrent frees (in which case `NULL` is returned).
+ return mi_segment_reclaim(segment, heap, block_size, reclaimed, tld);
+ }
+ else if (segment->abandoned_visits >= 3) {
+ // always reclaim on 3rd visit to limit the list length.
+ mi_segment_reclaim(segment, heap, 0, NULL, tld);
+ }
+ else {
+ // otherwise, push on the visited list so it gets not looked at too quickly again
+ mi_abandoned_visited_push(segment);
+ }
+ }
+ return NULL;
+}
+
+
+/* -----------------------------------------------------------
+ Reclaim or allocate
+----------------------------------------------------------- */
+
+static mi_segment_t* mi_segment_reclaim_or_alloc(mi_heap_t* heap, size_t block_size, mi_page_kind_t page_kind, size_t page_shift, mi_segments_tld_t* tld, mi_os_tld_t* os_tld)
+{
+ mi_assert_internal(page_kind <= MI_PAGE_LARGE);
+ mi_assert_internal(block_size < MI_HUGE_BLOCK_SIZE);
+ // 1. try to get a segment from our cache
+ mi_segment_t* segment = mi_segment_cache_pop(MI_SEGMENT_SIZE, tld);
+ if (segment != NULL) {
+ mi_segment_init(segment, 0, page_kind, page_shift, tld, os_tld);
+ return segment;
+ }
+ // 2. try to reclaim an abandoned segment
+ bool reclaimed;
+ segment = mi_segment_try_reclaim(heap, block_size, page_kind, &reclaimed, tld);
+ if (reclaimed) {
+ // reclaimed the right page right into the heap
+ mi_assert_internal(segment != NULL && segment->page_kind == page_kind && page_kind <= MI_PAGE_LARGE);
+ return NULL; // pretend out-of-memory as the page will be in the page queue of the heap with available blocks
+ }
+ else if (segment != NULL) {
+ // reclaimed a segment with empty pages (of `page_kind`) in it
+ return segment;
+ }
+ // 3. otherwise allocate a fresh segment
+ return mi_segment_alloc(0, page_kind, page_shift, tld, os_tld);
+}
+
+
+/* -----------------------------------------------------------
+ Small page allocation
+----------------------------------------------------------- */
+
+static mi_page_t* mi_segment_find_free(mi_segment_t* segment, mi_segments_tld_t* tld) {
+ mi_assert_internal(mi_segment_has_free(segment));
+ mi_assert_expensive(mi_segment_is_valid(segment, tld));
+ for (size_t i = 0; i < segment->capacity; i++) { // TODO: use a bitmap instead of search?
+ mi_page_t* page = &segment->pages[i];
+ if (!page->segment_in_use) {
+ bool ok = mi_segment_page_claim(segment, page, tld);
+ if (ok) return page;
+ }
+ }
+ mi_assert(false);
+ return NULL;
+}
+
+// Allocate a page inside a segment. Requires that the page has free pages
+static mi_page_t* mi_segment_page_alloc_in(mi_segment_t* segment, mi_segments_tld_t* tld) {
+ mi_assert_internal(mi_segment_has_free(segment));
+ return mi_segment_find_free(segment, tld);
+}
+
+static mi_page_t* mi_segment_page_alloc(mi_heap_t* heap, size_t block_size, mi_page_kind_t kind, size_t page_shift, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) {
+ // find an available segment the segment free queue
+ mi_segment_queue_t* const free_queue = mi_segment_free_queue_of_kind(kind, tld);
+ if (mi_segment_queue_is_empty(free_queue)) {
+ // possibly allocate or reclaim a fresh segment
+ mi_segment_t* const segment = mi_segment_reclaim_or_alloc(heap, block_size, kind, page_shift, tld, os_tld);
+ if (segment == NULL) return NULL; // return NULL if out-of-memory (or reclaimed)
+ mi_assert_internal(free_queue->first == segment);
+ mi_assert_internal(segment->page_kind==kind);
+ mi_assert_internal(segment->used < segment->capacity);
+ }
+ mi_assert_internal(free_queue->first != NULL);
+ mi_page_t* const page = mi_segment_page_alloc_in(free_queue->first, tld);
+ mi_assert_internal(page != NULL);
+#if MI_DEBUG>=2
+ // verify it is committed
+ _mi_segment_page_start(_mi_page_segment(page), page, sizeof(void*), NULL, NULL)[0] = 0;
+#endif
+ return page;
+}
+
+static mi_page_t* mi_segment_small_page_alloc(mi_heap_t* heap, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) {
+ return mi_segment_page_alloc(heap, block_size, MI_PAGE_SMALL,MI_SMALL_PAGE_SHIFT,tld,os_tld);
+}
+
+static mi_page_t* mi_segment_medium_page_alloc(mi_heap_t* heap, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) {
+ return mi_segment_page_alloc(heap, block_size, MI_PAGE_MEDIUM, MI_MEDIUM_PAGE_SHIFT, tld, os_tld);
+}
+
+/* -----------------------------------------------------------
+ large page allocation
+----------------------------------------------------------- */
+
+static mi_page_t* mi_segment_large_page_alloc(mi_heap_t* heap, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) {
+ mi_segment_t* segment = mi_segment_reclaim_or_alloc(heap,block_size,MI_PAGE_LARGE,MI_LARGE_PAGE_SHIFT,tld,os_tld);
+ if (segment == NULL) return NULL;
+ mi_page_t* page = mi_segment_find_free(segment, tld);
+ mi_assert_internal(page != NULL);
+#if MI_DEBUG>=2
+ _mi_segment_page_start(segment, page, sizeof(void*), NULL, NULL)[0] = 0;
+#endif
+ return page;
+}
+
+static mi_page_t* mi_segment_huge_page_alloc(size_t size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld)
+{
+ mi_segment_t* segment = mi_segment_alloc(size, MI_PAGE_HUGE, MI_SEGMENT_SHIFT,tld,os_tld);
+ if (segment == NULL) return NULL;
+ mi_assert_internal(mi_segment_page_size(segment) - segment->segment_info_size - (2*(MI_SECURE == 0 ? 0 : _mi_os_page_size())) >= size);
+ segment->thread_id = 0; // huge pages are immediately abandoned
+ mi_segments_track_size(-(long)segment->segment_size, tld);
+ mi_page_t* page = mi_segment_find_free(segment, tld);
+ mi_assert_internal(page != NULL);
+ return page;
+}
+
+// free huge block from another thread
+void _mi_segment_huge_page_free(mi_segment_t* segment, mi_page_t* page, mi_block_t* block) {
+ // huge page segments are always abandoned and can be freed immediately by any thread
+ mi_assert_internal(segment->page_kind==MI_PAGE_HUGE);
+ mi_assert_internal(segment == _mi_page_segment(page));
+ mi_assert_internal(mi_atomic_read_relaxed(&segment->thread_id)==0);
+
+ // claim it and free
+ mi_heap_t* heap = mi_heap_get_default(); // issue #221; don't use the internal get_default_heap as we need to ensure the thread is initialized.
+ // paranoia: if this it the last reference, the cas should always succeed
+ if (mi_atomic_cas_strong(&segment->thread_id, heap->thread_id, 0)) {
+ mi_block_set_next(page, block, page->free);
+ page->free = block;
+ page->used--;
+ page->is_zero = false;
+ mi_assert(page->used == 0);
+ mi_tld_t* tld = heap->tld;
+ const size_t bsize = mi_page_usable_block_size(page);
+ if (bsize > MI_HUGE_OBJ_SIZE_MAX) {
+ _mi_stat_decrease(&tld->stats.giant, bsize);
+ }
+ else {
+ _mi_stat_decrease(&tld->stats.huge, bsize);
+ }
+ mi_segments_track_size((long)segment->segment_size, &tld->segments);
+ _mi_segment_page_free(page, true, &tld->segments);
+ }
+}
+
+/* -----------------------------------------------------------
+ Page allocation
+----------------------------------------------------------- */
+
+mi_page_t* _mi_segment_page_alloc(mi_heap_t* heap, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) {
+ mi_page_t* page;
+ if (block_size <= MI_SMALL_OBJ_SIZE_MAX) {
+ page = mi_segment_small_page_alloc(heap, block_size, tld, os_tld);
+ }
+ else if (block_size <= MI_MEDIUM_OBJ_SIZE_MAX) {
+ page = mi_segment_medium_page_alloc(heap, block_size, tld, os_tld);
+ }
+ else if (block_size <= MI_LARGE_OBJ_SIZE_MAX) {
+ page = mi_segment_large_page_alloc(heap, block_size, tld, os_tld);
+ }
+ else {
+ page = mi_segment_huge_page_alloc(block_size,tld,os_tld);
+ }
+ mi_assert_expensive(page == NULL || mi_segment_is_valid(_mi_page_segment(page),tld));
+ mi_assert_internal(page == NULL || (mi_segment_page_size(_mi_page_segment(page)) - (MI_SECURE == 0 ? 0 : _mi_os_page_size())) >= block_size);
+ mi_reset_delayed(tld);
+ mi_assert_internal(page == NULL || mi_page_not_in_queue(page, tld));
+ return page;
+}
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+#if defined(__sun)
+// same remarks as os.c for the static's context.
+#undef _XOPEN_SOURCE
+#undef _POSIX_C_SOURCE
+#endif
+
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+
+// For a static override we create a single object file
+// containing the whole library. If it is linked first
+// it will override all the standard library allocation
+// functions (on Unix's).
+#include "stats.c"
+#include "random.c"
+#include "os.c"
+#include "arena.c"
+#include "region.c"
+#include "segment.c"
+#include "page.c"
+#include "heap.c"
+#include "alloc.c"
+#include "alloc-aligned.c"
+#include "alloc-posix.c"
+#if MI_OSX_ZONE
+#include "alloc-override-osx.c"
+#endif
+#include "init.c"
+#include "options.c"
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc-internal.h"
+#include "mimalloc-atomic.h"
+
+#include <stdio.h> // fputs, stderr
+#include <string.h> // memset
+
+
+/* -----------------------------------------------------------
+ Statistics operations
+----------------------------------------------------------- */
+
+static bool mi_is_in_main(void* stat) {
+ return ((uint8_t*)stat >= (uint8_t*)&_mi_stats_main
+ && (uint8_t*)stat < ((uint8_t*)&_mi_stats_main + sizeof(mi_stats_t)));
+}
+
+static void mi_stat_update(mi_stat_count_t* stat, int64_t amount) {
+ if (amount == 0) return;
+ if (mi_is_in_main(stat))
+ {
+ // add atomically (for abandoned pages)
+ mi_atomic_addi64(&stat->current,amount);
+ mi_atomic_maxi64(&stat->peak, mi_atomic_readi64(&stat->current));
+ if (amount > 0) {
+ mi_atomic_addi64(&stat->allocated,amount);
+ }
+ else {
+ mi_atomic_addi64(&stat->freed, -amount);
+ }
+ }
+ else {
+ // add thread local
+ stat->current += amount;
+ if (stat->current > stat->peak) stat->peak = stat->current;
+ if (amount > 0) {
+ stat->allocated += amount;
+ }
+ else {
+ stat->freed += -amount;
+ }
+ }
+}
+
+void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount) {
+ if (mi_is_in_main(stat)) {
+ mi_atomic_addi64( &stat->count, 1 );
+ mi_atomic_addi64( &stat->total, (int64_t)amount );
+ }
+ else {
+ stat->count++;
+ stat->total += amount;
+ }
+}
+
+void _mi_stat_increase(mi_stat_count_t* stat, size_t amount) {
+ mi_stat_update(stat, (int64_t)amount);
+}
+
+void _mi_stat_decrease(mi_stat_count_t* stat, size_t amount) {
+ mi_stat_update(stat, -((int64_t)amount));
+}
+
+// must be thread safe as it is called from stats_merge
+static void mi_stat_add(mi_stat_count_t* stat, const mi_stat_count_t* src, int64_t unit) {
+ if (stat==src) return;
+ if (src->allocated==0 && src->freed==0) return;
+ mi_atomic_addi64( &stat->allocated, src->allocated * unit);
+ mi_atomic_addi64( &stat->current, src->current * unit);
+ mi_atomic_addi64( &stat->freed, src->freed * unit);
+ // peak scores do not work across threads..
+ mi_atomic_addi64( &stat->peak, src->peak * unit);
+}
+
+static void mi_stat_counter_add(mi_stat_counter_t* stat, const mi_stat_counter_t* src, int64_t unit) {
+ if (stat==src) return;
+ mi_atomic_addi64( &stat->total, src->total * unit);
+ mi_atomic_addi64( &stat->count, src->count * unit);
+}
+
+// must be thread safe as it is called from stats_merge
+static void mi_stats_add(mi_stats_t* stats, const mi_stats_t* src) {
+ if (stats==src) return;
+ mi_stat_add(&stats->segments, &src->segments,1);
+ mi_stat_add(&stats->pages, &src->pages,1);
+ mi_stat_add(&stats->reserved, &src->reserved, 1);
+ mi_stat_add(&stats->committed, &src->committed, 1);
+ mi_stat_add(&stats->reset, &src->reset, 1);
+ mi_stat_add(&stats->page_committed, &src->page_committed, 1);
+
+ mi_stat_add(&stats->pages_abandoned, &src->pages_abandoned, 1);
+ mi_stat_add(&stats->segments_abandoned, &src->segments_abandoned, 1);
+ mi_stat_add(&stats->threads, &src->threads, 1);
+
+ mi_stat_add(&stats->malloc, &src->malloc, 1);
+ mi_stat_add(&stats->segments_cache, &src->segments_cache, 1);
+ mi_stat_add(&stats->huge, &src->huge, 1);
+ mi_stat_add(&stats->giant, &src->giant, 1);
+
+ mi_stat_counter_add(&stats->pages_extended, &src->pages_extended, 1);
+ mi_stat_counter_add(&stats->mmap_calls, &src->mmap_calls, 1);
+ mi_stat_counter_add(&stats->commit_calls, &src->commit_calls, 1);
+
+ mi_stat_counter_add(&stats->page_no_retire, &src->page_no_retire, 1);
+ mi_stat_counter_add(&stats->searches, &src->searches, 1);
+ mi_stat_counter_add(&stats->huge_count, &src->huge_count, 1);
+ mi_stat_counter_add(&stats->giant_count, &src->giant_count, 1);
+#if MI_STAT>1
+ for (size_t i = 0; i <= MI_BIN_HUGE; i++) {
+ if (src->normal[i].allocated > 0 || src->normal[i].freed > 0) {
+ mi_stat_add(&stats->normal[i], &src->normal[i], 1);
+ }
+ }
+#endif
+}
+
+/* -----------------------------------------------------------
+ Display statistics
+----------------------------------------------------------- */
+
+// unit > 0 : size in binary bytes
+// unit == 0: count as decimal
+// unit < 0 : count in binary
+static void mi_printf_amount(int64_t n, int64_t unit, mi_output_fun* out, void* arg, const char* fmt) {
+ char buf[32];
+ int len = 32;
+ const char* suffix = (unit <= 0 ? " " : "b");
+ const int64_t base = (unit == 0 ? 1000 : 1024);
+ if (unit>0) n *= unit;
+
+ const int64_t pos = (n < 0 ? -n : n);
+ if (pos < base) {
+ snprintf(buf, len, "%d %s ", (int)n, suffix);
+ }
+ else {
+ int64_t divider = base;
+ const char* magnitude = "k";
+ if (pos >= divider*base) { divider *= base; magnitude = "m"; }
+ if (pos >= divider*base) { divider *= base; magnitude = "g"; }
+ const int64_t tens = (n / (divider/10));
+ const long whole = (long)(tens/10);
+ const long frac1 = (long)(tens%10);
+ snprintf(buf, len, "%ld.%ld %s%s", whole, frac1, magnitude, suffix);
+ }
+ _mi_fprintf(out, arg, (fmt==NULL ? "%11s" : fmt), buf);
+}
+
+
+static void mi_print_amount(int64_t n, int64_t unit, mi_output_fun* out, void* arg) {
+ mi_printf_amount(n,unit,out,arg,NULL);
+}
+
+static void mi_print_count(int64_t n, int64_t unit, mi_output_fun* out, void* arg) {
+ if (unit==1) _mi_fprintf(out, arg, "%11s"," ");
+ else mi_print_amount(n,0,out,arg);
+}
+
+static void mi_stat_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg ) {
+ _mi_fprintf(out, arg,"%10s:", msg);
+ if (unit>0) {
+ mi_print_amount(stat->peak, unit, out, arg);
+ mi_print_amount(stat->allocated, unit, out, arg);
+ mi_print_amount(stat->freed, unit, out, arg);
+ mi_print_amount(unit, 1, out, arg);
+ mi_print_count(stat->allocated, unit, out, arg);
+ if (stat->allocated > stat->freed)
+ _mi_fprintf(out, arg, " not all freed!\n");
+ else
+ _mi_fprintf(out, arg, " ok\n");
+ }
+ else if (unit<0) {
+ mi_print_amount(stat->peak, -1, out, arg);
+ mi_print_amount(stat->allocated, -1, out, arg);
+ mi_print_amount(stat->freed, -1, out, arg);
+ if (unit==-1) {
+ _mi_fprintf(out, arg, "%22s", "");
+ }
+ else {
+ mi_print_amount(-unit, 1, out, arg);
+ mi_print_count((stat->allocated / -unit), 0, out, arg);
+ }
+ if (stat->allocated > stat->freed)
+ _mi_fprintf(out, arg, " not all freed!\n");
+ else
+ _mi_fprintf(out, arg, " ok\n");
+ }
+ else {
+ mi_print_amount(stat->peak, 1, out, arg);
+ mi_print_amount(stat->allocated, 1, out, arg);
+ _mi_fprintf(out, arg, "\n");
+ }
+}
+
+static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg ) {
+ _mi_fprintf(out, arg, "%10s:", msg);
+ mi_print_amount(stat->total, -1, out, arg);
+ _mi_fprintf(out, arg, "\n");
+}
+
+static void mi_stat_counter_print_avg(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg) {
+ const int64_t avg_tens = (stat->count == 0 ? 0 : (stat->total*10 / stat->count));
+ const long avg_whole = (long)(avg_tens/10);
+ const long avg_frac1 = (long)(avg_tens%10);
+ _mi_fprintf(out, arg, "%10s: %5ld.%ld avg\n", msg, avg_whole, avg_frac1);
+}
+
+
+static void mi_print_header(mi_output_fun* out, void* arg ) {
+ _mi_fprintf(out, arg, "%10s: %10s %10s %10s %10s %10s\n", "heap stats", "peak ", "total ", "freed ", "unit ", "count ");
+}
+
+#if MI_STAT>1
+static void mi_stats_print_bins(mi_stat_count_t* all, const mi_stat_count_t* bins, size_t max, const char* fmt, mi_output_fun* out, void* arg) {
+ bool found = false;
+ char buf[64];
+ for (size_t i = 0; i <= max; i++) {
+ if (bins[i].allocated > 0) {
+ found = true;
+ int64_t unit = _mi_bin_size((uint8_t)i);
+ snprintf(buf, 64, "%s %3zu", fmt, i);
+ mi_stat_add(all, &bins[i], unit);
+ mi_stat_print(&bins[i], buf, unit, out, arg);
+ }
+ }
+ //snprintf(buf, 64, "%s all", fmt);
+ //mi_stat_print(all, buf, 1);
+ if (found) {
+ _mi_fprintf(out, arg, "\n");
+ mi_print_header(out, arg);
+ }
+}
+#endif
+
+
+
+//------------------------------------------------------------
+// Use an output wrapper for line-buffered output
+// (which is nice when using loggers etc.)
+//------------------------------------------------------------
+typedef struct buffered_s {
+ mi_output_fun* out; // original output function
+ void* arg; // and state
+ char* buf; // local buffer of at least size `count+1`
+ size_t used; // currently used chars `used <= count`
+ size_t count; // total chars available for output
+} buffered_t;
+
+static void mi_buffered_flush(buffered_t* buf) {
+ buf->buf[buf->used] = 0;
+ _mi_fputs(buf->out, buf->arg, NULL, buf->buf);
+ buf->used = 0;
+}
+
+static void mi_buffered_out(const char* msg, void* arg) {
+ buffered_t* buf = (buffered_t*)arg;
+ if (msg==NULL || buf==NULL) return;
+ for (const char* src = msg; *src != 0; src++) {
+ char c = *src;
+ if (buf->used >= buf->count) mi_buffered_flush(buf);
+ mi_assert_internal(buf->used < buf->count);
+ buf->buf[buf->used++] = c;
+ if (c == '\n') mi_buffered_flush(buf);
+ }
+}
+
+//------------------------------------------------------------
+// Print statistics
+//------------------------------------------------------------
+
+static void mi_process_info(mi_msecs_t* utime, mi_msecs_t* stime, size_t* peak_rss, size_t* page_faults, size_t* page_reclaim, size_t* peak_commit);
+
+static void _mi_stats_print(mi_stats_t* stats, mi_msecs_t elapsed, mi_output_fun* out0, void* arg0) mi_attr_noexcept {
+ // wrap the output function to be line buffered
+ char buf[256];
+ buffered_t buffer = { out0, arg0, buf, 0, 255 };
+ mi_output_fun* out = &mi_buffered_out;
+ void* arg = &buffer;
+
+ // and print using that
+ mi_print_header(out,arg);
+ #if MI_STAT>1
+ mi_stat_count_t normal = { 0,0,0,0 };
+ mi_stats_print_bins(&normal, stats->normal, MI_BIN_HUGE, "normal",out,arg);
+ mi_stat_print(&normal, "normal", 1, out, arg);
+ mi_stat_print(&stats->huge, "huge", (stats->huge_count.count == 0 ? 1 : -(stats->huge.allocated / stats->huge_count.count)), out, arg);
+ mi_stat_print(&stats->giant, "giant", (stats->giant_count.count == 0 ? 1 : -(stats->giant.allocated / stats->giant_count.count)), out, arg);
+ mi_stat_count_t total = { 0,0,0,0 };
+ mi_stat_add(&total, &normal, 1);
+ mi_stat_add(&total, &stats->huge, 1);
+ mi_stat_add(&total, &stats->giant, 1);
+ mi_stat_print(&total, "total", 1, out, arg);
+ _mi_fprintf(out, arg, "malloc requested: ");
+ mi_print_amount(stats->malloc.allocated, 1, out, arg);
+ _mi_fprintf(out, arg, "\n\n");
+ #endif
+ mi_stat_print(&stats->reserved, "reserved", 1, out, arg);
+ mi_stat_print(&stats->committed, "committed", 1, out, arg);
+ mi_stat_print(&stats->reset, "reset", 1, out, arg);
+ mi_stat_print(&stats->page_committed, "touched", 1, out, arg);
+ mi_stat_print(&stats->segments, "segments", -1, out, arg);
+ mi_stat_print(&stats->segments_abandoned, "-abandoned", -1, out, arg);
+ mi_stat_print(&stats->segments_cache, "-cached", -1, out, arg);
+ mi_stat_print(&stats->pages, "pages", -1, out, arg);
+ mi_stat_print(&stats->pages_abandoned, "-abandoned", -1, out, arg);
+ mi_stat_counter_print(&stats->pages_extended, "-extended", out, arg);
+ mi_stat_counter_print(&stats->page_no_retire, "-noretire", out, arg);
+ mi_stat_counter_print(&stats->mmap_calls, "mmaps", out, arg);
+ mi_stat_counter_print(&stats->commit_calls, "commits", out, arg);
+ mi_stat_print(&stats->threads, "threads", -1, out, arg);
+ mi_stat_counter_print_avg(&stats->searches, "searches", out, arg);
+ _mi_fprintf(out, arg, "%10s: %7i\n", "numa nodes", _mi_os_numa_node_count());
+ if (elapsed > 0) _mi_fprintf(out, arg, "%10s: %7ld.%03ld s\n", "elapsed", elapsed/1000, elapsed%1000);
+
+ mi_msecs_t user_time;
+ mi_msecs_t sys_time;
+ size_t peak_rss;
+ size_t page_faults;
+ size_t page_reclaim;
+ size_t peak_commit;
+ mi_process_info(&user_time, &sys_time, &peak_rss, &page_faults, &page_reclaim, &peak_commit);
+ _mi_fprintf(out, arg, "%10s: user: %ld.%03ld s, system: %ld.%03ld s, faults: %lu, reclaims: %lu, rss: ", "process", user_time/1000, user_time%1000, sys_time/1000, sys_time%1000, (unsigned long)page_faults, (unsigned long)page_reclaim );
+ mi_printf_amount((int64_t)peak_rss, 1, out, arg, "%s");
+ if (peak_commit > 0) {
+ _mi_fprintf(out, arg, ", commit charge: ");
+ mi_printf_amount((int64_t)peak_commit, 1, out, arg, "%s");
+ }
+ _mi_fprintf(out, arg, "\n");
+}
+
+static mi_msecs_t mi_time_start; // = 0
+
+static mi_stats_t* mi_stats_get_default(void) {
+ mi_heap_t* heap = mi_heap_get_default();
+ return &heap->tld->stats;
+}
+
+static void mi_stats_merge_from(mi_stats_t* stats) {
+ if (stats != &_mi_stats_main) {
+ mi_stats_add(&_mi_stats_main, stats);
+ memset(stats, 0, sizeof(mi_stats_t));
+ }
+}
+
+void mi_stats_reset(void) mi_attr_noexcept {
+ mi_stats_t* stats = mi_stats_get_default();
+ if (stats != &_mi_stats_main) { memset(stats, 0, sizeof(mi_stats_t)); }
+ memset(&_mi_stats_main, 0, sizeof(mi_stats_t));
+ mi_time_start = _mi_clock_start();
+}
+
+void mi_stats_merge(void) mi_attr_noexcept {
+ mi_stats_merge_from( mi_stats_get_default() );
+}
+
+void _mi_stats_done(mi_stats_t* stats) { // called from `mi_thread_done`
+ mi_stats_merge_from(stats);
+}
+
+void mi_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept {
+ mi_msecs_t elapsed = _mi_clock_end(mi_time_start);
+ mi_stats_merge_from(mi_stats_get_default());
+ _mi_stats_print(&_mi_stats_main, elapsed, out, arg);
+}
+
+void mi_stats_print(void* out) mi_attr_noexcept {
+ // for compatibility there is an `out` parameter (which can be `stdout` or `stderr`)
+ mi_stats_print_out((mi_output_fun*)out, NULL);
+}
+
+void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept {
+ mi_msecs_t elapsed = _mi_clock_end(mi_time_start);
+ _mi_stats_print(mi_stats_get_default(), elapsed, out, arg);
+}
+
+
+// ----------------------------------------------------------------
+// Basic timer for convenience; use milli-seconds to avoid doubles
+// ----------------------------------------------------------------
+#ifdef _WIN32
+#include <windows.h>
+static mi_msecs_t mi_to_msecs(LARGE_INTEGER t) {
+ static LARGE_INTEGER mfreq; // = 0
+ if (mfreq.QuadPart == 0LL) {
+ LARGE_INTEGER f;
+ QueryPerformanceFrequency(&f);
+ mfreq.QuadPart = f.QuadPart/1000LL;
+ if (mfreq.QuadPart == 0) mfreq.QuadPart = 1;
+ }
+ return (mi_msecs_t)(t.QuadPart / mfreq.QuadPart);
+}
+
+mi_msecs_t _mi_clock_now(void) {
+ LARGE_INTEGER t;
+ QueryPerformanceCounter(&t);
+ return mi_to_msecs(t);
+}
+#else
+#include <time.h>
+#ifdef CLOCK_REALTIME
+mi_msecs_t _mi_clock_now(void) {
+ struct timespec t;
+ clock_gettime(CLOCK_REALTIME, &t);
+ return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000);
+}
+#else
+// low resolution timer
+mi_msecs_t _mi_clock_now(void) {
+ return ((mi_msecs_t)clock() / ((mi_msecs_t)CLOCKS_PER_SEC / 1000));
+}
+#endif
+#endif
+
+
+static mi_msecs_t mi_clock_diff;
+
+mi_msecs_t _mi_clock_start(void) {
+ if (mi_clock_diff == 0.0) {
+ mi_msecs_t t0 = _mi_clock_now();
+ mi_clock_diff = _mi_clock_now() - t0;
+ }
+ return _mi_clock_now();
+}
+
+mi_msecs_t _mi_clock_end(mi_msecs_t start) {
+ mi_msecs_t end = _mi_clock_now();
+ return (end - start - mi_clock_diff);
+}
+
+
+// --------------------------------------------------------
+// Basic process statistics
+// --------------------------------------------------------
+
+#if defined(_WIN32)
+#include <windows.h>
+#include <psapi.h>
+#pragma comment(lib,"psapi.lib")
+
+static mi_msecs_t filetime_msecs(const FILETIME* ftime) {
+ ULARGE_INTEGER i;
+ i.LowPart = ftime->dwLowDateTime;
+ i.HighPart = ftime->dwHighDateTime;
+ mi_msecs_t msecs = (i.QuadPart / 10000); // FILETIME is in 100 nano seconds
+ return msecs;
+}
+static void mi_process_info(mi_msecs_t* utime, mi_msecs_t* stime, size_t* peak_rss, size_t* page_faults, size_t* page_reclaim, size_t* peak_commit) {
+ FILETIME ct;
+ FILETIME ut;
+ FILETIME st;
+ FILETIME et;
+ GetProcessTimes(GetCurrentProcess(), &ct, &et, &st, &ut);
+ *utime = filetime_msecs(&ut);
+ *stime = filetime_msecs(&st);
+
+ PROCESS_MEMORY_COUNTERS info;
+ GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
+ *peak_rss = (size_t)info.PeakWorkingSetSize;
+ *page_faults = (size_t)info.PageFaultCount;
+ *peak_commit = (size_t)info.PeakPagefileUsage;
+ *page_reclaim = 0;
+}
+
+#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__)
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+#if defined(__APPLE__) && defined(__MACH__)
+#include <mach/mach.h>
+#endif
+
+#if defined(__HAIKU__)
+#include <kernel/OS.h>
+#endif
+
+static mi_msecs_t timeval_secs(const struct timeval* tv) {
+ return ((mi_msecs_t)tv->tv_sec * 1000L) + ((mi_msecs_t)tv->tv_usec / 1000L);
+}
+
+static void mi_process_info(mi_msecs_t* utime, mi_msecs_t* stime, size_t* peak_rss, size_t* page_faults, size_t* page_reclaim, size_t* peak_commit) {
+ struct rusage rusage;
+ getrusage(RUSAGE_SELF, &rusage);
+#if !defined(__HAIKU__)
+#if defined(__APPLE__) && defined(__MACH__)
+ *peak_rss = rusage.ru_maxrss;
+#else
+ *peak_rss = rusage.ru_maxrss * 1024;
+#endif
+ *page_faults = rusage.ru_majflt;
+ *page_reclaim = rusage.ru_minflt;
+ *peak_commit = 0;
+#else
+// Haiku does not have (yet?) a way to
+// get these stats per process
+ thread_info tid;
+ area_info mem;
+ ssize_t c;
+ *peak_rss = 0;
+ *page_faults = 0;
+ *page_reclaim = 0;
+ *peak_commit = 0;
+ get_thread_info(find_thread(0), &tid);
+
+ while (get_next_area_info(tid.team, &c, &mem) == B_OK) {
+ *peak_rss += mem.ram_size;
+ }
+#endif
+ *utime = timeval_secs(&rusage.ru_utime);
+ *stime = timeval_secs(&rusage.ru_stime);
+}
+
+#else
+#ifndef __wasi__
+// WebAssembly instances are not processes
+#pragma message("define a way to get process info")
+#endif
+
+static void mi_process_info(mi_msecs_t* utime, mi_msecs_t* stime, size_t* peak_rss, size_t* page_faults, size_t* page_reclaim, size_t* peak_commit) {
+ *peak_rss = 0;
+ *page_faults = 0;
+ *page_reclaim = 0;
+ *peak_commit = 0;
+ *utime = 0;
+ *stime = 0;
+}
+#endif
--- /dev/null
+cmake_minimum_required(VERSION 3.0)
+project(mimalloc-test C CXX)
+
+# Set default build type
+if (NOT CMAKE_BUILD_TYPE)
+ if ("${CMAKE_BINARY_DIR}" MATCHES ".*(D|d)ebug$")
+ message(STATUS "No build type selected, default to *** Debug ***")
+ set(CMAKE_BUILD_TYPE "Debug")
+ else()
+ message(STATUS "No build type selected, default to *** Release ***")
+ set(CMAKE_BUILD_TYPE "Release")
+ endif()
+endif()
+
+# Import mimalloc (if installed)
+find_package(mimalloc 1.6 REQUIRED NO_SYSTEM_ENVIRONMENT_PATH)
+message(STATUS "Found mimalloc installed at: ${MIMALLOC_TARGET_DIR}")
+
+# overriding with a dynamic library
+add_executable(dynamic-override main-override.c)
+target_link_libraries(dynamic-override PUBLIC mimalloc)
+
+add_executable(dynamic-override-cxx main-override.cpp)
+target_link_libraries(dynamic-override-cxx PUBLIC mimalloc)
+
+
+# overriding with a static object file works reliable as the symbols in the
+# object file have priority over those in library files
+add_executable(static-override-obj main-override.c ${MIMALLOC_TARGET_DIR}/mimalloc.o)
+target_include_directories(static-override-obj PUBLIC ${MIMALLOC_TARGET_DIR}/include)
+target_link_libraries(static-override-obj PUBLIC pthread)
+
+
+# overriding with a static library works too if using the `mimalloc-override.h`
+# header to redefine malloc/free. (the library already overrides new/delete)
+add_executable(static-override-static main-override-static.c)
+target_link_libraries(static-override-static PUBLIC mimalloc-static)
+
+
+# overriding with a static library: this may not work if the library is linked too late
+# on the command line after the C runtime library; but we cannot control that well in CMake
+add_executable(static-override main-override.c)
+target_link_libraries(static-override PUBLIC mimalloc-static)
+
+add_executable(static-override-cxx main-override.cpp)
+target_link_libraries(static-override-cxx PUBLIC mimalloc-static)
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <mimalloc.h>
+#include <mimalloc-override.h> // redefines malloc etc.
+
+static void double_free1();
+static void double_free2();
+static void corrupt_free();
+static void block_overflow1();
+static void invalid_free();
+
+int main() {
+ mi_version();
+
+ // detect double frees and heap corruption
+ // double_free1();
+ // double_free2();
+ // corrupt_free();
+ // block_overflow1();
+ invalid_free();
+
+ void* p1 = malloc(78);
+ void* p2 = malloc(24);
+ free(p1);
+ p1 = mi_malloc(8);
+ //char* s = strdup("hello\n");
+ free(p2);
+ p2 = malloc(16);
+ p1 = realloc(p1, 32);
+ free(p1);
+ free(p2);
+ //free(s);
+ //mi_collect(true);
+
+ /* now test if override worked by allocating/freeing across the api's*/
+ //p1 = mi_malloc(32);
+ //free(p1);
+ //p2 = malloc(32);
+ //mi_free(p2);
+ mi_stats_print(NULL);
+ return 0;
+}
+
+static void invalid_free() {
+ free((void*)0xBADBEEF);
+ realloc((void*)0xBADBEEF,10);
+}
+
+static void block_overflow1() {
+ uint8_t* p = (uint8_t*)mi_malloc(17);
+ p[18] = 0;
+ free(p);
+}
+
+// The double free samples come ArcHeap [1] by Insu Yun (issue #161)
+// [1]: https://arxiv.org/pdf/1903.00503.pdf
+
+static void double_free1() {
+ void* p[256];
+ //uintptr_t buf[256];
+
+ p[0] = mi_malloc(622616);
+ p[1] = mi_malloc(655362);
+ p[2] = mi_malloc(786432);
+ mi_free(p[2]);
+ // [VULN] Double free
+ mi_free(p[2]);
+ p[3] = mi_malloc(786456);
+ // [BUG] Found overlap
+ // p[3]=0x429b2ea2000 (size=917504), p[1]=0x429b2e42000 (size=786432)
+ fprintf(stderr, "p3: %p-%p, p1: %p-%p, p2: %p\n", p[3], (uint8_t*)(p[3]) + 786456, p[1], (uint8_t*)(p[1]) + 655362, p[2]);
+}
+
+static void double_free2() {
+ void* p[256];
+ //uintptr_t buf[256];
+ // [INFO] Command buffer: 0x327b2000
+ // [INFO] Input size: 182
+ p[0] = malloc(712352);
+ p[1] = malloc(786432);
+ free(p[0]);
+ // [VULN] Double free
+ free(p[0]);
+ p[2] = malloc(786440);
+ p[3] = malloc(917504);
+ p[4] = malloc(786440);
+ // [BUG] Found overlap
+ // p[4]=0x433f1402000 (size=917504), p[1]=0x433f14c2000 (size=786432)
+ fprintf(stderr, "p1: %p-%p, p2: %p-%p\n", p[4], (uint8_t*)(p[4]) + 917504, p[1], (uint8_t*)(p[1]) + 786432);
+}
+
+
+// Try to corrupt the heap through buffer overflow
+#define N 256
+#define SZ 64
+
+static void corrupt_free() {
+ void* p[N];
+ // allocate
+ for (int i = 0; i < N; i++) {
+ p[i] = malloc(SZ);
+ }
+ // free some
+ for (int i = 0; i < N; i += (N/10)) {
+ free(p[i]);
+ p[i] = NULL;
+ }
+ // try to corrupt the free list
+ for (int i = 0; i < N; i++) {
+ if (p[i] != NULL) {
+ memset(p[i], 0, SZ+8);
+ }
+ }
+ // allocate more.. trying to trigger an allocation from a corrupted entry
+ // this may need many allocations to get there (if at all)
+ for (int i = 0; i < 4096; i++) {
+ malloc(SZ);
+ }
+}
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include <mimalloc.h>
+
+int main() {
+ mi_version(); // ensure mimalloc library is linked
+ void* p1 = malloc(78);
+ void* p2 = malloc(24);
+ free(p1);
+ p1 = malloc(8);
+ //char* s = strdup("hello\n");
+ free(p2);
+ p2 = malloc(16);
+ p1 = realloc(p1, 32);
+ free(p1);
+ free(p2);
+ //free(s);
+ //mi_collect(true);
+
+ /* now test if override worked by allocating/freeing across the api's*/
+ //p1 = mi_malloc(32);
+ //free(p1);
+ //p2 = malloc(32);
+ //mi_free(p2);
+ mi_stats_print(NULL);
+ return 0;
+}
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <mimalloc.h>
+#include <new>
+#include <vector>
+#include <future>
+#include <iostream>
+
+#include <thread>
+#include <mimalloc.h>
+#include <assert.h>
+
+#ifdef _WIN32
+#include <mimalloc-new-delete.h>
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+static void msleep(unsigned long msecs) { Sleep(msecs); }
+#else
+#include <unistd.h>
+static void msleep(unsigned long msecs) { usleep(msecs * 1000UL); }
+#endif
+
+void heap_thread_free_large(); // issue #221
+void heap_no_delete(); // issue #202
+void heap_late_free(); // issue #204
+void padding_shrink(); // issue #209
+void various_tests();
+void test_mt_shutdown();
+
+int main() {
+ mi_stats_reset(); // ignore earlier allocations
+ heap_thread_free_large();
+ heap_no_delete();
+ heap_late_free();
+ padding_shrink();
+ various_tests();
+ //test_mt_shutdown();
+ mi_stats_print(NULL);
+ return 0;
+}
+
+static void* p = malloc(8);
+
+void free_p() {
+ free(p);
+ return;
+}
+
+class Test {
+private:
+ int i;
+public:
+ Test(int x) { i = x; }
+ ~Test() { }
+};
+
+
+void various_tests() {
+ atexit(free_p);
+ void* p1 = malloc(78);
+ void* p2 = mi_malloc_aligned(16, 24);
+ free(p1);
+ p1 = malloc(8);
+ char* s = mi_strdup("hello\n");
+
+ //char* s = _strdup("hello\n");
+ //char* buf = NULL;
+ //size_t len;
+ //_dupenv_s(&buf,&len,"MIMALLOC_VERBOSE");
+ //mi_free(buf);
+
+ mi_free(p2);
+ p2 = malloc(16);
+ p1 = realloc(p1, 32);
+ free(p1);
+ free(p2);
+ mi_free(s);
+ Test* t = new Test(42);
+ delete t;
+ t = new (std::nothrow) Test(42);
+ delete t;
+}
+
+class Static {
+private:
+ void* p;
+public:
+ Static() {
+ p = malloc(64);
+ return;
+ }
+ ~Static() {
+ free(p);
+ return;
+ }
+};
+
+static Static s = Static();
+
+
+bool test_stl_allocator1() {
+ std::vector<int, mi_stl_allocator<int> > vec;
+ vec.push_back(1);
+ vec.pop_back();
+ return vec.size() == 0;
+}
+
+struct some_struct { int i; int j; double z; };
+
+bool test_stl_allocator2() {
+ std::vector<some_struct, mi_stl_allocator<some_struct> > vec;
+ vec.push_back(some_struct());
+ vec.pop_back();
+ return vec.size() == 0;
+}
+
+
+
+// Issue #202
+void heap_no_delete_worker() {
+ mi_heap_t* heap = mi_heap_new();
+ void* q = mi_heap_malloc(heap, 1024);
+ // mi_heap_delete(heap); // uncomment to prevent assertion
+}
+
+void heap_no_delete() {
+ auto t1 = std::thread(heap_no_delete_worker);
+ t1.join();
+}
+
+
+// Issue #204
+volatile void* global_p;
+
+void t1main() {
+ mi_heap_t* heap = mi_heap_new();
+ global_p = mi_heap_malloc(heap, 1024);
+ mi_heap_delete(heap);
+}
+
+void heap_late_free() {
+ auto t1 = std::thread(t1main);
+
+ msleep(2000);
+ assert(global_p);
+ mi_free((void*)global_p);
+
+ t1.join();
+}
+
+// issue #209
+static void* shared_p;
+static void alloc0(/* void* arg */)
+{
+ shared_p = mi_malloc(8);
+}
+
+void padding_shrink(void)
+{
+ auto t1 = std::thread(alloc0);
+ t1.join();
+ mi_free(shared_p);
+}
+
+
+// Issue #221
+void heap_thread_free_large_worker() {
+ mi_free(shared_p);
+}
+
+void heap_thread_free_large() {
+ for (int i = 0; i < 100; i++) {
+ shared_p = mi_malloc_aligned(2*1024*1024 + 1, 8);
+ auto t1 = std::thread(heap_thread_free_large_worker);
+ t1.join();
+ }
+}
+
+
+
+void test_mt_shutdown()
+{
+ const int threads = 5;
+ std::vector< std::future< std::vector< char* > > > ts;
+
+ auto fn = [&]()
+ {
+ std::vector< char* > ps;
+ ps.reserve(1000);
+ for (int i = 0; i < 1000; i++)
+ ps.emplace_back(new char[1]);
+ return ps;
+ };
+
+ for (int i = 0; i < threads; i++)
+ ts.emplace_back(std::async(std::launch::async, fn));
+
+ for (auto& f : ts)
+ for (auto& p : f.get())
+ delete[] p;
+
+ std::cout << "done" << std::endl;
+}
--- /dev/null
+#include <stdio.h>
+#include <assert.h>
+#include <mimalloc.h>
+
+void test_heap(void* p_out) {
+ mi_heap_t* heap = mi_heap_new();
+ void* p1 = mi_heap_malloc(heap,32);
+ void* p2 = mi_heap_malloc(heap,48);
+ mi_free(p_out);
+ mi_heap_destroy(heap);
+ //mi_heap_delete(heap); mi_free(p1); mi_free(p2);
+}
+
+void test_large() {
+ const size_t N = 1000;
+
+ for (size_t i = 0; i < N; ++i) {
+ size_t sz = 1ull << 21;
+ char* a = mi_mallocn_tp(char,sz);
+ for (size_t k = 0; k < sz; k++) { a[k] = 'x'; }
+ mi_free(a);
+ }
+}
+
+int main() {
+ void* p1 = mi_malloc(16);
+ void* p2 = mi_malloc(1000000);
+ mi_free(p1);
+ mi_free(p2);
+ p1 = mi_malloc(16);
+ p2 = mi_malloc(16);
+ mi_free(p1);
+ mi_free(p2);
+
+ test_heap(mi_malloc(32));
+
+ p1 = mi_malloc_aligned(64, 16);
+ p2 = mi_malloc_aligned(160,24);
+ mi_free(p2);
+ mi_free(p1);
+ //test_large();
+
+ mi_collect(true);
+ mi_stats_print(NULL);
+ return 0;
+}
--- /dev/null
+Testing allocators is difficult as bugs may only surface after particular
+allocation patterns. The main approach to testing _mimalloc_ is therefore
+to have extensive internal invariant checking (see `page_is_valid` in `page.c`
+for example), which is enabled in debug mode with `-DMI_DEBUG_FULL=ON`.
+The main testing strategy is then to run [`mimalloc-bench`][bench] using full
+invariant checking to catch any potential problems over a wide range of intensive
+allocation benchmarks and programs.
+
+However, this does not test well for the entire API surface and this is tested
+with `test-api.c` when using `make test` (from `out/debug` etc). (This is
+not complete yet, please add to it.)
+
+The `main.c` and `main-override.c` are there to test if building and overriding
+from a local install works and therefore these build a separate `test/CMakeLists.txt`.
+
+[bench]: https://github.com/daanx/mimalloc-bench
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+/*
+Testing allocators is difficult as bugs may only surface after particular
+allocation patterns. The main approach to testing _mimalloc_ is therefore
+to have extensive internal invariant checking (see `page_is_valid` in `page.c`
+for example), which is enabled in debug mode with `-DMI_DEBUG_FULL=ON`.
+The main testing is then to run `mimalloc-bench` [1] using full invariant checking
+to catch any potential problems over a wide range of intensive allocation bench
+marks.
+
+However, this does not test well for the entire API surface. In this test file
+we therefore test the API over various inputs. Please add more tests :-)
+
+[1] https://github.com/daanx/mimalloc-bench
+*/
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <errno.h>
+
+#ifdef __cplusplus
+#include <vector>
+#endif
+
+#include "mimalloc.h"
+// #include "mimalloc-internal.h"
+
+// ---------------------------------------------------------------------------
+// Test macros: CHECK(name,predicate) and CHECK_BODY(name,body)
+// ---------------------------------------------------------------------------
+static int ok = 0;
+static int failed = 0;
+
+#define CHECK_BODY(name,body) \
+ do { \
+ fprintf(stderr,"test: %s... ", name ); \
+ bool result = true; \
+ do { body } while(false); \
+ if (!(result)) { \
+ failed++; \
+ fprintf(stderr, \
+ "\n FAILED: %s:%d:\n %s\n", \
+ __FILE__, \
+ __LINE__, \
+ #body); \
+ /* exit(1); */ \
+ } \
+ else { \
+ ok++; \
+ fprintf(stderr,"ok.\n"); \
+ } \
+ } while (false)
+
+#define CHECK(name,expr) CHECK_BODY(name,{ result = (expr); })
+
+// ---------------------------------------------------------------------------
+// Test functions
+// ---------------------------------------------------------------------------
+bool test_heap1();
+bool test_heap2();
+bool test_stl_allocator1();
+bool test_stl_allocator2();
+
+// ---------------------------------------------------------------------------
+// Main testing
+// ---------------------------------------------------------------------------
+int main() {
+ mi_option_disable(mi_option_verbose);
+
+ // ---------------------------------------------------
+ // Malloc
+ // ---------------------------------------------------
+
+ CHECK_BODY("malloc-zero",{
+ void* p = mi_malloc(0); mi_free(p);
+ });
+ CHECK_BODY("malloc-nomem1",{
+ result = (mi_malloc(SIZE_MAX/2) == NULL);
+ });
+ CHECK_BODY("malloc-null",{
+ mi_free(NULL);
+ });
+ CHECK_BODY("calloc-overflow",{
+ // use (size_t)&mi_calloc to get some number without triggering compiler warnings
+ result = (mi_calloc((size_t)&mi_calloc,SIZE_MAX/1000) == NULL);
+ });
+ CHECK_BODY("calloc0",{
+ result = (mi_usable_size(mi_calloc(0,1000)) <= 16);
+ });
+
+ // ---------------------------------------------------
+ // Extended
+ // ---------------------------------------------------
+ CHECK_BODY("posix_memalign1", {
+ void* p = &p;
+ int err = mi_posix_memalign(&p, sizeof(void*), 32);
+ result = ((err==0 && (uintptr_t)p % sizeof(void*) == 0) || p==&p);
+ mi_free(p);
+ });
+ CHECK_BODY("posix_memalign_no_align", {
+ void* p = &p;
+ int err = mi_posix_memalign(&p, 3, 32);
+ result = (err==EINVAL && p==&p);
+ });
+ CHECK_BODY("posix_memalign_zero", {
+ void* p = &p;
+ int err = mi_posix_memalign(&p, sizeof(void*), 0);
+ mi_free(p);
+ result = (err==0);
+ });
+ CHECK_BODY("posix_memalign_nopow2", {
+ void* p = &p;
+ int err = mi_posix_memalign(&p, 3*sizeof(void*), 32);
+ result = (err==EINVAL && p==&p);
+ });
+ CHECK_BODY("posix_memalign_nomem", {
+ void* p = &p;
+ int err = mi_posix_memalign(&p, sizeof(void*), SIZE_MAX);
+ result = (err==ENOMEM && p==&p);
+ });
+
+ // ---------------------------------------------------
+ // Aligned API
+ // ---------------------------------------------------
+ CHECK_BODY("malloc-aligned1", {
+ void* p = mi_malloc_aligned(32,32); result = (p != NULL && (uintptr_t)(p) % 32 == 0); mi_free(p);
+ });
+ CHECK_BODY("malloc-aligned2", {
+ void* p = mi_malloc_aligned(48,32); result = (p != NULL && (uintptr_t)(p) % 32 == 0); mi_free(p);
+ });
+ CHECK_BODY("malloc-aligned3", {
+ void* p1 = mi_malloc_aligned(48,32); bool result1 = (p1 != NULL && (uintptr_t)(p1) % 32 == 0);
+ void* p2 = mi_malloc_aligned(48,32); bool result2 = (p2 != NULL && (uintptr_t)(p2) % 32 == 0);
+ mi_free(p2);
+ mi_free(p1);
+ result = (result1&&result2);
+ });
+ CHECK_BODY("malloc-aligned4", {
+ void* p;
+ bool ok = true;
+ for (int i = 0; i < 8 && ok; i++) {
+ p = mi_malloc_aligned(8, 16);
+ ok = (p != NULL && (uintptr_t)(p) % 16 == 0); mi_free(p);
+ }
+ result = ok;
+ });
+ CHECK_BODY("malloc-aligned5", {
+ void* p = mi_malloc_aligned(4097,4096); size_t usable = mi_usable_size(p); result = usable >= 4097 && usable < 10000; mi_free(p);
+ });
+ CHECK_BODY("malloc-aligned-at1", {
+ void* p = mi_malloc_aligned_at(48,32,0); result = (p != NULL && ((uintptr_t)(p) + 0) % 32 == 0); mi_free(p);
+ });
+ CHECK_BODY("malloc-aligned-at2", {
+ void* p = mi_malloc_aligned_at(50,32,8); result = (p != NULL && ((uintptr_t)(p) + 8) % 32 == 0); mi_free(p);
+ });
+ CHECK_BODY("memalign1", {
+ void* p;
+ bool ok = true;
+ for (int i = 0; i < 8 && ok; i++) {
+ p = mi_memalign(16,8);
+ ok = (p != NULL && (uintptr_t)(p) % 16 == 0); mi_free(p);
+ }
+ result = ok;
+ });
+
+ // ---------------------------------------------------
+ // Heaps
+ // ---------------------------------------------------
+ CHECK("heap_destroy", test_heap1());
+ CHECK("heap_delete", test_heap2());
+
+ //mi_stats_print(NULL);
+
+ // ---------------------------------------------------
+ // various
+ // ---------------------------------------------------
+ CHECK_BODY("realpath", {
+ char* s = mi_realpath( ".", NULL );
+ // printf("realpath: %s\n",s);
+ mi_free(s);
+ });
+
+ CHECK("stl_allocator1", test_stl_allocator1());
+ CHECK("stl_allocator2", test_stl_allocator2());
+
+ // ---------------------------------------------------
+ // Done
+ // ---------------------------------------------------[]
+ fprintf(stderr,"\n\n---------------------------------------------\n"
+ "succeeded: %i\n"
+ "failed : %i\n\n", ok, failed);
+ return failed;
+}
+
+// ---------------------------------------------------
+// Larger test functions
+// ---------------------------------------------------
+
+bool test_heap1() {
+ mi_heap_t* heap = mi_heap_new();
+ int* p1 = mi_heap_malloc_tp(heap,int);
+ int* p2 = mi_heap_malloc_tp(heap,int);
+ *p1 = *p2 = 43;
+ mi_heap_destroy(heap);
+ return true;
+}
+
+bool test_heap2() {
+ mi_heap_t* heap = mi_heap_new();
+ int* p1 = mi_heap_malloc_tp(heap,int);
+ int* p2 = mi_heap_malloc_tp(heap,int);
+ mi_heap_delete(heap);
+ *p1 = 42;
+ mi_free(p1);
+ mi_free(p2);
+ return true;
+}
+
+bool test_stl_allocator1() {
+#ifdef __cplusplus
+ std::vector<int, mi_stl_allocator<int> > vec;
+ vec.push_back(1);
+ vec.pop_back();
+ return vec.size() == 0;
+#else
+ return true;
+#endif
+}
+
+struct some_struct { int i; int j; double z; };
+
+bool test_stl_allocator2() {
+#ifdef __cplusplus
+ std::vector<some_struct, mi_stl_allocator<some_struct> > vec;
+ vec.push_back(some_struct());
+ vec.pop_back();
+ return vec.size() == 0;
+#else
+ return true;
+#endif
+}
--- /dev/null
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018,2019 Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license.
+-----------------------------------------------------------------------------*/
+
+/* This is a stress test for the allocator, using multiple threads and
+ transferring objects between threads. It tries to reflect real-world workloads:
+ - allocation size is distributed linearly in powers of two
+ - with some fraction extra large (and some extra extra large)
+ - the allocations are initialized and read again at free
+ - pointers transfer between threads
+ - threads are terminated and recreated with some objects surviving in between
+ - uses deterministic "randomness", but execution can still depend on
+ (random) thread scheduling. Do not use this test as a benchmark!
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <mimalloc.h>
+
+// > mimalloc-test-stress [THREADS] [SCALE] [ITER]
+//
+// argument defaults
+static int THREADS = 32; // more repeatable if THREADS <= #processors
+static int SCALE = 10; // scaling factor
+static int ITER = 50; // N full iterations destructing and re-creating all threads
+
+// static int THREADS = 8; // more repeatable if THREADS <= #processors
+// static int SCALE = 100; // scaling factor
+
+#define STRESS // undefine for leak test
+
+static bool allow_large_objects = true; // allow very large objects?
+static size_t use_one_size = 0; // use single object size of `N * sizeof(uintptr_t)`?
+
+
+#ifdef USE_STD_MALLOC
+#define custom_calloc(n,s) calloc(n,s)
+#define custom_realloc(p,s) realloc(p,s)
+#define custom_free(p) free(p)
+#else
+#define custom_calloc(n,s) mi_calloc(n,s)
+#define custom_realloc(p,s) mi_realloc(p,s)
+#define custom_free(p) mi_free(p)
+#endif
+
+// transfer pointer between threads
+#define TRANSFERS (1000)
+static volatile void* transfer[TRANSFERS];
+
+
+#if (UINTPTR_MAX != UINT32_MAX)
+const uintptr_t cookie = 0xbf58476d1ce4e5b9UL;
+#else
+const uintptr_t cookie = 0x1ce4e5b9UL;
+#endif
+
+static void* atomic_exchange_ptr(volatile void** p, void* newval);
+
+typedef uintptr_t* random_t;
+
+static uintptr_t pick(random_t r) {
+ uintptr_t x = *r;
+#if (UINTPTR_MAX > UINT32_MAX)
+ // by Sebastiano Vigna, see: <http://xoshiro.di.unimi.it/splitmix64.c>
+ x ^= x >> 30;
+ x *= 0xbf58476d1ce4e5b9UL;
+ x ^= x >> 27;
+ x *= 0x94d049bb133111ebUL;
+ x ^= x >> 31;
+#else
+ // by Chris Wellons, see: <https://nullprogram.com/blog/2018/07/31/>
+ x ^= x >> 16;
+ x *= 0x7feb352dUL;
+ x ^= x >> 15;
+ x *= 0x846ca68bUL;
+ x ^= x >> 16;
+#endif
+ *r = x;
+ return x;
+}
+
+static bool chance(size_t perc, random_t r) {
+ return (pick(r) % 100 <= perc);
+}
+
+static void* alloc_items(size_t items, random_t r) {
+ if (chance(1, r)) {
+ if (chance(1, r) && allow_large_objects) items *= 10000; // 0.01% giant
+ else if (chance(10, r) && allow_large_objects) items *= 1000; // 0.1% huge
+ else items *= 100; // 1% large objects;
+ }
+ if (items == 40) items++; // pthreads uses that size for stack increases
+ if (use_one_size > 0) items = (use_one_size / sizeof(uintptr_t));
+ if (items==0) items = 1;
+ uintptr_t* p = (uintptr_t*)custom_calloc(items,sizeof(uintptr_t));
+ if (p != NULL) {
+ for (uintptr_t i = 0; i < items; i++) {
+ p[i] = (items - i) ^ cookie;
+ }
+ }
+ return p;
+}
+
+static void free_items(void* p) {
+ if (p != NULL) {
+ uintptr_t* q = (uintptr_t*)p;
+ uintptr_t items = (q[0] ^ cookie);
+ for (uintptr_t i = 0; i < items; i++) {
+ if ((q[i] ^ cookie) != items - i) {
+ fprintf(stderr, "memory corruption at block %p at %zu\n", p, i);
+ abort();
+ }
+ }
+ }
+ custom_free(p);
+}
+
+
+static void stress(intptr_t tid) {
+ //bench_start_thread();
+ uintptr_t r = (tid * 43); // rand();
+ const size_t max_item_shift = 5; // 128
+ const size_t max_item_retained_shift = max_item_shift + 2;
+ size_t allocs = 100 * ((size_t)SCALE) * (tid % 8 + 1); // some threads do more
+ size_t retain = allocs / 2;
+ void** data = NULL;
+ size_t data_size = 0;
+ size_t data_top = 0;
+ void** retained = (void**)custom_calloc(retain,sizeof(void*));
+ size_t retain_top = 0;
+
+ while (allocs > 0 || retain > 0) {
+ if (retain == 0 || (chance(50, &r) && allocs > 0)) {
+ // 50%+ alloc
+ allocs--;
+ if (data_top >= data_size) {
+ data_size += 100000;
+ data = (void**)custom_realloc(data, data_size * sizeof(void*));
+ }
+ data[data_top++] = alloc_items(1ULL << (pick(&r) % max_item_shift), &r);
+ }
+ else {
+ // 25% retain
+ retained[retain_top++] = alloc_items( 1ULL << (pick(&r) % max_item_retained_shift), &r);
+ retain--;
+ }
+ if (chance(66, &r) && data_top > 0) {
+ // 66% free previous alloc
+ size_t idx = pick(&r) % data_top;
+ free_items(data[idx]);
+ data[idx] = NULL;
+ }
+ if (chance(25, &r) && data_top > 0) {
+ // 25% exchange a local pointer with the (shared) transfer buffer.
+ size_t data_idx = pick(&r) % data_top;
+ size_t transfer_idx = pick(&r) % TRANSFERS;
+ void* p = data[data_idx];
+ void* q = atomic_exchange_ptr(&transfer[transfer_idx], p);
+ data[data_idx] = q;
+ }
+ }
+ // free everything that is left
+ for (size_t i = 0; i < retain_top; i++) {
+ free_items(retained[i]);
+ }
+ for (size_t i = 0; i < data_top; i++) {
+ free_items(data[i]);
+ }
+ custom_free(retained);
+ custom_free(data);
+ //bench_end_thread();
+}
+
+static void run_os_threads(size_t nthreads, void (*entry)(intptr_t tid));
+
+static void test_stress(void) {
+ uintptr_t r = rand();
+ for (int n = 0; n < ITER; n++) {
+ run_os_threads(THREADS, &stress);
+ for (int i = 0; i < TRANSFERS; i++) {
+ if (chance(50, &r) || n + 1 == ITER) { // free all on last run, otherwise free half of the transfers
+ void* p = atomic_exchange_ptr(&transfer[i], NULL);
+ free_items(p);
+ }
+ }
+ // mi_collect(false);
+#ifndef NDEBUG
+ if ((n + 1) % 10 == 0) { printf("- iterations left: %3d\n", ITER - (n + 1)); }
+#endif
+ }
+}
+
+#ifndef STRESS
+static void leak(intptr_t tid) {
+ uintptr_t r = rand();
+ void* p = alloc_items(1 /*pick(&r)%128*/, &r);
+ if (chance(50, &r)) {
+ intptr_t i = (pick(&r) % TRANSFERS);
+ void* q = atomic_exchange_ptr(&transfer[i], p);
+ free_items(q);
+ }
+}
+
+static void test_leak(void) {
+ for (int n = 0; n < ITER; n++) {
+ run_os_threads(THREADS, &leak);
+ mi_collect(false);
+#ifndef NDEBUG
+ if ((n + 1) % 10 == 0) { printf("- iterations left: %3d\n", ITER - (n + 1)); }
+#endif
+ }
+}
+#endif
+
+int main(int argc, char** argv) {
+ // > mimalloc-test-stress [THREADS] [SCALE] [ITER]
+ if (argc >= 2) {
+ char* end;
+ long n = strtol(argv[1], &end, 10);
+ if (n > 0) THREADS = n;
+ }
+ if (argc >= 3) {
+ char* end;
+ long n = (strtol(argv[2], &end, 10));
+ if (n > 0) SCALE = n;
+ }
+ if (argc >= 4) {
+ char* end;
+ long n = (strtol(argv[3], &end, 10));
+ if (n > 0) ITER = n;
+ }
+ printf("Using %d threads with a %d%% load-per-thread and %d iterations\n", THREADS, SCALE, ITER);
+ //int res = mi_reserve_huge_os_pages(4,1);
+ //printf("(reserve huge: %i\n)", res);
+
+ //bench_start_program();
+
+ // Run ITER full iterations where half the objects in the transfer buffer survive to the next round.
+ srand(0x7feb352d);
+ // mi_stats_reset();
+#ifdef STRESS
+ test_stress();
+#else
+ test_leak();
+#endif
+
+ // mi_collect(true);
+ mi_stats_print(NULL);
+ //bench_end_program();
+ return 0;
+}
+
+
+static void (*thread_entry_fun)(intptr_t) = &stress;
+
+#ifdef _WIN32
+
+#include <windows.h>
+
+static DWORD WINAPI thread_entry(LPVOID param) {
+ thread_entry_fun((intptr_t)param);
+ return 0;
+}
+
+static void run_os_threads(size_t nthreads, void (*fun)(intptr_t)) {
+ thread_entry_fun = fun;
+ DWORD* tids = (DWORD*)custom_calloc(nthreads,sizeof(DWORD));
+ HANDLE* thandles = (HANDLE*)custom_calloc(nthreads,sizeof(HANDLE));
+ for (uintptr_t i = 0; i < nthreads; i++) {
+ thandles[i] = CreateThread(0, 4096, &thread_entry, (void*)(i), 0, &tids[i]);
+ }
+ for (size_t i = 0; i < nthreads; i++) {
+ WaitForSingleObject(thandles[i], INFINITE);
+ }
+ for (size_t i = 0; i < nthreads; i++) {
+ CloseHandle(thandles[i]);
+ }
+ custom_free(tids);
+ custom_free(thandles);
+}
+
+static void* atomic_exchange_ptr(volatile void** p, void* newval) {
+#if (INTPTR_MAX == INT32_MAX)
+ return (void*)InterlockedExchange((volatile LONG*)p, (LONG)newval);
+#else
+ return (void*)InterlockedExchange64((volatile LONG64*)p, (LONG64)newval);
+#endif
+}
+#else
+
+#include <pthread.h>
+
+static void* thread_entry(void* param) {
+ thread_entry_fun((uintptr_t)param);
+ return NULL;
+}
+
+static void run_os_threads(size_t nthreads, void (*fun)(intptr_t)) {
+ thread_entry_fun = fun;
+ pthread_t* threads = (pthread_t*)custom_calloc(nthreads,sizeof(pthread_t));
+ memset(threads, 0, sizeof(pthread_t) * nthreads);
+ //pthread_setconcurrency(nthreads);
+ for (uintptr_t i = 0; i < nthreads; i++) {
+ pthread_create(&threads[i], NULL, &thread_entry, (void*)i);
+ }
+ for (size_t i = 0; i < nthreads; i++) {
+ pthread_join(threads[i], NULL);
+ }
+ custom_free(threads);
+}
+
+#ifdef __cplusplus
+#include <atomic>
+static void* atomic_exchange_ptr(volatile void** p, void* newval) {
+ return std::atomic_exchange((volatile std::atomic<void*>*)p, newval);
+}
+#else
+#include <stdatomic.h>
+static void* atomic_exchange_ptr(volatile void** p, void* newval) {
+ return atomic_exchange((volatile _Atomic(void*)*)p, newval);
+}
+#endif
+
+#endif
--- /dev/null
+/.flatpak-builder
+/build-dir
+/repo
+*.flatpak
--- /dev/null
+#!/bin/sh -ex
+cd $(dirname $0)
+flatpak-builder "$@" --force-clean --repo repo build-dir com.solvespace.SolveSpace.json
+flatpak build-bundle repo solvespace.flatpak com.solvespace.SolveSpace
--- /dev/null
+{
+ "app-id": "com.solvespace.SolveSpace",
+ "runtime": "org.gnome.Platform",
+ "runtime-version": "3.30",
+ "sdk": "org.gnome.Sdk",
+ "finish-args": [
+ /* Access to display server and OpenGL */
+ "--share=ipc", "--socket=fallback-x11", "--socket=wayland", "--device=dri",
+ /* Access to save files */
+ "--filesystem=home"
+ ],
+ "cleanup": [
+ "/include", "/lib/*/include",
+ "*.a", "*.la", "*.m4", "/lib/libslvs*.so*", "/lib/libglibmm_generate_extra_defs*.so*",
+ "/share/pkgconfig", "*.pc",
+ "/share/man", "/share/doc",
+ "/share/aclocal",
+ /* mm-common junk */
+ "/bin/mm-common-prepare",
+ "/share/mm-common"
+ ],
+ "command": "solvespace",
+ "modules": [
+ {
+ "name": "mm-common",
+ "sources": [
+ {
+ "type": "archive",
+ "url": "http://ftp.gnome.org/pub/GNOME/sources/mm-common/0.9/mm-common-0.9.12.tar.xz",
+ "sha256": "ceffdcce1e5b52742884c233ec604bf6fded12eea9da077ce7a62c02c87e7c0b"
+ }
+ ]
+ },
+ {
+ "name": "sigc++",
+ "config-opts": [
+ "--disable-documentation"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "http://ftp.gnome.org/pub/GNOME/sources/libsigc++/2.10/libsigc++-2.10.1.tar.xz",
+ "sha256": "c9a25f26178c6cbb147f9904d8c533b5a5c5111a41ac2eb781eb734eea446003"
+ }
+ ]
+ },
+ {
+ "name": "glibmm",
+ "config-opts": [
+ "--disable-documentation"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "http://ftp.gnome.org/pub/GNOME/sources/glibmm/2.58/glibmm-2.58.1.tar.xz",
+ "sha256": "6e5fe03bdf1e220eeffd543e017fd2fb15bcec9235f0ffd50674aff9362a85f0"
+ }
+ ]
+ },
+ {
+ "name": "cairomm",
+ "config-opts": [
+ "--disable-documentation"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "http://ftp.gnome.org/pub/GNOME/sources/cairomm/1.12/cairomm-1.12.0.tar.xz",
+ "sha256": "a54ada8394a86182525c0762e6f50db6b9212a2109280d13ec6a0b29bfd1afe6"
+ }
+ ]
+ },
+ {
+ "name": "pangomm",
+ "config-opts": [
+ "--disable-documentation"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "http://ftp.gnome.org/pub/GNOME/sources/pangomm/2.40/pangomm-2.40.2.tar.xz",
+ "sha256": "0a97aa72513db9088ca3034af923484108746dba146e98ed76842cf858322d05"
+ }
+ ]
+ },
+ {
+ "name": "atkmm",
+ "config-opts": [
+ "--disable-documentation"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "http://ftp.gnome.org/pub/GNOME/sources/atkmm/2.28/atkmm-2.28.0.tar.xz",
+ "sha256": "4c4cfc917fd42d3879ce997b463428d6982affa0fb660cafcc0bc2d9afcedd3a"
+ }
+ ]
+ },
+ {
+ "name": "gtkmm",
+ "config-opts": [
+ "--disable-documentation"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "http://ftp.gnome.org/pub/GNOME/sources/gtkmm/3.24/gtkmm-3.24.1.tar.xz",
+ "sha256": "ddfe42ed2458a20a34de252854bcf4b52d3f0c671c045f56b42aa27c7542d2fd"
+ }
+ ]
+ },
+ {
+ "name": "libjson-c",
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://s3.amazonaws.com/json-c_releases/releases/json-c-0.13.1-nodoc.tar.gz",
+ "sha256": "94a26340c0785fcff4f46ff38609cf84ebcd670df0c8efd75d039cc951d80132"
+ }
+ ],
+ "buildsystem": "cmake",
+ "builddir": true
+ },
+ {
+ "name": "SolveSpace",
+ "sources": [
+ {
+ "type": "git",
+ "path": "/home/whitequark/Projects/solvespace"
+ }
+ ],
+ "buildsystem": "cmake",
+ "builddir": true,
+ "config-opts": [
+ "-DFLATPAK=ON",
+ "-DENABLE_CLI=OFF",
+ "-DENABLE_TESTS=OFF"
+ ]
+ }
+ ]
+}
--- /dev/null
+*.snap
+solvespace-snap-src
+squashfs-root
--- /dev/null
+#!/bin/sh -xe
+
+dir="$(dirname "$(readlink -f "$0")")"
+solvespace_snap_src="$dir/solvespace-snap-src"
+trap "rm -rf $solvespace_snap_src" EXIT
+
+cd "$dir"
+
+git_root="$(git rev-parse --show-toplevel)"
+rsync --filter=":- .gitignore" -r "$git_root"/ "$solvespace_snap_src"
+
+snapcraft "$@"
--- /dev/null
+name: solvespace
+base: core18
+summary: Parametric 2d/3d CAD
+adopt-info: solvespace
+description: |
+ SOLVESPACE is a free (GPLv3) parametric 3d CAD tool.
+ Applications include
+ * modeling 3d parts — draw with extrudes, revolves, and Boolean (union / difference) operations
+ * modeling 2d parts — draw the part as a single section, and export DXF, PDF, SVG; use 3d assembly to verify fit
+ * 3d-printed parts — export the STL or other triangle mesh expected by most 3d printers
+ * preparing CAM data — export 2d vector art for a waterjet machine or laser cutter; or generate STEP or STL, for import into third-party CAM software for machining
+ * mechanism design — use the constraint solver to simulate planar or spatial linkages, with pin, ball, or slide joints
+ * plane and solid geometry — replace hand-solved trigonometry and spreadsheets with a live dimensioned drawing
+
+confinement: strict
+license: GPL-3.0
+
+layout:
+ /usr/share/solvespace:
+ symlink: $SNAP/usr/share/solvespace
+
+apps:
+ solvespace:
+ command: usr/bin/solvespace
+ desktop: solvespace.desktop
+ extensions: [gnome-3-34]
+ plugs: [opengl, unity7, home, removable-media, gsettings, network]
+ environment:
+ __EGL_VENDOR_LIBRARY_DIRS: $SNAP/gnome-platform/usr/share/glvnd/egl_vendor.d:$SNAP/usr/share/glvnd/egl_vendor.d
+ cli:
+ command: usr/bin/solvespace-cli
+ extensions: [gnome-3-34]
+ plugs: [home, removable-media, network]
+
+parts:
+ solvespace:
+ plugin: cmake
+ source: ./solvespace-snap-src
+ source-type: local
+ override-pull: |
+ snapcraftctl pull
+ version_major=$(grep "solvespace_VERSION_MAJOR" CMakeLists.txt | tr -d "()" | cut -d" " -f2)
+ version_minor=$(grep "solvespace_VERSION_MINOR" CMakeLists.txt | tr -d "()" | cut -d" " -f2)
+ version="$version_major.$version_minor~$(git rev-parse --short=8 HEAD)"
+ snapcraftctl set-version "$version"
+ git describe --exact-match HEAD && grade="stable" || grade="devel"
+ snapcraftctl set-grade "$grade"
+ git submodule update --init extlib/libdxfrw extlib/flatbuffers extlib/q3d extlib/mimalloc
+ configflags:
+ - -DCMAKE_INSTALL_PREFIX=/usr
+ - -DCMAKE_BUILD_TYPE=Release
+ - -DENABLE_TESTS=OFF
+ - -DSNAP=ON
+ - -DENABLE_OPENMP=ON
+ - -DENABLE_LTO=ON
+ build-packages:
+ - zlib1g-dev
+ - libpng-dev
+ - libfreetype6-dev
+ - libjson-c-dev
+ - libgl-dev
+ - libsigc++-2.0-dev
+ - libspnav-dev
+ - git
+ - g++
+ stage-packages:
+ - libspnav0
+ - libsigc++-2.0-0v5
+
+ cleanup:
+ after: [solvespace]
+ plugin: nil
+ build-snaps: [core18, gnome-3-34-1804]
+ override-prime: |
+ # Remove all files from snap that are already included in the base snap or in
+ # any connected content snaps
+ set -eux
+ for snap in "core18" "gnome-3-34-1804"; do # List all content-snaps and base snaps you're using here
+ cd "/snap/$snap/current" && find . -type f,l -exec rm -f "$SNAPCRAFT_PRIME/{}" \;
+ done
--- /dev/null
+# First, set up registration functions for the kinds of resources we handle.
+set(resource_root ${CMAKE_CURRENT_SOURCE_DIR}/)
+set(resource_list)
+if(WIN32)
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/win32/versioninfo.rc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/win32/versioninfo.rc)
+
+ set(rc_file ${CMAKE_CURRENT_BINARY_DIR}/resources.rc)
+ file(WRITE ${rc_file} "// Autogenerated; do not edit\n")
+ file(APPEND ${rc_file} "#include <windows.h>\n")
+ file(APPEND ${rc_file} "#include \"${CMAKE_CURRENT_BINARY_DIR}/win32/versioninfo.rc\"\n")
+
+ function(add_resource name)
+ set(source ${CMAKE_CURRENT_SOURCE_DIR}/${name})
+
+ if(${ARGC} GREATER 1)
+ set(id ${ARGV1})
+ else()
+ string(REPLACE ${resource_root} "" id ${source})
+ endif()
+ if(${ARGC} GREATER 2)
+ set(type ${ARGV2})
+ else()
+ set(type RCDATA)
+ endif()
+ file(SHA512 "${source}" hash)
+ file(APPEND ${rc_file} "${id} ${type} \"${source}\" // ${hash}\n")
+ # CMake doesn't track file dependencies across directories, so we force
+ # a reconfigure (which changes the RC file because of the hash above)
+ # every time a resource is changed.
+ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${source}")
+ endfunction()
+elseif(APPLE)
+ set(app_resource_dir ${CMAKE_BINARY_DIR}/bin/SolveSpace.app/Contents/Resources)
+ set(cli_resource_dir ${CMAKE_BINARY_DIR}/res)
+
+ function(add_resource name)
+ set(source ${CMAKE_CURRENT_SOURCE_DIR}/${name})
+ set(target_app ${app_resource_dir}/${name})
+ set(target_cli ${cli_resource_dir}/${name})
+ set(resource_list "${resource_list};${target_app};${target_cli}" PARENT_SCOPE)
+
+ get_filename_component(target_app_dir ${target_app} DIRECTORY)
+ get_filename_component(target_cli_dir ${target_cli} DIRECTORY)
+ add_custom_command(
+ OUTPUT ${target_app} ${target_cli}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${target_app_dir}
+ COMMAND ${CMAKE_COMMAND} -E copy ${source} ${target_app}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${target_cli_dir}
+ COMMAND ${CMAKE_COMMAND} -E copy ${source} ${target_cli}
+ COMMENT "Copying resource ${name}"
+ DEPENDS ${source}
+ VERBATIM)
+ endfunction()
+
+ function(add_xib name)
+ set(source ${CMAKE_CURRENT_SOURCE_DIR}/${name})
+ get_filename_component(basename ${name} NAME_WE)
+ set(target ${app_resource_dir}/${basename}.nib)
+ set(resource_list "${resource_list};${target}" PARENT_SCOPE)
+
+ add_custom_command(
+ OUTPUT ${target}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${app_resource_dir}
+ COMMAND ibtool --errors --warnings --notices --output-format human-readable-text
+ --compile ${target} ${source}
+ COMMENT "Building Interface Builder file ${name}"
+ DEPENDS ${source}
+ VERBATIM)
+ endfunction()
+
+ function(add_iconset name)
+ set(source ${CMAKE_CURRENT_SOURCE_DIR}/${name})
+ get_filename_component(basename ${name} NAME_WE)
+ set(target ${app_resource_dir}/${basename}.icns)
+ set(resource_list "${resource_list};${target}" PARENT_SCOPE)
+
+ add_custom_command(
+ OUTPUT ${target}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${app_resource_dir}
+ COMMAND iconutil -c icns -o ${target} ${source}
+ COMMENT "Building icon set ${name}"
+ DEPENDS ${source}
+ VERBATIM)
+ endfunction()
+else() # Unix
+ include(GNUInstallDirs)
+
+ set(app_resource_dir ${CMAKE_BINARY_DIR}/res)
+
+ function(add_resource name)
+ set(source ${CMAKE_CURRENT_SOURCE_DIR}/${name})
+ set(target ${app_resource_dir}/${name})
+ set(resource_list "${resource_list};${target}" PARENT_SCOPE)
+
+ get_filename_component(target_dir ${target} DIRECTORY)
+ add_custom_command(
+ OUTPUT ${target}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${target_dir}
+ COMMAND ${CMAKE_COMMAND} -E copy ${source} ${target}
+ COMMENT "Copying resource ${name}"
+ DEPENDS ${source}
+ VERBATIM)
+
+ get_filename_component(name_dir ${name} DIRECTORY)
+ install(FILES ${source}
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/solvespace/${name_dir})
+ endfunction()
+endif()
+
+function(add_resources)
+ foreach(name ${ARGN})
+ add_resource(${name})
+ set(resource_list "${resource_list}" PARENT_SCOPE)
+ endforeach()
+endfunction()
+
+# Second, register all resources.
+if(WIN32)
+ add_resource(win32/icon.ico 4000 ICON)
+ add_resource(win32/manifest.xml 1 RT_MANIFEST)
+elseif(APPLE)
+ add_iconset (cocoa/AppIcon.iconset)
+ add_xib (cocoa/MainMenu.xib)
+ add_xib (cocoa/SaveFormatAccessory.xib)
+else()
+ add_resource(freedesktop/solvespace-48x48.png)
+
+ if(FLATPAK)
+ configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/freedesktop/solvespace-flatpak.desktop.in
+ ${CMAKE_CURRENT_BINARY_DIR}/freedesktop/solvespace-flatpak.desktop)
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freedesktop/solvespace-flatpak.desktop
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications
+ RENAME com.solvespace.SolveSpace.desktop)
+
+ install(FILES freedesktop/solvespace-flatpak-mime.xml
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages
+ RENAME com.solvespace.SolveSpace-slvs.xml)
+
+ install(FILES freedesktop/solvespace-scalable.svg
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps
+ RENAME com.solvespace.SolveSpace.svg)
+ install(FILES freedesktop/solvespace-scalable.svg
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/mimetypes
+ RENAME com.solvespace.SolveSpace.svg)
+
+ foreach(SIZE 16x16 24x24 32x32 48x48)
+ install(FILES freedesktop/solvespace-${SIZE}.png
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/apps
+ RENAME com.solvespace.SolveSpace.png)
+ install(FILES freedesktop/solvespace-${SIZE}.png
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/mimetypes
+ RENAME com.solvespace.SolveSpace.png)
+ endforeach()
+ elseif(SNAP)
+ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/freedesktop/solvespace-snap.desktop
+ DESTINATION /
+ RENAME solvespace.desktop)
+
+ # snapd does not support registering new mime types
+
+ install(FILES freedesktop/solvespace-scalable.svg
+ DESTINATION /meta/icons/hicolor/scalable/apps
+ RENAME snap.solvespace.svg)
+
+ foreach(SIZE 16x16 24x24 32x32 48x48)
+ install(FILES freedesktop/solvespace-${SIZE}.png
+ DESTINATION /meta/icons/hicolor/${SIZE}/apps
+ RENAME snap.solvespace.png)
+ endforeach()
+ else()
+ configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/freedesktop/solvespace.desktop.in
+ ${CMAKE_CURRENT_BINARY_DIR}/freedesktop/solvespace.desktop)
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freedesktop/solvespace.desktop
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
+
+ install(FILES freedesktop/solvespace-mime.xml
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages
+ RENAME solvespace-slvs.xml)
+
+ install(FILES freedesktop/solvespace-scalable.svg
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps
+ RENAME solvespace.svg)
+ install(FILES freedesktop/solvespace-scalable.svg
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/mimetypes
+ RENAME application.x-solvespace.svg)
+
+ foreach(SIZE 16x16 24x24 32x32 48x48)
+ install(FILES freedesktop/solvespace-${SIZE}.png
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/apps
+ RENAME solvespace.png)
+ install(FILES freedesktop/solvespace-${SIZE}.png
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/mimetypes
+ RENAME application.x-solvespace.png)
+ endforeach()
+
+ foreach(SIZE 16x16 24x24 32x32 48x48)
+ install(FILES freedesktop/solvespace-${SIZE}.xpm
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps)
+ endforeach()
+ endif()
+endif()
+
+add_resources(
+ banner.txt
+ icons/graphics-window/angle.png
+ icons/graphics-window/arc.png
+ icons/graphics-window/assemble.png
+ icons/graphics-window/bezier.png
+ icons/graphics-window/circle.png
+ icons/graphics-window/construction.png
+ icons/graphics-window/equal.png
+ icons/graphics-window/extrude.png
+ icons/graphics-window/horiz.png
+ icons/graphics-window/image.png
+ icons/graphics-window/in3d.png
+ icons/graphics-window/lathe.png
+ icons/graphics-window/length.png
+ icons/graphics-window/line.png
+ icons/graphics-window/ontoworkplane.png
+ icons/graphics-window/other-supp.png
+ icons/graphics-window/parallel.png
+ icons/graphics-window/perpendicular.png
+ icons/graphics-window/pointonx.png
+ icons/graphics-window/point.png
+ icons/graphics-window/rectangle.png
+ icons/graphics-window/ref.png
+ icons/graphics-window/same-orientation.png
+ icons/graphics-window/sketch-in-3d.png
+ icons/graphics-window/sketch-in-plane.png
+ icons/graphics-window/step-rotate.png
+ icons/graphics-window/step-translate.png
+ icons/graphics-window/symmetric.png
+ icons/graphics-window/tangent-arc.png
+ icons/graphics-window/text.png
+ icons/graphics-window/trim.png
+ icons/graphics-window/vert.png
+ icons/text-window/constraint.png
+ icons/text-window/construction.png
+ icons/text-window/edges.png
+ icons/text-window/faces.png
+ icons/text-window/occluded-visible.png
+ icons/text-window/occluded-stippled.png
+ icons/text-window/occluded-invisible.png
+ icons/text-window/mesh.png
+ icons/text-window/normal.png
+ icons/text-window/outlines.png
+ icons/text-window/point.png
+ icons/text-window/shaded.png
+ icons/text-window/workplane.png
+ locales.txt
+ locales/de_DE.po
+ locales/en_US.po
+ locales/fr_FR.po
+ locales/uk_UA.po
+ locales/ru_RU.po
+ locales/zh_CN.po
+ fonts/unifont.hex.gz
+ fonts/private/0-check-false.png
+ fonts/private/1-check-true.png
+ fonts/private/2-radio-false.png
+ fonts/private/3-radio-true.png
+ fonts/private/4-stipple-dot.png
+ fonts/private/5-stipple-dash-long.png
+ fonts/private/6-stipple-dash.png
+ fonts/private/7-stipple-zigzag.png
+ fonts/unicode.lff.gz
+ fonts/BitstreamVeraSans-Roman-builtin.ttf
+ shaders/imesh.frag
+ shaders/imesh.vert
+ shaders/imesh_point.frag
+ shaders/imesh_point.vert
+ shaders/imesh_tex.frag
+ shaders/imesh_texa.frag
+ shaders/imesh_tex.vert
+ shaders/mesh.frag
+ shaders/mesh.vert
+ shaders/mesh_fill.frag
+ shaders/mesh_fill.vert
+ shaders/edge.frag
+ shaders/edge.vert
+ shaders/outline.vert
+ threejs/three-r76.js.gz
+ threejs/hammer-2.0.8.js.gz
+ threejs/SolveSpaceControls.js)
+
+# Third, distribute the resources.
+add_custom_target(resources
+ DEPENDS ${resource_list})
+if(WIN32)
+ set_property(TARGET resources PROPERTY EXTRA_SOURCES ${rc_file})
+endif()
--- /dev/null
+SolveSpace!
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="12F45" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
+ </dependencies>
+ <objects>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-2" userLabel="File's Owner" customClass="NSApplication"/>
+ <customObject id="-3" userLabel="Application"/>
+ <customObject id="-4" userLabel="Application Delegate" customClass="SSApplicationDelegate"/>
+ <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+ <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+ <items>
+ <menuItem title="SolveSpace" id="1Xt-HY-uBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="SolveSpace" systemMenu="apple" id="uQy-DD-JDr">
+ <items>
+ <menuItem title="About SolveSpace" id="5kV-Vb-QxS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+ <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW">
+ <connections>
+ <action selector="preferences:" target="-4" id="xyv-2f-7kO"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+ <menuItem title="Services" id="NMo-om-nkz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+ <menuItem title="Hide SolveSpace" keyEquivalent="h" id="Olw-nP-bQN">
+ <connections>
+ <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All" id="Kd2-mp-pUS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+ <menuItem title="Quit SolveSpace" keyEquivalent="q" id="4sb-4s-VLi">
+ <connections>
+ <action selector="terminate:" target="-3" id="Te7-pn-YzF"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </objects>
+</document>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="12F45" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
+ <capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="NSViewController">
+ <connections>
+ <outlet property="textField" destination="z9Z-cA-QIW" id="w4z-a4-Khs"/>
+ <outlet property="button" destination="nNy-fR-AhK" id="w3z-a4-Khs"/>
+ <outlet property="view" destination="c22-O7-iKe" id="w2z-a4-Khs"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application"/>
+ <customView id="c22-O7-iKe">
+ <rect key="frame" x="0.0" y="0.0" width="294" height="51"/>
+ <subviews>
+ <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nNy-fR-AhK">
+ <rect key="frame" x="105" y="12" width="34" height="26"/>
+ <popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="f8z-Qp-Igm">
+ <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="menu"/>
+ <menu key="menu" title="OtherViews" id="VgN-HZ-Q4a"/>
+ </popUpButtonCell>
+ <connections>
+ <binding destination="-2" name="selectedIndex" keyPath="index" id="AXx-uh-tee"/>
+ </connections>
+ </popUpButton>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="z9Z-cA-QIW">
+ <rect key="frame" x="17" y="17" width="73" height="17"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="File Format:" id="2vD-ht-BhF">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ </subviews>
+ <constraints>
+ <constraint firstItem="nNy-fR-AhK" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" constant="15" id="B3P-9V-lvu"/>
+ <constraint firstItem="nNy-fR-AhK" firstAttribute="leading" secondItem="z9Z-cA-QIW" secondAttribute="trailing" constant="20" id="Uex-s1-7UF"/>
+ <constraint firstAttribute="bottom" secondItem="nNy-fR-AhK" secondAttribute="bottom" constant="15" id="pPM-eQ-gOD"/>
+ <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="nNy-fR-AhK" secondAttribute="trailing" constant="20" id="rPT-bl-Md7"/>
+ <constraint firstItem="z9Z-cA-QIW" firstAttribute="baseline" secondItem="nNy-fR-AhK" secondAttribute="baseline" constant="1" id="spD-eU-Frq"/>
+ <constraint firstItem="nNy-fR-AhK" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="centerX" constant="-40" id="whX-1w-qMB"/>
+ </constraints>
+ </customView>
+ <userDefaultsController representsSharedInstance="YES" id="ybp-e7-hms"/>
+ </objects>
+</document>
--- /dev/null
+/* XPM */
+static char *solvespace_16x16[] = {
+/* columns rows colors chars-per-pixel */
+"16 16 5 1 ",
+" c black",
+". c #1ED500",
+"X c #DE00D6",
+"o c #CBCBCB",
+"O c None",
+/* pixels */
+"OOO OOOOOOOOOOOO",
+"OOO OOOOOOOOOOOO",
+"OOO OOOOOOOOOOOO",
+"OOO OOOOOXOOOOOO",
+"OOO OOOOOXoOOOOO",
+"OOO OOOOOXoOOOOO",
+"OOO OOOOOXoOOOOO",
+"OOO OOOOOXoOOOOO",
+"OOO OOOOOXoOOOOO",
+"OOO OOXXXXXXXOOO",
+"OOO OOOoooooooOO",
+"OO...OOOOOOOOOOO",
+" ... ",
+"OO...OOOOOOOOOOO",
+"OOO OOOOOOOOOOOO",
+"OOO OOOOOOOOOOOO"
+};
--- /dev/null
+/* XPM */
+static char *solvespace_24x24[] = {
+/* columns rows colors chars-per-pixel */
+"24 24 5 1 ",
+" c black",
+". c #1ED500",
+"X c #DE00D6",
+"o c #CBCBCB",
+"O c None",
+/* pixels */
+"OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOO OOOOOOOOOOOOOOOO",
+"OOOOOOO OOOOOOOOOOOOOOOO",
+"OOOOOOO OOOOOOOOOOOOOOOO",
+"OOOOOOO OOOOOXOOOOOOOOOO",
+"OOOOOOO OOOOOXoOOOOOOOOO",
+"OOOOOOO OOOOOXoOOOOOOOOO",
+"OOOOOOO OOOOOXoOOOOOOOOO",
+"OOOOOOO OOOOOXoOOOOOOOOO",
+"OOOOOOO OOOOOXoOOOOOOOOO",
+"OOOOOOO OOXXXXXXXOOOOOOO",
+"OOOOOOO OOOoooooooOOOOOO",
+"OOOOOO...OOOOOOOOOOOOOOO",
+"OOOO ... OOOO",
+"OOOOOO...OOOOOOOOOOOOOOO",
+"OOOOOOO OOOOOOOOOOOOOOOO",
+"OOOOOOO OOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOO"
+};
--- /dev/null
+/* XPM */
+static char *solvespace_32x32[] = {
+/* columns rows colors chars-per-pixel */
+"32 32 5 1 ",
+" c black",
+". c #1ED500",
+"X c #DE00D6",
+"o c #CBCBCB",
+"O c None",
+/* pixels */
+"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOXXOOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
+"OOOOOO OOOOXXXXXXXXXXXXOOOOOOOO",
+"OOOOOO OOOOXXXXXXXXXXXXOOOOOOOO",
+"OOOOOO OOOOOooooooooooooOOOOOOO",
+"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
+"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
+" ...... ",
+" ...... ",
+"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
+"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO"
+};
--- /dev/null
+/* XPM */
+static char *solvespace_48x48[] = {
+/* columns rows colors chars-per-pixel */
+"48 48 5 1 ",
+" c black",
+". c #1ED500",
+"X c #DE00D6",
+"o c #CBCBCB",
+"O c None",
+/* pixels */
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOXXOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOXXXXXXXXXXXXOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOXXXXXXXXXXXXOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOooooooooooooOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOO ...... OOOOOOOO",
+"OOOOOOOO ...... OOOOOOOO",
+"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
+"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO"
+};
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
+ <mime-type type="application/x-solvespace">
+ <comment xml:lang="en">SolveSpace model</comment>
+ <generic-icon name="com.solvespace.SolveSpace"/>
+ <glob pattern="*.slvs"/>
+ </mime-type>
+</mime-info>
--- /dev/null
+[Desktop Entry]
+Version=1.0
+Name=SolveSpace
+Comment=A parametric 2d/3d CAD
+Exec=${CMAKE_INSTALL_FULL_BINDIR}/solvespace
+MimeType=application/x-solvespace
+Icon=com.solvespace.SolveSpace
+Type=Application
+Categories=Graphics
+Keywords=parametric;cad;2d;3d;
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
+ <mime-type type="application/x-solvespace">
+ <comment xml:lang="en">SolveSpace model</comment>
+ <generic-icon name="solvespace"/>
+ <glob pattern="*.slvs"/>
+ </mime-type>
+</mime-info>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<svg version="1.1" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
+ <g>
+ <path d="m116 152h84v12h-84z" fill="#cbcbcb"/>
+ <path d="m152 80h12v74h-12z" fill="#cbcbcb"/>
+ <path d="m104 140h84v12h-84z" fill="#e40cf2"/>
+ <path d="m140 68h12v74h-12z" fill="#e40cf2"/>
+ <path d="m68 32h12v192h-12z"/>
+ <path d="m32 176h192v12h-192z"/>
+ <path d="m56 164h36v36h-36z" fill="#43f20c"/>
+ </g>
+</svg>
--- /dev/null
+[Desktop Entry]
+Version=1.0
+Name=SolveSpace
+Comment=A parametric 2d/3d CAD
+Exec=solvespace
+MimeType=application/x-solvespace
+Icon=${SNAP}/meta/icons/hicolor/scalable/apps/snap.solvespace.svg
+Type=Application
+Categories=Graphics
+Keywords=parametric;cad;2d;3d;
--- /dev/null
+[Desktop Entry]
+Version=1.0
+Name=SolveSpace
+Comment=A parametric 2d/3d CAD
+Exec=${CMAKE_INSTALL_FULL_BINDIR}/solvespace
+MimeType=application/x-solvespace
+Icon=solvespace
+Type=Application
+Categories=Graphics
+Keywords=parametric;cad;2d;3d;
--- /dev/null
+# This file lists the ISO locale codes (ISO 639-1/ISO 3166-1), Windows LCIDs,
+# and human-readable names for every culture supported by SolveSpace.
+de-DE,0407,Deutsch
+en-US,0409,English (US)
+fr-FR,040C,Français
+ru-RU,0419,Русский
+uk-UA,0422,Українська
+zh-CN,0804,简体中文
--- /dev/null
+# German translations for the SolveSpace package.
+# Copyright (C) 2018 the SolveSpace authors
+# This file is distributed under the same license as the SolveSpace package.
+# Guido Hoss <netstuff@peakfox.com>, 2018. #zanata
+msgid ""
+msgstr ""
+"Project-Id-Version: SolveSpace 3.0\n"
+"Report-Msgid-Bugs-To: whitequark@whitequark.org\n"
+"POT-Creation-Date: 2020-11-17 20:50-0500\n"
+"PO-Revision-Date: 2018-07-19 06:55+0000\n"
+"Last-Translator: Reini Urban <rurban@cpan.org>\n"
+"Language-Team: none\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Zanata 4.5.0\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: clipboard.cpp:274
+msgid ""
+"Cut, paste, and copy work only in a workplane.\n"
+"\n"
+"Activate one with Sketch -> In Workplane."
+msgstr ""
+"Ausschneiden, Einfügen und Kopieren sind nur in einer Arbeitsebene "
+"zulässig.\n"
+"\n"
+"Aktivieren Sie eine mit Skizze -> In Arbeitsebene"
+
+#: clipboard.cpp:291
+msgid "Clipboard is empty; nothing to paste."
+msgstr "Zwischenablage ist leer; es gibt nichts einzufügen."
+
+#: clipboard.cpp:338
+msgid "Number of copies to paste must be at least one."
+msgstr "Die Anzahl der einzufügenden Kopien muss mind. 1 sein."
+
+#: clipboard.cpp:354 textscreens.cpp:783
+msgid "Scale cannot be zero."
+msgstr "Maßstab kann nicht Null sein."
+
+#: clipboard.cpp:396
+msgid "Select one point to define origin of rotation."
+msgstr "Wählen Sie einen Punkt, um den Drehmittelpunkt zu definieren."
+
+#: clipboard.cpp:408
+msgid "Select two points to define translation vector."
+msgstr "Wählen Sie zwei Punkte, um den Verschiebungsvektor zu definieren."
+
+#: clipboard.cpp:418
+msgid ""
+"Transformation is identity. So all copies will be exactly on top of each "
+"other."
+msgstr ""
+"Die Transformation ist die Identität. Alle Kopien werden deckungsgleich "
+"übereinanderliegen."
+
+#: clipboard.cpp:422
+msgid "Too many items to paste; split this into smaller pastes."
+msgstr ""
+"Zuviele Objekte zum Einfügen; teilen Sie diese in kleinere "
+"Einfügeoperationen auf."
+
+#: clipboard.cpp:427
+msgid "No workplane active."
+msgstr "Es ist keine Arbeitsebene aktiv."
+
+#: confscreen.cpp:410
+msgid "Bad format: specify coordinates as x, y, z"
+msgstr "Ungültiges Format: geben Sie Koordinaten als x, y, z an"
+
+#: confscreen.cpp:420 style.cpp:659 textscreens.cpp:805
+msgid "Bad format: specify color as r, g, b"
+msgstr "Ungültiges Format: geben Sie Farben als r, g, b an"
+
+#: confscreen.cpp:446
+msgid ""
+"The perspective factor will have no effect until you enable View -> Use "
+"Perspective Projection."
+msgstr ""
+"Der Perspektivfaktor wird sich nicht auswirken, bis Sie Ansicht -> "
+"Perspektive Projektion aktivieren."
+
+#: confscreen.cpp:459 confscreen.cpp:469
+#, c-format
+msgid "Specify between 0 and %d digits after the decimal."
+msgstr "Geben Sie 0 bis %d Ziffern nach dem Dezimalzeichen an."
+
+#: confscreen.cpp:481
+msgid "Export scale must not be zero!"
+msgstr "Der Exportmaßstab darf nicht Null sein!"
+
+#: confscreen.cpp:493
+msgid "Cutter radius offset must not be negative!"
+msgstr "Der Werkzeugradialabstand darf nicht negativ sein!"
+
+#: confscreen.cpp:547
+msgid "Bad value: autosave interval should be positive"
+msgstr ""
+"Ungültiger Wert: Interval für automatisches Speichern muss positiv sein"
+
+#: confscreen.cpp:550
+msgid "Bad format: specify interval in integral minutes"
+msgstr "Ungültiges Format: geben Sie das Interval in ganzen Minuten an"
+
+#: constraint.cpp:12
+msgctxt "constr-name"
+msgid "pts-coincident"
+msgstr "Pkt-Deckung"
+
+#: constraint.cpp:13
+msgctxt "constr-name"
+msgid "pt-pt-distance"
+msgstr "Pkt-Pkt-Abstand"
+
+#: constraint.cpp:14
+msgctxt "constr-name"
+msgid "pt-line-distance"
+msgstr "Pkt-Linie-Abstand"
+
+#: constraint.cpp:15
+msgctxt "constr-name"
+msgid "pt-plane-distance"
+msgstr "Pkt-Ebene-Abstand"
+
+#: constraint.cpp:16
+msgctxt "constr-name"
+msgid "pt-face-distance"
+msgstr "Pkt-Fläche-Abstand"
+
+#: constraint.cpp:17
+msgctxt "constr-name"
+msgid "proj-pt-pt-distance"
+msgstr "Proj-Pkt-Pkt-Abstand"
+
+#: constraint.cpp:18
+msgctxt "constr-name"
+msgid "pt-in-plane"
+msgstr "Pkt-in-Ebene"
+
+#: constraint.cpp:19
+msgctxt "constr-name"
+msgid "pt-on-line"
+msgstr "Pkt-auf-Linie"
+
+#: constraint.cpp:20
+msgctxt "constr-name"
+msgid "pt-on-face"
+msgstr "Pkt-auf-Fläche"
+
+#: constraint.cpp:21
+msgctxt "constr-name"
+msgid "eq-length"
+msgstr "gl-Länge"
+
+#: constraint.cpp:22
+msgctxt "constr-name"
+msgid "eq-length-and-pt-ln-dist"
+msgstr "gl-Länge-und-Pkt-Linie-Abstand"
+
+#: constraint.cpp:23
+msgctxt "constr-name"
+msgid "eq-pt-line-distances"
+msgstr "gl-Pkt-Linie-Abstände"
+
+#: constraint.cpp:24
+msgctxt "constr-name"
+msgid "length-ratio"
+msgstr "Längenverhältnis"
+
+#: constraint.cpp:25
+msgctxt "constr-name"
+msgid "length-difference"
+msgstr "Längendifferenz"
+
+#: constraint.cpp:26
+msgctxt "constr-name"
+msgid "symmetric"
+msgstr "Symmetrisch"
+
+#: constraint.cpp:27
+msgctxt "constr-name"
+msgid "symmetric-h"
+msgstr "Symmetrisch-H"
+
+#: constraint.cpp:28
+msgctxt "constr-name"
+msgid "symmetric-v"
+msgstr "Symmetrisch-V"
+
+#: constraint.cpp:29
+msgctxt "constr-name"
+msgid "symmetric-line"
+msgstr "Symmetrisch-Linie"
+
+#: constraint.cpp:30
+msgctxt "constr-name"
+msgid "at-midpoint"
+msgstr "auf-Mittelpunkt"
+
+#: constraint.cpp:31
+msgctxt "constr-name"
+msgid "horizontal"
+msgstr "Horizontal"
+
+#: constraint.cpp:32
+msgctxt "constr-name"
+msgid "vertical"
+msgstr "Vertikal"
+
+#: constraint.cpp:33
+msgctxt "constr-name"
+msgid "diameter"
+msgstr "Durchmesser"
+
+#: constraint.cpp:34
+msgctxt "constr-name"
+msgid "pt-on-circle"
+msgstr "Pkt-auf-Kreis"
+
+#: constraint.cpp:35
+msgctxt "constr-name"
+msgid "same-orientation"
+msgstr "gl-Orientierung"
+
+#: constraint.cpp:36
+msgctxt "constr-name"
+msgid "angle"
+msgstr "Winkel"
+
+#: constraint.cpp:37
+msgctxt "constr-name"
+msgid "parallel"
+msgstr "Parallel"
+
+#: constraint.cpp:38
+msgctxt "constr-name"
+msgid "arc-line-tangent"
+msgstr "Bogen-Linie-Tangente"
+
+#: constraint.cpp:39
+msgctxt "constr-name"
+msgid "cubic-line-tangent"
+msgstr "Kub-Linie-Tangente"
+
+#: constraint.cpp:40
+msgctxt "constr-name"
+msgid "curve-curve-tangent"
+msgstr "Kurve-Kurve-Tangente"
+
+#: constraint.cpp:41
+msgctxt "constr-name"
+msgid "perpendicular"
+msgstr "Rechtwinklig"
+
+#: constraint.cpp:42
+msgctxt "constr-name"
+msgid "eq-radius"
+msgstr "gl-Radius"
+
+#: constraint.cpp:43
+msgctxt "constr-name"
+msgid "eq-angle"
+msgstr "gl-Winkel"
+
+#: constraint.cpp:44
+msgctxt "constr-name"
+msgid "eq-line-len-arc-len"
+msgstr "gl-Linie-Länge-Bogen-Länge"
+
+#: constraint.cpp:45
+msgctxt "constr-name"
+msgid "lock-where-dragged"
+msgstr "Fix-an-Position"
+
+#: constraint.cpp:46
+msgctxt "constr-name"
+msgid "comment"
+msgstr "Kommentar"
+
+#: constraint.cpp:171
+msgid ""
+"Bad selection for distance / diameter constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two points (distance between points)\n"
+" * a line segment (length)\n"
+" * two points and a line segment or normal (projected distance)\n"
+" * a workplane and a point (minimum distance)\n"
+" * a line segment and a point (minimum distance)\n"
+" * a plane face and a point (minimum distance)\n"
+" * a circle or an arc (diameter)\n"
+msgstr ""
+"Ungültige Auswahl für Einschränkung Abstand / Durchmesser Diese "
+"Einschränkung ist anwendbar auf:\n"
+"\n"
+" * zwei Punkte [Abstand zwischen Punkten]\n"
+" * ein Liniensegment [Länge]\n"
+" * zwei Punkte und ein Liniensegment oder Normale [projizierter Abstand]\n"
+" * eine Arbeitsebene und ein Punkt [minimaler Abstand] \n"
+" * ein Liniensegment und ein Punkt [minimaler Abstand]\n"
+" * eine Seitenfläche und ein Punkt [minimaler Abstand]\n"
+" * ein Kreis oder ein Bogen [Durchmesser]\n"
+
+#: constraint.cpp:224
+msgid ""
+"Bad selection for on point / curve / plane constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points (points coincident)\n"
+" * a point and a workplane (point in plane)\n"
+" * a point and a line segment (point on line)\n"
+" * a point and a circle or arc (point on curve)\n"
+" * a point and a plane face (point on face)\n"
+msgstr ""
+"Ungültige Auswahl für Einschränkung \"Auf Punkt / Kurve / Ebene\". Diese "
+"Einschränkung ist anwendbar auf:\n"
+"\n"
+" * zwei Punkte [deckungsgleich]\n"
+" * einen Punkt und eine Arbeitsebene [Punkt auf Ebene]\n"
+" * einen Punkt und ein Liniensegment [Punkt auf Linie]\n"
+" * einen Punkt und einen Kreis oder Bogen [Punkt auf Kurve]\n"
+" * einen Punkt und eine Seitenfläche [Punkt auf Fläche]\n"
+
+#: constraint.cpp:286
+msgid ""
+"Bad selection for equal length / radius constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two line segments (equal length)\n"
+" * two line segments and two points (equal point-line distances)\n"
+" * a line segment and two points (equal point-line distances)\n"
+" * a line segment, and a point and line segment (point-line distance "
+"equals length)\n"
+" * four line segments or normals (equal angle between A,B and C,D)\n"
+" * three line segments or normals (equal angle between A,B and B,C)\n"
+" * two circles or arcs (equal radius)\n"
+" * a line segment and an arc (line segment length equals arc length)\n"
+msgstr ""
+"Ungültige Auswahl für Einschränkung \"gleicher Abstand / Radius\". Diese "
+"Einschränkung ist anwendbar auf:\n"
+"\n"
+" * zwei Liniensegmente [gleiche Länge]\n"
+" * zwei Liniensegmente und zwei Punkte [gleiche Punkt-Linien-Abstände]\n"
+" * ein Liniensegment und zwei Punkte [gleiche Punkt-Linien-Abstände]\n"
+" * ein Liniensegment und ein Punkt oder Liniensegment [Abstand Punkt-"
+"Linie gleich Länge]\n"
+" * vier Liniensegmente oder Normale [gleicher Winkel zwischen A,B und C,"
+"D]\n"
+" * drei Liniensegmente oder Normale [gleicher Winkel zwischen A,B und B,"
+"C]\n"
+" * zwei Kreise oder Bögen [gleicher Radius]\n"
+" * ein Liniensegment und ein Bogen [Länge des Liniensegments gleich "
+"Bogenlänge]\n"
+
+#: constraint.cpp:325
+msgid ""
+"Bad selection for length ratio constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+"Ungültige Auswahl für Einschränkung \"Längenverhältnis\". Diese "
+"Einschränkung ist anwendbar auf:\n"
+"\n"
+" * zwei Liniensegmente\n"
+
+#: constraint.cpp:342
+msgid ""
+"Bad selection for length difference constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+"Ungültige Auswahl für Einschränkung \"Längendifferenz\". Diese Einschränkung "
+"ist anwendbar auf:\n"
+"\n"
+" * zwei Liniensegmente\n"
+
+#: constraint.cpp:368
+msgid ""
+"Bad selection for at midpoint constraint. This constraint can apply to:\n"
+"\n"
+" * a line segment and a point (point at midpoint)\n"
+" * a line segment and a workplane (line's midpoint on plane)\n"
+msgstr ""
+"Ungültige Auswahl für Einschränkung \"Mittelpunkt\". Diese Einschränkung ist "
+"anwendbar auf:\n"
+"\n"
+" * ein Liniensegment und ein Punkt [Punkt auf Mittelpunkt]\n"
+" * ein Liniensegment und eine Arbeitsebene [Mittelpunkt der Linie auf "
+"Ebene]\n"
+
+#: constraint.cpp:426
+msgid ""
+"Bad selection for symmetric constraint. This constraint can apply to:\n"
+"\n"
+" * two points or a line segment (symmetric about workplane's coordinate "
+"axis)\n"
+" * line segment, and two points or a line segment (symmetric about line "
+"segment)\n"
+" * workplane, and two points or a line segment (symmetric about "
+"workplane)\n"
+msgstr ""
+"Ungültige Auswahl für Einschränkung \"Symmetrisch\". Diese Einschränkung ist "
+"anwendbar auf:\n"
+"\n"
+" * zwei Punkte oder ein Liniensegment [symmetrisch zu Koordinatenachse "
+"der Arbeitsebene]\n"
+" * ein Liniensegment und zwei Punkte oder ein Liniensegment [symmetrisch "
+"zu Liniensegment]\n"
+" * eine Arbeitsebene und zwei Punkte oder ein Liniensegment [symmetrisch "
+"zu Arbeitsebene]\n"
+
+#: constraint.cpp:440
+msgid ""
+"A workplane must be active when constraining symmetric without an explicit "
+"symmetry plane."
+msgstr ""
+"Eine Arbeitsebene muss aktiv sein, um die Symmetrie ohne explizite "
+"Symmetrieebene einzuschränken."
+
+#: constraint.cpp:470
+msgid ""
+"Activate a workplane (with Sketch -> In Workplane) before applying a "
+"horizontal or vertical constraint."
+msgstr ""
+"Aktivieren Sie eine Arbeitsebene (mit Skizze -> In Arbeitsebene), bevor Sie "
+"horizontal oder vertikal einschränken."
+
+#: constraint.cpp:483
+msgid ""
+"Bad selection for horizontal / vertical constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points\n"
+" * a line segment\n"
+msgstr ""
+"Ungültige Auswahl für Einschränkung \"horizontal / vertikal\". Diese "
+"Einschränkung ist anwendbar auf:\n"
+"\n"
+" * zwei Punkte\n"
+" * ein Liniensegment\n"
+
+#: constraint.cpp:504
+msgid ""
+"Bad selection for same orientation constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two normals\n"
+msgstr ""
+"Ungültige Auswahl für Einschränkung \"gleiche Orientierung\". Diese "
+"Einschränkung ist anwendbar auf:\n"
+"\n"
+" * zwei Normale\n"
+
+#: constraint.cpp:554
+msgid "Must select an angle constraint."
+msgstr "Sie müssen einen eingeschränkten Winkel auswählen."
+
+#: constraint.cpp:567
+msgid "Must select a constraint with associated label."
+msgstr "Sie müssen eine Einschränkung mit zugeordneter Kennzeichnung angeben."
+
+#: constraint.cpp:578
+msgid ""
+"Bad selection for angle constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+"Ungültige Auswahl für Einschränkung \"Winkel\". Diese Einschränkung ist "
+"anwendbar auf:\n"
+"\n"
+" * zwei Liniensegmente\n"
+" * ein Liniensegment und eine Normale\n"
+" * zwei Normale\n"
+
+#: constraint.cpp:635
+msgid ""
+"The tangent arc and line segment must share an endpoint. Constrain them with "
+"Constrain -> On Point before constraining tangent."
+msgstr ""
+"Die Bogentangente und das Liniensegment müssen einen gemeinsamen Endpunkt "
+"haben. Schränken Sie mit \"Einschränkung / Auf Punkt\" ein, bevor Sie die "
+"Tangente einschränken. -> Sc"
+
+#: constraint.cpp:659
+msgid ""
+"The tangent cubic and line segment must share an endpoint. Constrain them "
+"with Constrain -> On Point before constraining tangent."
+msgstr ""
+"Die Kurventangente und das Liniensegment müssen einen gemeinsamen Endpunkt "
+"haben. Schränken Sie mit \"Einschränkung / Auf Punkt\" ein, bevor Sie die "
+"Tangente einschränken. -> Sc"
+
+#: constraint.cpp:669
+msgid "Curve-curve tangency must apply in workplane."
+msgstr ""
+"Die Kurven-Kurven-Tangente muss in der Arbeitsebene eingeschränkt werden."
+
+#: constraint.cpp:687
+msgid ""
+"The curves must share an endpoint. Constrain them with Constrain -> On Point "
+"before constraining tangent."
+msgstr ""
+"Die Kurven müssen einen gemeinsamen Endpunkt haben. Schränken Sie mit "
+"\"Einschränkung / Auf Punkt\" ein, bevor Sie die Tangente einschränken. -> Sc"
+
+#: constraint.cpp:696
+msgid ""
+"Bad selection for parallel / tangent constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments (parallel)\n"
+" * a line segment and a normal (parallel)\n"
+" * two normals (parallel)\n"
+" * two line segments, arcs, or beziers, that share an endpoint (tangent)\n"
+msgstr ""
+"Ungültige Auswahl für Einschränkung \"Parallel / Tangente\". Diese "
+"Einschränkung ist anwendbar auf:\n"
+"\n"
+" * zwei Liniensegmente [parallel]\n"
+" * ein Liniensegment und eine Normale [parallel]\n"
+" * zwei Normalen [parallel]\n"
+" * zwei Liniensegmente, Bögen oder Beziers mit gemeinsamem Endpunkt "
+"[Tangente]\n"
+
+#: constraint.cpp:714
+msgid ""
+"Bad selection for perpendicular constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+"Ungültige Auswahl für Einschränkung \"Rechtwinklig\". Diese Einschränkung "
+"ist anwendbar auf:\n"
+"\n"
+" * zwei Liniensegmente\n"
+" * ein Liniensegment und eine Normale\n"
+" * zwei Normale\n"
+
+#: constraint.cpp:729
+msgid ""
+"Bad selection for lock point where dragged constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * a point\n"
+msgstr ""
+"Ungültige Auswahl für Einschränkung \"Punkt an Position\". Diese "
+"Einschränkung ist anwendbar auf:\n"
+"\n"
+" * einen Punkt\n"
+
+#: constraint.cpp:740
+msgid "click center of comment text"
+msgstr "Klicken Sie auf die Mitte des Kommentartextes"
+
+#: export.cpp:19
+msgid ""
+"No solid model present; draw one with extrudes and revolves, or use Export "
+"2d View to export bare lines and curves."
+msgstr ""
+"Kein Festkörper vorhanden; zeichnen Sie eines mit Extrusionen und Drehungen, "
+"oder exportieren Sie bloße Linien und Kurven mit \"2D-Ansicht exportieren\""
+
+#: export.cpp:61
+msgid ""
+"Bad selection for export section. Please select:\n"
+"\n"
+" * nothing, with an active workplane (workplane is section plane)\n"
+" * a face (section plane through face)\n"
+" * a point and two line segments (plane through point and parallel to "
+"lines)\n"
+msgstr ""
+"Ungültige Auswahl für teilweisen Export. Bitte wählen Sie aus:\n"
+"\n"
+" * nichts, mit einer aktiven Arbeitsebene [Arbeitsebene ist "
+"Schnittebene]\n"
+" * eine Seitenfläche [Schnittebene durch Seitenfläche]\n"
+" * einen Punkt und zwei Liniensegmente [Schnittebene durch Punkt und "
+"parallel zu Linien]\n"
+
+#: export.cpp:822
+msgid "Active group mesh is empty; nothing to export."
+msgstr "Das Netz der aktiven Gruppe ist leer; es gibt nichts zu exportieren."
+
+#: exportvector.cpp:337
+msgid "freehand lines were replaced with continuous lines"
+msgstr "Freihandlinien wurden mit durchgehenden Linien ersetzt"
+
+#: exportvector.cpp:339
+msgid "zigzag lines were replaced with continuous lines"
+msgstr "Zickzacklinien wurden mit durchgehenden Linien ersetzt"
+
+#: exportvector.cpp:593
+msgid ""
+"Some aspects of the drawing have no DXF equivalent and were not exported:\n"
+msgstr ""
+"Teile der Zeichnung haben keine Entsprechung in DXF und wurden nicht "
+"exportiert:\n"
+
+#: exportvector.cpp:839
+msgid ""
+"PDF page size exceeds 200 by 200 inches; many viewers may reject this file."
+msgstr ""
+"Die PDF-Seitengröße überschreitet 200 x 200 Zoll; die Datei kann u.U. in "
+"vielen Programmen nicht geöffnet werden."
+
+#: file.cpp:44 group.cpp:91
+msgctxt "group-name"
+msgid "sketch-in-plane"
+msgstr "Skizze-in-Ebene"
+
+#: file.cpp:62
+msgctxt "group-name"
+msgid "#references"
+msgstr "#Referenzen"
+
+#: file.cpp:549
+msgid ""
+"Unrecognized data in file. This file may be corrupt, or from a newer version "
+"of the program."
+msgstr ""
+"Nicht erkannte Daten in der Datei. Diese Datei könnte beschädigt sein oder "
+"von einer neueren Version des Programms stammen."
+
+#: file.cpp:859
+msgctxt "title"
+msgid "Missing File"
+msgstr "Fehlende Datei"
+
+#: file.cpp:860
+#, c-format
+msgctxt "dialog"
+msgid "The linked file “%s” is not present."
+msgstr "Die verlinkte Datei “%s” fehlt."
+
+#: file.cpp:862
+msgctxt "dialog"
+msgid ""
+"Do you want to locate it manually?\n"
+"\n"
+"If you decline, any geometry that depends on the missing file will be "
+"permanently removed."
+msgstr ""
+"Möchten Sie sie selber auswählen?\n"
+"Falls Sie ablehnen, wird jegliche mit der fehlenden Datei verknüpfte "
+"Geometrie verworfen."
+
+#: file.cpp:865
+msgctxt "button"
+msgid "&Yes"
+msgstr "&Ja"
+
+#: file.cpp:867
+msgctxt "button"
+msgid "&No"
+msgstr "&Nein"
+
+#: file.cpp:869
+msgctxt "button"
+msgid "&Cancel"
+msgstr "&Abbrechen"
+
+#: graphicswin.cpp:41
+msgid "&File"
+msgstr "&Datei"
+
+#: graphicswin.cpp:42
+msgid "&New"
+msgstr "&Neu"
+
+#: graphicswin.cpp:43
+msgid "&Open..."
+msgstr "&Öffnen"
+
+#: graphicswin.cpp:44
+msgid "Open &Recent"
+msgstr "Öffne &letzte"
+
+#: graphicswin.cpp:45
+msgid "&Save"
+msgstr "&Speichern"
+
+#: graphicswin.cpp:46
+msgid "Save &As..."
+msgstr "Speichern &Als…"
+
+#: graphicswin.cpp:48
+msgid "Export &Image..."
+msgstr "Exportiere &Bild…"
+
+#: graphicswin.cpp:49
+msgid "Export 2d &View..."
+msgstr "Exportiere 2D-Ansicht…"
+
+#: graphicswin.cpp:50
+msgid "Export 2d &Section..."
+msgstr "Exportiere 2D-Auswahl…"
+
+#: graphicswin.cpp:51
+msgid "Export 3d &Wireframe..."
+msgstr "Exportiere 3D-Drahtgittermodell"
+
+#: graphicswin.cpp:52
+msgid "Export Triangle &Mesh..."
+msgstr "Exportiere Dreiecksnetz…"
+
+#: graphicswin.cpp:53
+msgid "Export &Surfaces..."
+msgstr "Exportiere Oberflächen…"
+
+#: graphicswin.cpp:54
+msgid "Im&port..."
+msgstr "Im&port…"
+
+#: graphicswin.cpp:57
+msgid "E&xit"
+msgstr "Beenden"
+
+#: graphicswin.cpp:60
+msgid "&Edit"
+msgstr "&Bearbeiten"
+
+#: graphicswin.cpp:61
+msgid "&Undo"
+msgstr "&Rückgängig machen"
+
+#: graphicswin.cpp:62
+msgid "&Redo"
+msgstr "&Wiederholen"
+
+#: graphicswin.cpp:63
+msgid "Re&generate All"
+msgstr "Alles &neu zeichnen"
+
+#: graphicswin.cpp:65
+msgid "Snap Selection to &Grid"
+msgstr "Auswahl auf &Raster ausrichten"
+
+#: graphicswin.cpp:66
+msgid "Rotate Imported &90°"
+msgstr "Importierte Objekte &90° drehen"
+
+#: graphicswin.cpp:68
+msgid "Cu&t"
+msgstr "Ausschneiden"
+
+#: graphicswin.cpp:69
+msgid "&Copy"
+msgstr "Kopieren"
+
+#: graphicswin.cpp:70
+msgid "&Paste"
+msgstr "Einfügen"
+
+#: graphicswin.cpp:71
+msgid "Paste &Transformed..."
+msgstr "&Transformiert einfügen…"
+
+#: graphicswin.cpp:72
+msgid "&Delete"
+msgstr "&Löschen"
+
+#: graphicswin.cpp:74
+msgid "Select &Edge Chain"
+msgstr "&Kantenverlauf auswählen"
+
+#: graphicswin.cpp:75
+msgid "Select &All"
+msgstr "&Alle auswählen"
+
+#: graphicswin.cpp:76
+msgid "&Unselect All"
+msgstr "Alle &deselektieren"
+
+#: graphicswin.cpp:78
+msgid "&Line Styles..."
+msgstr "&Linien Stile..."
+
+#: graphicswin.cpp:79
+msgid "&View Projection..."
+msgstr "An&sichts Projektion..."
+
+#: graphicswin.cpp:81
+msgid "Con&figuration..."
+msgstr "&Einstellungen..."
+
+#: graphicswin.cpp:84
+msgid "&View"
+msgstr "Ansicht"
+
+#: graphicswin.cpp:85
+msgid "Zoom &In"
+msgstr "Zoom größer"
+
+#: graphicswin.cpp:86
+msgid "Zoom &Out"
+msgstr "Zoom kleiner"
+
+#: graphicswin.cpp:87
+msgid "Zoom To &Fit"
+msgstr "Zoom anpassen"
+
+#: graphicswin.cpp:89
+msgid "Align View to &Workplane"
+msgstr "Ansicht auf Arbeitsebene ausrichten"
+
+#: graphicswin.cpp:90
+msgid "Nearest &Ortho View"
+msgstr "Nächste Ortho-Ansicht"
+
+#: graphicswin.cpp:91
+msgid "Nearest &Isometric View"
+msgstr "Nächste isometrische Ansicht"
+
+#: graphicswin.cpp:92
+msgid "&Center View At Point"
+msgstr "Ansicht auf Punkt zentrieren"
+
+#: graphicswin.cpp:94
+msgid "Show Snap &Grid"
+msgstr "Arbeitsraster anzeigen"
+
+#: graphicswin.cpp:95
+msgid "Use &Perspective Projection"
+msgstr "Perspektivische Projektion"
+
+#: graphicswin.cpp:96
+msgid "Dimension &Units"
+msgstr "Maßeinheit"
+
+#: graphicswin.cpp:97
+msgid "Dimensions in &Millimeters"
+msgstr "Maße in Millimeter"
+
+#: graphicswin.cpp:98
+msgid "Dimensions in M&eters"
+msgstr "Masse in M&etern"
+
+#: graphicswin.cpp:99
+msgid "Dimensions in &Inches"
+msgstr "Maße in Zoll"
+
+#: graphicswin.cpp:101
+msgid "Show &Toolbar"
+msgstr "Werkzeugleiste anzeigen"
+
+#: graphicswin.cpp:102
+msgid "Show Property Bro&wser"
+msgstr "Attributbrowser anzeigen"
+
+#: graphicswin.cpp:104
+msgid "&Full Screen"
+msgstr "Vollbildschirm"
+
+#: graphicswin.cpp:106
+msgid "&New Group"
+msgstr "Neue Gruppe"
+
+#: graphicswin.cpp:107
+msgid "Sketch In &3d"
+msgstr "In 3D skizzieren"
+
+#: graphicswin.cpp:108
+msgid "Sketch In New &Workplane"
+msgstr "In neuer Arbeitsebene skizzieren"
+
+#: graphicswin.cpp:110
+msgid "Step &Translating"
+msgstr "Kopieren und verschieben"
+
+#: graphicswin.cpp:111
+msgid "Step &Rotating"
+msgstr "Kopieren und drehen"
+
+#: graphicswin.cpp:113
+msgid "E&xtrude"
+msgstr "E&xtrudieren"
+
+#: graphicswin.cpp:114
+msgid "&Helix"
+msgstr "&Helix"
+
+#: graphicswin.cpp:115
+msgid "&Lathe"
+msgstr "R&otieren"
+
+#: graphicswin.cpp:116
+msgid "Re&volve"
+msgstr "D&rehen"
+
+#: graphicswin.cpp:118
+msgid "Link / Assemble..."
+msgstr "Verknüpfen / Zusammensetzen"
+
+#: graphicswin.cpp:119
+msgid "Link Recent"
+msgstr "Letzte verknüpfen"
+
+#: graphicswin.cpp:121
+msgid "&Sketch"
+msgstr "&Skizze"
+
+#: graphicswin.cpp:122
+msgid "In &Workplane"
+msgstr "In Arbeitsebene"
+
+#: graphicswin.cpp:123
+msgid "Anywhere In &3d"
+msgstr "Im 3D-Raum"
+
+#: graphicswin.cpp:125
+msgid "Datum &Point"
+msgstr "Bezugspunkt"
+
+#: graphicswin.cpp:126
+msgid "&Workplane"
+msgstr "Arbeits&ebene"
+
+#: graphicswin.cpp:128
+msgid "Line &Segment"
+msgstr "&Linie"
+
+#: graphicswin.cpp:129
+msgid "C&onstruction Line Segment"
+msgstr "K&onstruktionslinie"
+
+#: graphicswin.cpp:130
+msgid "&Rectangle"
+msgstr "&Rechteck"
+
+#: graphicswin.cpp:131
+msgid "&Circle"
+msgstr "&Kreis"
+
+#: graphicswin.cpp:132
+msgid "&Arc of a Circle"
+msgstr "Kreisbogen"
+
+#: graphicswin.cpp:133
+msgid "&Bezier Cubic Spline"
+msgstr "Kubischer &Bezier-Spline"
+
+#: graphicswin.cpp:135
+msgid "&Text in TrueType Font"
+msgstr "&Text in Truetype-Font"
+
+#: graphicswin.cpp:136
+msgid "&Image"
+msgstr "B&ild"
+
+#: graphicswin.cpp:138
+msgid "To&ggle Construction"
+msgstr "Konstruktionselement an/aus"
+
+#: graphicswin.cpp:139
+msgid "Tangent &Arc at Point"
+msgstr "Bogentangente an Punkt"
+
+#: graphicswin.cpp:140
+msgid "Split Curves at &Intersection"
+msgstr "Kurven im Schnittpunkt trennen"
+
+#: graphicswin.cpp:142
+msgid "&Constrain"
+msgstr "&Einschränkung"
+
+#: graphicswin.cpp:143
+msgid "&Distance / Diameter"
+msgstr "Abstand / Durchmesser"
+
+#: graphicswin.cpp:144
+msgid "Re&ference Dimension"
+msgstr "Referenzangabe"
+
+#: graphicswin.cpp:145
+msgid "A&ngle"
+msgstr "Winkel"
+
+#: graphicswin.cpp:146
+msgid "Reference An&gle"
+msgstr "Referenzwinkel"
+
+#: graphicswin.cpp:147
+msgid "Other S&upplementary Angle"
+msgstr "Komplementärwinkel"
+
+#: graphicswin.cpp:148
+msgid "Toggle R&eference Dim"
+msgstr "Referenzangabe ein/aus"
+
+#: graphicswin.cpp:150
+msgid "&Horizontal"
+msgstr "Horizontal"
+
+#: graphicswin.cpp:151
+msgid "&Vertical"
+msgstr "&Vertikal"
+
+#: graphicswin.cpp:153
+msgid "&On Point / Curve / Plane"
+msgstr "Auf Punkt / Kurve / Ebene"
+
+#: graphicswin.cpp:154
+msgid "E&qual Length / Radius / Angle"
+msgstr "Gleicher Abstand / Radius / Winkel"
+
+#: graphicswin.cpp:155
+msgid "Length Ra&tio"
+msgstr "Längenverhältnis"
+
+#: graphicswin.cpp:156
+msgid "Length Diff&erence"
+msgstr "Längendifferenz"
+
+#: graphicswin.cpp:157
+msgid "At &Midpoint"
+msgstr "Auf &Mittelpunkt"
+
+#: graphicswin.cpp:158
+msgid "S&ymmetric"
+msgstr "Symmetrisch"
+
+#: graphicswin.cpp:159
+msgid "Para&llel / Tangent"
+msgstr "Paral&llel / Tangente"
+
+#: graphicswin.cpp:160
+msgid "&Perpendicular"
+msgstr "Rechtwinklig"
+
+#: graphicswin.cpp:161
+msgid "Same Orient&ation"
+msgstr "Gleiche Orientierung"
+
+#: graphicswin.cpp:162
+msgid "Lock Point Where &Dragged"
+msgstr "Punkt an Position fixieren"
+
+#: graphicswin.cpp:164
+msgid "Comment"
+msgstr "Kommentar"
+
+#: graphicswin.cpp:166
+msgid "&Analyze"
+msgstr "&Analyse"
+
+#: graphicswin.cpp:167
+msgid "Measure &Volume"
+msgstr "&Volumen bestimmen"
+
+#: graphicswin.cpp:168
+msgid "Measure A&rea"
+msgstr "Fläche bestimmen"
+
+#: graphicswin.cpp:169
+msgid "Measure &Perimeter"
+msgstr "Umfang bestimmen"
+
+#: graphicswin.cpp:170
+msgid "Show &Interfering Parts"
+msgstr "Überlagernde Teile anzeigen"
+
+#: graphicswin.cpp:171
+msgid "Show &Naked Edges"
+msgstr "Freiliegende Kanten anzeigen"
+
+#: graphicswin.cpp:172
+msgid "Show &Center of Mass"
+msgstr "Massenmittelpunkt anzeigen"
+
+#: graphicswin.cpp:174
+msgid "Show &Underconstrained Points"
+msgstr "&Unterbeschränkte Punkte anzeigen"
+
+#: graphicswin.cpp:176
+msgid "&Trace Point"
+msgstr "Punkt nachzeichnen"
+
+#: graphicswin.cpp:177
+msgid "&Stop Tracing..."
+msgstr "Nachzeichnen beenden"
+
+#: graphicswin.cpp:178
+msgid "Step &Dimension..."
+msgstr "Schrittgröße…"
+
+#: graphicswin.cpp:180
+msgid "&Help"
+msgstr "&Hilfe"
+
+#: graphicswin.cpp:181
+msgid "&Language"
+msgstr "Sprache"
+
+#: graphicswin.cpp:182
+msgid "&Website / Manual"
+msgstr "&Website / Anleitung"
+
+#: graphicswin.cpp:184
+msgid "&About"
+msgstr "Über"
+
+#: graphicswin.cpp:352
+msgid "(no recent files)"
+msgstr "(keine vorhergehenden Dateien)"
+
+#: graphicswin.cpp:360
+#, c-format
+msgid "File '%s' does not exist."
+msgstr ""
+
+#: graphicswin.cpp:721
+msgid "No workplane is active, so the grid will not appear."
+msgstr ""
+"Das Raster wird nicht angezeigt, weil keine Arbeitsebene ausgewählt ist."
+
+#: graphicswin.cpp:730
+msgid ""
+"The perspective factor is set to zero, so the view will always be a parallel "
+"projection.\n"
+"\n"
+"For a perspective projection, modify the perspective factor in the "
+"configuration screen. A value around 0.3 is typical."
+msgstr ""
+"Der Perspektivfaktor is auf Null gesetzt, also wird die Ansicht eine "
+"Parallelprojektion sein.\n"
+"\n"
+"Ändern Sie den Faktor für die Perspektivprojektion in der "
+"Konfigurationsmaske. Ein typischer Wert ist ca. 0,3."
+
+#: graphicswin.cpp:809
+msgid ""
+"Select a point; this point will become the center of the view on screen."
+msgstr ""
+"Wählen Sie einen Punkt aus; dieser Punkt wird im Mittelpunkt der "
+"Bildschirmansicht sein."
+
+#: graphicswin.cpp:1103
+msgid "No additional entities share endpoints with the selected entities."
+msgstr ""
+"Die ausgewählten Objekte teilen keine gemeinsamen Endpunkte mit anderen "
+"Objekten."
+
+#: graphicswin.cpp:1121
+msgid ""
+"To use this command, select a point or other entity from an linked part, or "
+"make a link group the active group."
+msgstr ""
+"Für diesen Befehl wählen Sie einen Punkt oder ein anderes Objekt von einem "
+"verknüpften Teil aus, oder aktivieren Sie eine verknüpfte Gruppe."
+
+#: graphicswin.cpp:1144
+msgid ""
+"No workplane is active. Activate a workplane (with Sketch -> In Workplane) "
+"to define the plane for the snap grid."
+msgstr ""
+"Es wurde keine Arbeitsebene ausgewählt. Aktivieren Sie eine Arbeitsebene "
+"(mit Skizze -> In Arbeitsebene), um die Ebene für das Gitterraster zu "
+"definieren."
+
+#: graphicswin.cpp:1151
+msgid ""
+"Can't snap these items to grid; select points, text comments, or constraints "
+"with a label. To snap a line, select its endpoints."
+msgstr ""
+"Diese Objekte können nicht auf das Raster ausgerichtet werden. Dies geht nur "
+"für Punkte, Textkommentare, oder Einschränkungen mit einer Bezeichnung. Um "
+"eine Linie auf das Raster auszurichten, wählen Sie deren Endpunkte aus."
+
+#: graphicswin.cpp:1239
+msgid "No workplane selected. Activating default workplane for this group."
+msgstr ""
+"Es wurde keine Arbeitsebene ausgewählt. Die Standard-Arbeitsebene für diese "
+"Gruppe wird aktiviert."
+
+#: graphicswin.cpp:1242
+msgid ""
+"No workplane is selected, and the active group does not have a default "
+"workplane. Try selecting a workplane, or activating a sketch-in-new-"
+"workplane group."
+msgstr ""
+"Es wurde keine Arbeitsebene ausgewählt, und die aktive Gruppe hat keine "
+"standardmäßige Arbeitsebene. Wählen Sie eine Arbeitsebene aus, oder "
+"erstellen Sie eine Gruppe in einer neuen Arbeitsebene."
+
+#: graphicswin.cpp:1263
+msgid ""
+"Bad selection for tangent arc at point. Select a single point, or select "
+"nothing to set up arc parameters."
+msgstr ""
+"Ungültige Auswahl für Bogentangente an Punkt. Wählen Sie einen einzelnen "
+"Punkt. Um die Bogenparameter anzugeben, wählen Sie nichts aus."
+
+#: graphicswin.cpp:1274
+msgid "click point on arc (draws anti-clockwise)"
+msgstr ""
+"Erstellen Sie einen Punkt auf dem Bogen (zeichnet im Gegenuhrzeigersinn)"
+
+#: graphicswin.cpp:1275
+msgid "click to place datum point"
+msgstr "Klicken Sie, um einen Bezugspunkt zu platzieren"
+
+#: graphicswin.cpp:1276
+msgid "click first point of line segment"
+msgstr "Klicken Sie auf den ersten Punkt des Liniensegments"
+
+#: graphicswin.cpp:1278
+msgid "click first point of construction line segment"
+msgstr "Klicken Sie auf den ersten Punkt der Konstruktionslinie"
+
+#: graphicswin.cpp:1279
+msgid "click first point of cubic segment"
+msgstr "Klicken Sie auf den ersten Punkt der kubischen Linie"
+
+#: graphicswin.cpp:1280
+msgid "click center of circle"
+msgstr "Klicken Sie auf den Kreismittelpunkt"
+
+#: graphicswin.cpp:1281
+msgid "click origin of workplane"
+msgstr "Klicken Sie auf den Ursprungspunkt der Arbeitsebene"
+
+#: graphicswin.cpp:1282
+msgid "click one corner of rectangle"
+msgstr "Klicken Sie auf eine Ecke des Rechtecks"
+
+#: graphicswin.cpp:1283
+msgid "click top left of text"
+msgstr "Klicken Sie auf die obere linke Ecke des Texts"
+
+#: graphicswin.cpp:1289
+msgid "click top left of image"
+msgstr "Klicken Sie auf die obere linke Ecke des Bilds"
+
+#: graphicswin.cpp:1301
+msgid ""
+"No entities are selected. Select entities before trying to toggle their "
+"construction state."
+msgstr ""
+"Es wurden keine Objekte ausgewählt Wählen Sie Objekte aus, bevor Sie sie von/"
+"zu Konstruktionsmerkmalen umwandeln."
+
+#: group.cpp:86
+msgctxt "group-name"
+msgid "sketch-in-3d"
+msgstr "Skizze-in-3D"
+
+#: group.cpp:142
+msgid ""
+"Bad selection for new sketch in workplane. This group can be created with:\n"
+"\n"
+" * a point (through the point, orthogonal to coordinate axes)\n"
+" * a point and two line segments (through the point, parallel to the "
+"lines)\n"
+" * a workplane (copy of the workplane)\n"
+msgstr ""
+"Ungültige Auswahl für Skizze in neuer Arbeitsebene Diese Gruppe kann "
+"erstellt werden mit:\n"
+"\n"
+" * einem Punkt (durch den Punkt, orthogonal zu den Koordinatenachsen)\n"
+" * einem Punkt und zwei Liniensegmenten (durch den Punkt, parallel zu den "
+"Linien)\n"
+" * einer Arbeitsebene (Kopie der Arbeitsebene)\n"
+
+#: group.cpp:154
+msgid ""
+"Activate a workplane (Sketch -> In Workplane) before extruding. The sketch "
+"will be extruded normal to the workplane."
+msgstr ""
+"Aktivieren Sie vor der Extrusion eine Arbeitsebene (mit Skizze -> In "
+"Arbeitsebene). Die Skizze wird senkrecht zur Arbeitsebene extrudiert"
+
+#: group.cpp:163
+msgctxt "group-name"
+msgid "extrude"
+msgstr "Extrusion"
+
+#: group.cpp:168
+msgid "Lathe operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:179
+msgid ""
+"Bad selection for new lathe group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+"Ungültige Auswahl für neue Gruppe mit Drehquerschnitt Diese Gruppe kann "
+"erstellt werden mit:\n"
+"\n"
+" * einem Punkt und einem Liniensegment oder einer Normale (Drehung um "
+"eine Achse parallel zur Linie/Normalen, durch den Punkt)\n"
+" * einem Liniensegment (Drehung um das Liniensegment)\n"
+
+#: group.cpp:189
+msgctxt "group-name"
+msgid "lathe"
+msgstr "Drehung"
+
+#: group.cpp:194
+msgid "Revolve operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:205
+msgid ""
+"Bad selection for new revolve group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:217
+msgctxt "group-name"
+msgid "revolve"
+msgstr ""
+
+#: group.cpp:222
+msgid "Helix operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:233
+msgid ""
+"Bad selection for new helix group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:245
+msgctxt "group-name"
+msgid "helix"
+msgstr ""
+
+#: group.cpp:258
+msgid ""
+"Bad selection for new rotation. This group can be created with:\n"
+"\n"
+" * a point, while locked in workplane (rotate in plane, about that "
+"point)\n"
+" * a point and a line or a normal (rotate about an axis through the "
+"point, and parallel to line / normal)\n"
+msgstr ""
+"Ungültige Auswahl für neue Rotation Diese Gruppe kann erstellt werden mit:\n"
+"\n"
+" * einem Punkt in vorgegebener Arbeitsebene (in der Ebene um den Punkt "
+"drehen)\n"
+" * einem Punkt und einer Linie oder einer Normale (um eine Achse durch "
+"den Punkt drehen, parallel zur Linie / Normale)\n"
+
+#: group.cpp:271
+msgctxt "group-name"
+msgid "rotate"
+msgstr "Drehen"
+
+#: group.cpp:282
+msgctxt "group-name"
+msgid "translate"
+msgstr "Versetzen"
+
+#: group.cpp:400
+msgid "(unnamed)"
+msgstr "unbenannt"
+
+#: groupmesh.cpp:708
+msgid "not closed contour, or not all same style!"
+msgstr "Kontur nicht geschlossen, oder kein einheitlicher Linientyp!"
+
+#: groupmesh.cpp:721
+msgid "points not all coplanar!"
+msgstr "Punkte sind nicht alle koplanar!"
+
+#: groupmesh.cpp:723
+msgid "contour is self-intersecting!"
+msgstr "Kontur überschneidet sich selbst!"
+
+#: groupmesh.cpp:725
+msgid "zero-length edge!"
+msgstr "Kante mit Länge Null!"
+
+#: modify.cpp:254
+msgid "Must be sketching in workplane to create tangent arc."
+msgstr "Eine Bogentangente kann nur in einer Arbeitsebene erstellt werden."
+
+#: modify.cpp:301
+msgid ""
+"To create a tangent arc, select a point where two non-construction lines or "
+"circles in this group and workplane join."
+msgstr ""
+"Um eine Bogentangente zu erstellen, wählen Sie einen Punkt, in dem sich zwei "
+"nicht-Konstruktionslinien oder -kreise in dieser Gruppe und Arbeitsebene "
+"treffen. "
+
+#: modify.cpp:388
+msgid ""
+"Couldn't round this corner. Try a smaller radius, or try creating the "
+"desired geometry by hand with tangency constraints."
+msgstr ""
+"Diese Ecke konnte nicht abgerundet werden. Versuchen Sie einen kleineren "
+"Radius, oder erstellen Sie die gewünschte Geometrie von Hand mit \"Tangente"
+"\"-Einschränkungen."
+
+#: modify.cpp:597
+msgid "Couldn't split this entity; lines, circles, or cubics only."
+msgstr ""
+"Dieses Objekt konnte nicht geteilt werden. Dies geht nur für Linien, Kreise "
+"oder kubische Splines."
+
+#: modify.cpp:624
+msgid "Must be sketching in workplane to split."
+msgstr "Trennen ist nur in einer Arbeitsebene möglich."
+
+#: modify.cpp:631
+msgid ""
+"Select two entities that intersect each other (e.g. two lines/circles/arcs "
+"or a line/circle/arc and a point)."
+msgstr ""
+"Wählen Sie zwei Objekte aus, die sich schneiden (z.B. zwei Linien/Kreise/"
+"Bögen, oder eine Linie/Kreis/Bogen und ein Punkt)."
+
+#: modify.cpp:736
+msgid "Can't split; no intersection found."
+msgstr "Trennen nicht möglich; keine Überschneidung gefunden."
+
+#: mouse.cpp:560
+msgid "Assign to Style"
+msgstr "Linientyp zuordnen"
+
+#: mouse.cpp:576
+msgid "No Style"
+msgstr "Kein Linientyp"
+
+#: mouse.cpp:579
+msgid "Newly Created Custom Style..."
+msgstr "Neu erstellter benutzerdefinierter Linientyp…"
+
+#: mouse.cpp:586
+msgid "Group Info"
+msgstr "Info zu Gruppe"
+
+#: mouse.cpp:606
+msgid "Style Info"
+msgstr "Info zu Linientyp"
+
+#: mouse.cpp:626
+msgid "Select Edge Chain"
+msgstr "Kantenverlauf auswählen"
+
+#: mouse.cpp:632
+msgid "Toggle Reference Dimension"
+msgstr "Von/zu Referenzangabe wechseln"
+
+#: mouse.cpp:638
+msgid "Other Supplementary Angle"
+msgstr "Anderer Komplementärwinkel"
+
+#: mouse.cpp:643
+msgid "Snap to Grid"
+msgstr "Auf Raster ausrichten"
+
+#: mouse.cpp:652
+msgid "Remove Spline Point"
+msgstr "Spline-Punkt löschen"
+
+#: mouse.cpp:687
+msgid "Add Spline Point"
+msgstr "Spline-Punkt hinzufügen"
+
+#: mouse.cpp:691
+msgid "Cannot add spline point: maximum number of points reached."
+msgstr ""
+"Spline-Punkt kann nicht hinzugefügt werden: maximale Anzahl der Punkte "
+"erreicht."
+
+#: mouse.cpp:716
+msgid "Toggle Construction"
+msgstr "Konstruktionselement an/aus"
+
+#: mouse.cpp:731
+msgid "Delete Point-Coincident Constraint"
+msgstr "Einschränkung \"Punkte deckungsgleich\" löschen"
+
+#: mouse.cpp:750
+msgid "Cut"
+msgstr "Ausschneiden"
+
+#: mouse.cpp:752
+msgid "Copy"
+msgstr "Kopieren"
+
+#: mouse.cpp:756
+msgid "Select All"
+msgstr "Alle auswählen"
+
+#: mouse.cpp:761
+msgid "Paste"
+msgstr "Einfügen"
+
+#: mouse.cpp:763
+msgid "Paste Transformed..."
+msgstr "Einfügen und transformieren…"
+
+#: mouse.cpp:768
+msgid "Delete"
+msgstr "Löschen"
+
+#: mouse.cpp:771
+msgid "Unselect All"
+msgstr "Alle deselektieren"
+
+#: mouse.cpp:778
+msgid "Unselect Hovered"
+msgstr "Aktive deselektieren"
+
+#: mouse.cpp:787
+msgid "Zoom to Fit"
+msgstr "Zoom an Bildschirm anpassen"
+
+#: mouse.cpp:990 mouse.cpp:1277
+msgid "click next point of line, or press Esc"
+msgstr "Klicken Sie auf den nächsten Punkt der Linie, oder drücken Sie Esc"
+
+#: mouse.cpp:996
+msgid ""
+"Can't draw rectangle in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+"Ein Rechteck kann nicht in 3D erstellt werden. Aktivieren Sie zuerst eine "
+"Arbeitsebene mit \"Skizze -> In Arbeitsebene\"."
+
+#: mouse.cpp:1030
+msgid "click to place other corner of rectangle"
+msgstr "Klicken Sie auf die gegenüberliegende Ecke des Rechtecks"
+
+#: mouse.cpp:1050
+msgid "click to set radius"
+msgstr "Klicken Sie, um den Radius festzulegen"
+
+#: mouse.cpp:1055
+msgid ""
+"Can't draw arc in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+"Ein Kreisbogen kann nicht in 3D erstellt werden. Aktivieren Sie zuerst eine "
+"Arbeitsebene mit \"Skizze -> In Arbeitsebene\"."
+
+#: mouse.cpp:1074
+msgid "click to place point"
+msgstr "Klicken Sie, um einen Punkt zu platzieren"
+
+#: mouse.cpp:1090
+msgid "click next point of cubic, or press Esc"
+msgstr ""
+"Klicken Sie auf den nächsten Punkt der kubischen Linie, oder drücken Sie Esc"
+
+#: mouse.cpp:1095
+msgid ""
+"Sketching in a workplane already; sketch in 3d before creating new workplane."
+msgstr ""
+"Eine Arbeitsebene ist bereits aktiv. Skizzieren Sie in 3D, bevor Sie eine "
+"neue Arbeitsebene erstellen."
+
+#: mouse.cpp:1111
+msgid ""
+"Can't draw text in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+"Text kann nicht in 3D erstellt werden. Aktivieren Sie zuerst eine "
+"Arbeitsebene mit \"Skizze -> In Arbeitsebene\"."
+
+#: mouse.cpp:1128
+msgid "click to place bottom right of text"
+msgstr "Klicken Sie auf die untere rechte Ecke des Texts"
+
+#: mouse.cpp:1134
+msgid ""
+"Can't draw image in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+"Das Bild kann nicht in 3D erstellt werden. Aktivieren Sie zuerst eine "
+"Arbeitsebene mit \"Skizze -> In Arbeitsebene\"."
+
+#: mouse.cpp:1161
+msgid "NEW COMMENT -- DOUBLE-CLICK TO EDIT"
+msgstr "NEUER KOMMENTAR -- DOPPELKLICKEN ZUM BEARBEITEN"
+
+#: platform/gui.cpp:85 platform/gui.cpp:89
+msgctxt "file-type"
+msgid "SolveSpace models"
+msgstr "SolveSpace-Modelle"
+
+#: platform/gui.cpp:90
+msgctxt "file-type"
+msgid "IDF circuit board"
+msgstr ""
+
+#: platform/gui.cpp:94
+msgctxt "file-type"
+msgid "PNG image"
+msgstr "PNG-Datei"
+
+#: platform/gui.cpp:98
+msgctxt "file-type"
+msgid "STL mesh"
+msgstr "STL-Netz"
+
+#: platform/gui.cpp:99
+msgctxt "file-type"
+msgid "Wavefront OBJ mesh"
+msgstr "Wavefront OBJ-Netz"
+
+#: platform/gui.cpp:100
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, with viewer"
+msgstr "Three.js-kompatibles Netz, mit Ansicht"
+
+#: platform/gui.cpp:101
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, mesh only"
+msgstr "Three.js-kompatibles Netz, nur Netz"
+
+#: platform/gui.cpp:102
+msgctxt "file-type"
+msgid "Q3D Object file"
+msgstr "Q3D Objektdatei"
+
+#: platform/gui.cpp:103
+msgctxt "file-type"
+msgid "VRML text file"
+msgstr "VRML Textdatei"
+
+#: platform/gui.cpp:107 platform/gui.cpp:114 platform/gui.cpp:121
+msgctxt "file-type"
+msgid "STEP file"
+msgstr "STEP-Datei"
+
+#: platform/gui.cpp:111
+msgctxt "file-type"
+msgid "PDF file"
+msgstr "PDF-Datei"
+
+#: platform/gui.cpp:112
+msgctxt "file-type"
+msgid "Encapsulated PostScript"
+msgstr "Eingebettetes Postscript"
+
+#: platform/gui.cpp:113
+msgctxt "file-type"
+msgid "Scalable Vector Graphics"
+msgstr "Skalierbare Vektorgrafik"
+
+#: platform/gui.cpp:115 platform/gui.cpp:122
+msgctxt "file-type"
+msgid "DXF file (AutoCAD 2007)"
+msgstr "DXF-Datei (AutoCAD 2007)"
+
+#: platform/gui.cpp:116
+msgctxt "file-type"
+msgid "HPGL file"
+msgstr "HPGL-Datei"
+
+#: platform/gui.cpp:117
+msgctxt "file-type"
+msgid "G Code"
+msgstr "G-Code"
+
+#: platform/gui.cpp:126
+msgctxt "file-type"
+msgid "AutoCAD DXF and DWG files"
+msgstr "AutoCAD DXF- und DWG-Dateien"
+
+#: platform/gui.cpp:130
+msgctxt "file-type"
+msgid "Comma-separated values"
+msgstr "Werte durch Komma getrennt (CSV)"
+
+#: platform/guigtk.cpp:1317 platform/guimac.mm:1360 platform/guiwin.cpp:1608
+msgid "untitled"
+msgstr "unbenannt"
+
+#: platform/guigtk.cpp:1328 platform/guigtk.cpp:1361 platform/guimac.mm:1318
+#: platform/guiwin.cpp:1555
+msgctxt "title"
+msgid "Save File"
+msgstr "Datei speichern"
+
+#: platform/guigtk.cpp:1329 platform/guigtk.cpp:1362 platform/guimac.mm:1301
+#: platform/guiwin.cpp:1557
+msgctxt "title"
+msgid "Open File"
+msgstr "Datei öffnen"
+
+#: platform/guigtk.cpp:1332 platform/guigtk.cpp:1368
+msgctxt "button"
+msgid "_Cancel"
+msgstr "_Abbrechen"
+
+#: platform/guigtk.cpp:1333 platform/guigtk.cpp:1366
+msgctxt "button"
+msgid "_Save"
+msgstr "_Speichern"
+
+#: platform/guigtk.cpp:1334 platform/guigtk.cpp:1367
+msgctxt "button"
+msgid "_Open"
+msgstr ""
+
+#: style.cpp:166
+msgid ""
+"Can't assign style to an entity that's derived from another entity; try "
+"assigning a style to this entity's parent."
+msgstr ""
+"Ein Linientyp kann keinem Objekt zugeordnet werden, das von einem anderen "
+"Objekt abgeleitet wurde. Versuchen Sie, dem übergeordneten Objekt einen Typ "
+"zuzuordnen."
+
+#: style.cpp:665
+msgid "Style name cannot be empty"
+msgstr "Name des Linientyps kann nicht leer sein."
+
+#: textscreens.cpp:741
+msgid "Can't repeat fewer than 1 time."
+msgstr "Nicht weniger als 1 Wiederholung möglich."
+
+#: textscreens.cpp:745
+msgid "Can't repeat more than 999 times."
+msgstr "Nicht mehr als 999 Wiederholungen möglich."
+
+#: textscreens.cpp:770
+msgid "Group name cannot be empty"
+msgstr "Der Name der Gruppe darf nicht leer sein."
+
+#: textscreens.cpp:813
+msgid "Opacity must be between zero and one."
+msgstr "Durchsichtigkeit muss zwischen Null und Eins sein."
+
+#: textscreens.cpp:848
+msgid "Radius cannot be zero or negative."
+msgstr "Radius darf nicht null oder negativ sein."
+
+#: toolbar.cpp:18
+msgid "Sketch line segment"
+msgstr "Liniensegment erstellen"
+
+#: toolbar.cpp:20
+msgid "Sketch rectangle"
+msgstr "Rechteck erstellen"
+
+#: toolbar.cpp:22
+msgid "Sketch circle"
+msgstr "Kreis erstellen"
+
+#: toolbar.cpp:24
+msgid "Sketch arc of a circle"
+msgstr "Kreisbogen erstellen"
+
+#: toolbar.cpp:26
+msgid "Sketch curves from text in a TrueType font"
+msgstr "Kurven aus Text in einem TrueType-Font erzeugen"
+
+#: toolbar.cpp:28
+msgid "Sketch image from a file"
+msgstr "Skizze von einer Bilddatei erstellen"
+
+#: toolbar.cpp:30
+msgid "Create tangent arc at selected point"
+msgstr "Bogentangente im ausgewählten Punkt erstellen"
+
+#: toolbar.cpp:32
+msgid "Sketch cubic Bezier spline"
+msgstr "Kubischen Bezier-Spline erstellen"
+
+#: toolbar.cpp:34
+msgid "Sketch datum point"
+msgstr "Bezugspunkt erstellen"
+
+#: toolbar.cpp:36
+msgid "Toggle construction"
+msgstr "Konstruktionselement an/aus"
+
+#: toolbar.cpp:38
+msgid "Split lines / curves where they intersect"
+msgstr "Linien / Kurven im Schnittpunkt trennen"
+
+#: toolbar.cpp:42
+msgid "Constrain distance / diameter / length"
+msgstr "Abstand / Durchmesser / Länge einschränken"
+
+#: toolbar.cpp:44
+msgid "Constrain angle"
+msgstr "Winkel einschränken"
+
+#: toolbar.cpp:46
+msgid "Constrain to be horizontal"
+msgstr "Horizontale Einschränkung"
+
+#: toolbar.cpp:48
+msgid "Constrain to be vertical"
+msgstr "Vertikale Einschränkung"
+
+#: toolbar.cpp:50
+msgid "Constrain to be parallel or tangent"
+msgstr "Einschränkung auf Parallele oder Tangente"
+
+#: toolbar.cpp:52
+msgid "Constrain to be perpendicular"
+msgstr "Rechtwinklige Einschränkung"
+
+#: toolbar.cpp:54
+msgid "Constrain point on line / curve / plane / point"
+msgstr "Punkt auf Linie / Kurve / Ebene / Punkt einschränken"
+
+#: toolbar.cpp:56
+msgid "Constrain symmetric"
+msgstr "Symmetrische Einschränkung"
+
+#: toolbar.cpp:58
+msgid "Constrain equal length / radius / angle"
+msgstr "Gleiche Länge / Radius / Winkel einschränken"
+
+#: toolbar.cpp:60
+msgid "Constrain normals in same orientation"
+msgstr "Normale auf gleiche Richtung einschränken"
+
+#: toolbar.cpp:62
+msgid "Other supplementary angle"
+msgstr "Anderer Komplementärwinkel"
+
+#: toolbar.cpp:64
+msgid "Toggle reference dimension"
+msgstr "Von/zu Referenzangabe wechseln"
+
+#: toolbar.cpp:68
+msgid "New group extruding active sketch"
+msgstr "Neue Gruppe mit extrudierter aktiver Skizze"
+
+#: toolbar.cpp:70
+msgid "New group rotating active sketch"
+msgstr "Neue Gruppe mit rotierter aktiver Skizze"
+
+#: toolbar.cpp:72
+msgid "New group step and repeat rotating"
+msgstr "Neue Gruppe mit kopierter gedrehter Skizze"
+
+#: toolbar.cpp:74
+msgid "New group step and repeat translating"
+msgstr "Neue Gruppe mit kopierter versetzter Skizze"
+
+#: toolbar.cpp:76
+msgid "New group in new workplane (thru given entities)"
+msgstr ""
+"Neue Gruppe in neuer Arbeitsebene (definiert durch ausgewählte Objekte)"
+
+#: toolbar.cpp:78
+msgid "New group in 3d"
+msgstr "Neue Gruppe in 3D"
+
+#: toolbar.cpp:80
+msgid "New group linking / assembling file"
+msgstr "Neue Gruppe mit verknüpfter Datei"
+
+#: toolbar.cpp:84
+msgid "Nearest isometric view"
+msgstr "Nächste isometrische Ansicht"
+
+#: toolbar.cpp:86
+msgid "Align view to active workplane"
+msgstr "Ansicht auf Arbeitsebene ausrichten"
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Error"
+msgstr "Fehler"
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Message"
+msgstr "Mitteilung"
+
+#: util.cpp:170
+msgctxt "button"
+msgid "&OK"
+msgstr "&OK"
+
+#: view.cpp:78
+msgid "Scale cannot be zero or negative."
+msgstr "Der Maßstab kann nicht Null oder negativ sein."
+
+#: view.cpp:90 view.cpp:99
+msgid "Bad format: specify x, y, z"
+msgstr "Ungültiges Format: geben Sie x, y, z ein"
+
+# solvespace.cpp:557
+#~ msgctxt "title"
+#~ msgid "(new sketch)"
+#~ msgstr "(Neue Skizze)"
+
+#~ msgctxt "title"
+#~ msgid "Property Browser"
+#~ msgstr "Attribut-Browser"
+
+#~ msgid "Specify between 0 and 8 digits after the decimal."
+#~ msgstr "Geben Sie 0 bis 8 Ziffern nach dem Dezimalzeichen an."
+
+#~ msgid "click to place bottom left of text"
+#~ msgstr "Klicken Sie auf die untere linke Ecke des Texts"
+
+#~ msgid "Do you want to save the changes you made to the new sketch?"
+#~ msgstr "Möchten Sie die Änderungen in Ihrer Skizze speichern?"
+
+#~ msgid "Your changes will be lost if you don't save them."
+#~ msgstr ""
+#~ "Ihre Änderungen werden verworfen, wenn sie nicht abgespeichert werden."
+
+#~ msgctxt "button"
+#~ msgid "Save"
+#~ msgstr "Speichern"
+
+#~ msgctxt "button"
+#~ msgid "Cancel"
+#~ msgstr "Abbrechen"
+
+#~ msgctxt "button"
+#~ msgid "Don't Save"
+#~ msgstr "Nicht speichern"
+
+#~ msgid "An autosave file is available for this project."
+#~ msgstr ""
+#~ "Eine automatisch gespeicherte Datei ist für dieses Projekt vorhanden."
+
+#~ msgid "Do you want to load the autosave file instead?"
+#~ msgstr "Möchten Sie die automatische Speicherdatei stattdessen öffnen?"
+
+#~ msgctxt "button"
+#~ msgid "Load"
+#~ msgstr "Öffnen"
+
+#~ msgctxt "button"
+#~ msgid "Don't Load"
+#~ msgstr "Nicht öffnen"
+
+#~ msgid ""
+#~ "Do you want to locate it manually?\n"
+#~ "If you select “No”, any geometry that depends on the missing file will be "
+#~ "removed."
+#~ msgstr ""
+#~ "Möchten Sie sie selber auswählen?\n"
+#~ "Falls Sie \"Nein\" wählen, wird jegliche mit der fehlenden Datei "
+#~ "verknüpfte Geometrie verworfen."
+
+#~ msgctxt "button"
+#~ msgid "Yes"
+#~ msgstr "Ja"
+
+#~ msgctxt "button"
+#~ msgid "No"
+#~ msgstr "Nein"
+
+#~ msgctxt "button"
+#~ msgid "OK"
+#~ msgstr "OK"
+
+#~ msgid "_Cancel"
+#~ msgstr "_Abbrechen"
+
+#~ msgid "_Open"
+#~ msgstr "_Öffnen"
+
+#~ msgid ""
+#~ "The file has changed since it was last saved.\n"
+#~ "\n"
+#~ "Do you want to save the changes?"
+#~ msgstr ""
+#~ "Die Datei wurde seit der letzten Speicherung geändert.\n"
+#~ "\n"
+#~ "Möchten Sie die Änderungen speichern?"
+
+#~ msgctxt "title"
+#~ msgid "Modified File"
+#~ msgstr "Geänderte Datei"
+
+#~ msgctxt "button"
+#~ msgid "Do_n't Save"
+#~ msgstr "Nicht speichern"
+
+#~ msgid ""
+#~ "An autosave file is available for this project.\n"
+#~ "\n"
+#~ "Do you want to load the autosave file instead?"
+#~ msgstr ""
+#~ "Eine automatisch gespeicherte Datei ist für dieses Projekt vorhanden.\n"
+#~ "\n"
+#~ "Wollen Sie die automatisch gespeicherte Datei öffnen?"
+
+#~ msgctxt "title"
+#~ msgid "Autosave Available"
+#~ msgstr "Automatische Speicherdatei verfügbar"
+
+#~ msgctxt "button"
+#~ msgid "_Load autosave"
+#~ msgstr "AutoDatei öffnen"
+
+#~ msgctxt "button"
+#~ msgid "Do_n't Load"
+#~ msgstr "Nicht öffnen"
+
+#~ msgctxt "button"
+#~ msgid "_Yes"
+#~ msgstr "_Ja"
+
+#~ msgctxt "button"
+#~ msgid "_No"
+#~ msgstr "_Nein"
--- /dev/null
+# English translations for SolveSpace package.
+# Copyright (C) 2017 the SolveSpace authors
+# This file is distributed under the same license as the SolveSpace package.
+# Automatically generated, 2017.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: SolveSpace 3.0\n"
+"Report-Msgid-Bugs-To: whitequark@whitequark.org\n"
+"POT-Creation-Date: 2020-11-17 20:50-0500\n"
+"PO-Revision-Date: 2017-01-05 10:30+0000\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"Language: en_US\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: clipboard.cpp:274
+msgid ""
+"Cut, paste, and copy work only in a workplane.\n"
+"\n"
+"Activate one with Sketch -> In Workplane."
+msgstr ""
+"Cut, paste, and copy work only in a workplane.\n"
+"\n"
+"Activate one with Sketch -> In Workplane."
+
+#: clipboard.cpp:291
+msgid "Clipboard is empty; nothing to paste."
+msgstr "Clipboard is empty; nothing to paste."
+
+#: clipboard.cpp:338
+msgid "Number of copies to paste must be at least one."
+msgstr "Number of copies to paste must be at least one."
+
+#: clipboard.cpp:354 textscreens.cpp:783
+msgid "Scale cannot be zero."
+msgstr "Scale cannot be zero."
+
+#: clipboard.cpp:396
+msgid "Select one point to define origin of rotation."
+msgstr "Select one point to define origin of rotation."
+
+#: clipboard.cpp:408
+msgid "Select two points to define translation vector."
+msgstr "Select two points to define translation vector."
+
+#: clipboard.cpp:418
+msgid ""
+"Transformation is identity. So all copies will be exactly on top of each "
+"other."
+msgstr ""
+"Transformation is identity. So all copies will be exactly on top of each "
+"other."
+
+#: clipboard.cpp:422
+msgid "Too many items to paste; split this into smaller pastes."
+msgstr "Too many items to paste; split this into smaller pastes."
+
+#: clipboard.cpp:427
+msgid "No workplane active."
+msgstr "No workplane active."
+
+#: confscreen.cpp:410
+msgid "Bad format: specify coordinates as x, y, z"
+msgstr "Bad format: specify coordinates as x, y, z"
+
+#: confscreen.cpp:420 style.cpp:659 textscreens.cpp:805
+msgid "Bad format: specify color as r, g, b"
+msgstr "Bad format: specify color as r, g, b"
+
+#: confscreen.cpp:446
+msgid ""
+"The perspective factor will have no effect until you enable View -> Use "
+"Perspective Projection."
+msgstr ""
+"The perspective factor will have no effect until you enable View -> Use "
+"Perspective Projection."
+
+#: confscreen.cpp:459 confscreen.cpp:469
+#, c-format
+msgid "Specify between 0 and %d digits after the decimal."
+msgstr "Specify between 0 and %d digits after the decimal."
+
+#: confscreen.cpp:481
+msgid "Export scale must not be zero!"
+msgstr "Export scale must not be zero!"
+
+#: confscreen.cpp:493
+msgid "Cutter radius offset must not be negative!"
+msgstr "Cutter radius offset must not be negative!"
+
+#: confscreen.cpp:547
+msgid "Bad value: autosave interval should be positive"
+msgstr "Bad value: autosave interval should be positive"
+
+#: confscreen.cpp:550
+msgid "Bad format: specify interval in integral minutes"
+msgstr "Bad format: specify interval in integral minutes"
+
+#: constraint.cpp:12
+msgctxt "constr-name"
+msgid "pts-coincident"
+msgstr "pts-coincident"
+
+#: constraint.cpp:13
+msgctxt "constr-name"
+msgid "pt-pt-distance"
+msgstr "pt-pt-distance"
+
+#: constraint.cpp:14
+msgctxt "constr-name"
+msgid "pt-line-distance"
+msgstr "pt-line-distance"
+
+#: constraint.cpp:15
+msgctxt "constr-name"
+msgid "pt-plane-distance"
+msgstr "pt-plane-distance"
+
+#: constraint.cpp:16
+msgctxt "constr-name"
+msgid "pt-face-distance"
+msgstr "pt-face-distance"
+
+#: constraint.cpp:17
+msgctxt "constr-name"
+msgid "proj-pt-pt-distance"
+msgstr "proj-pt-pt-distance"
+
+#: constraint.cpp:18
+msgctxt "constr-name"
+msgid "pt-in-plane"
+msgstr "pt-in-plane"
+
+#: constraint.cpp:19
+msgctxt "constr-name"
+msgid "pt-on-line"
+msgstr "pt-on-line"
+
+#: constraint.cpp:20
+msgctxt "constr-name"
+msgid "pt-on-face"
+msgstr "pt-on-face"
+
+#: constraint.cpp:21
+msgctxt "constr-name"
+msgid "eq-length"
+msgstr "eq-length"
+
+#: constraint.cpp:22
+msgctxt "constr-name"
+msgid "eq-length-and-pt-ln-dist"
+msgstr "eq-length-and-pt-ln-dist"
+
+#: constraint.cpp:23
+msgctxt "constr-name"
+msgid "eq-pt-line-distances"
+msgstr "eq-pt-line-distances"
+
+#: constraint.cpp:24
+msgctxt "constr-name"
+msgid "length-ratio"
+msgstr "length-ratio"
+
+#: constraint.cpp:25
+msgctxt "constr-name"
+msgid "length-difference"
+msgstr "length-difference"
+
+#: constraint.cpp:26
+msgctxt "constr-name"
+msgid "symmetric"
+msgstr "symmetric"
+
+#: constraint.cpp:27
+msgctxt "constr-name"
+msgid "symmetric-h"
+msgstr "symmetric-h"
+
+#: constraint.cpp:28
+msgctxt "constr-name"
+msgid "symmetric-v"
+msgstr "symmetric-v"
+
+#: constraint.cpp:29
+msgctxt "constr-name"
+msgid "symmetric-line"
+msgstr "symmetric-line"
+
+#: constraint.cpp:30
+msgctxt "constr-name"
+msgid "at-midpoint"
+msgstr "at-midpoint"
+
+#: constraint.cpp:31
+msgctxt "constr-name"
+msgid "horizontal"
+msgstr "horizontal"
+
+#: constraint.cpp:32
+msgctxt "constr-name"
+msgid "vertical"
+msgstr "vertical"
+
+#: constraint.cpp:33
+msgctxt "constr-name"
+msgid "diameter"
+msgstr "diameter"
+
+#: constraint.cpp:34
+msgctxt "constr-name"
+msgid "pt-on-circle"
+msgstr "pt-on-circle"
+
+#: constraint.cpp:35
+msgctxt "constr-name"
+msgid "same-orientation"
+msgstr "same-orientation"
+
+#: constraint.cpp:36
+msgctxt "constr-name"
+msgid "angle"
+msgstr "angle"
+
+#: constraint.cpp:37
+msgctxt "constr-name"
+msgid "parallel"
+msgstr "parallel"
+
+#: constraint.cpp:38
+msgctxt "constr-name"
+msgid "arc-line-tangent"
+msgstr "arc-line-tangent"
+
+#: constraint.cpp:39
+msgctxt "constr-name"
+msgid "cubic-line-tangent"
+msgstr "cubic-line-tangent"
+
+#: constraint.cpp:40
+msgctxt "constr-name"
+msgid "curve-curve-tangent"
+msgstr "curve-curve-tangent"
+
+#: constraint.cpp:41
+msgctxt "constr-name"
+msgid "perpendicular"
+msgstr "perpendicular"
+
+#: constraint.cpp:42
+msgctxt "constr-name"
+msgid "eq-radius"
+msgstr "eq-radius"
+
+#: constraint.cpp:43
+msgctxt "constr-name"
+msgid "eq-angle"
+msgstr "eq-angle"
+
+#: constraint.cpp:44
+msgctxt "constr-name"
+msgid "eq-line-len-arc-len"
+msgstr "eq-line-len-arc-len"
+
+#: constraint.cpp:45
+msgctxt "constr-name"
+msgid "lock-where-dragged"
+msgstr "lock-where-dragged"
+
+#: constraint.cpp:46
+msgctxt "constr-name"
+msgid "comment"
+msgstr "comment"
+
+#: constraint.cpp:171
+msgid ""
+"Bad selection for distance / diameter constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two points (distance between points)\n"
+" * a line segment (length)\n"
+" * two points and a line segment or normal (projected distance)\n"
+" * a workplane and a point (minimum distance)\n"
+" * a line segment and a point (minimum distance)\n"
+" * a plane face and a point (minimum distance)\n"
+" * a circle or an arc (diameter)\n"
+msgstr ""
+"Bad selection for distance / diameter constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two points (distance between points)\n"
+" * a line segment (length)\n"
+" * two points and a line segment or normal (projected distance)\n"
+" * a workplane and a point (minimum distance)\n"
+" * a line segment and a point (minimum distance)\n"
+" * a plane face and a point (minimum distance)\n"
+" * a circle or an arc (diameter)\n"
+
+#: constraint.cpp:224
+msgid ""
+"Bad selection for on point / curve / plane constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points (points coincident)\n"
+" * a point and a workplane (point in plane)\n"
+" * a point and a line segment (point on line)\n"
+" * a point and a circle or arc (point on curve)\n"
+" * a point and a plane face (point on face)\n"
+msgstr ""
+"Bad selection for on point / curve / plane constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points (points coincident)\n"
+" * a point and a workplane (point in plane)\n"
+" * a point and a line segment (point on line)\n"
+" * a point and a circle or arc (point on curve)\n"
+" * a point and a plane face (point on face)\n"
+
+#: constraint.cpp:286
+msgid ""
+"Bad selection for equal length / radius constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two line segments (equal length)\n"
+" * two line segments and two points (equal point-line distances)\n"
+" * a line segment and two points (equal point-line distances)\n"
+" * a line segment, and a point and line segment (point-line distance "
+"equals length)\n"
+" * four line segments or normals (equal angle between A,B and C,D)\n"
+" * three line segments or normals (equal angle between A,B and B,C)\n"
+" * two circles or arcs (equal radius)\n"
+" * a line segment and an arc (line segment length equals arc length)\n"
+msgstr ""
+"Bad selection for equal length / radius constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two line segments (equal length)\n"
+" * two line segments and two points (equal point-line distances)\n"
+" * a line segment and two points (equal point-line distances)\n"
+" * a line segment, and a point and line segment (point-line distance "
+"equals length)\n"
+" * four line segments or normals (equal angle between A,B and C,D)\n"
+" * three line segments or normals (equal angle between A,B and B,C)\n"
+" * two circles or arcs (equal radius)\n"
+" * a line segment and an arc (line segment length equals arc length)\n"
+
+#: constraint.cpp:325
+msgid ""
+"Bad selection for length ratio constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+"Bad selection for length ratio constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+
+#: constraint.cpp:342
+msgid ""
+"Bad selection for length difference constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+"Bad selection for length difference constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments\n"
+
+#: constraint.cpp:368
+msgid ""
+"Bad selection for at midpoint constraint. This constraint can apply to:\n"
+"\n"
+" * a line segment and a point (point at midpoint)\n"
+" * a line segment and a workplane (line's midpoint on plane)\n"
+msgstr ""
+"Bad selection for at midpoint constraint. This constraint can apply to:\n"
+"\n"
+" * a line segment and a point (point at midpoint)\n"
+" * a line segment and a workplane (line's midpoint on plane)\n"
+
+#: constraint.cpp:426
+msgid ""
+"Bad selection for symmetric constraint. This constraint can apply to:\n"
+"\n"
+" * two points or a line segment (symmetric about workplane's coordinate "
+"axis)\n"
+" * line segment, and two points or a line segment (symmetric about line "
+"segment)\n"
+" * workplane, and two points or a line segment (symmetric about "
+"workplane)\n"
+msgstr ""
+"Bad selection for symmetric constraint. This constraint can apply to:\n"
+"\n"
+" * two points or a line segment (symmetric about workplane's coordinate "
+"axis)\n"
+" * line segment, and two points or a line segment (symmetric about line "
+"segment)\n"
+" * workplane, and two points or a line segment (symmetric about "
+"workplane)\n"
+
+#: constraint.cpp:440
+msgid ""
+"A workplane must be active when constraining symmetric without an explicit "
+"symmetry plane."
+msgstr ""
+"A workplane must be active when constraining symmetric without an explicit "
+"symmetry plane."
+
+#: constraint.cpp:470
+msgid ""
+"Activate a workplane (with Sketch -> In Workplane) before applying a "
+"horizontal or vertical constraint."
+msgstr ""
+"Activate a workplane (with Sketch -> In Workplane) before applying a "
+"horizontal or vertical constraint."
+
+#: constraint.cpp:483
+msgid ""
+"Bad selection for horizontal / vertical constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points\n"
+" * a line segment\n"
+msgstr ""
+"Bad selection for horizontal / vertical constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points\n"
+" * a line segment\n"
+
+#: constraint.cpp:504
+msgid ""
+"Bad selection for same orientation constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two normals\n"
+msgstr ""
+"Bad selection for same orientation constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two normals\n"
+
+#: constraint.cpp:554
+msgid "Must select an angle constraint."
+msgstr "Must select an angle constraint."
+
+#: constraint.cpp:567
+msgid "Must select a constraint with associated label."
+msgstr "Must select a constraint with associated label."
+
+#: constraint.cpp:578
+msgid ""
+"Bad selection for angle constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+"Bad selection for angle constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+
+#: constraint.cpp:635
+msgid ""
+"The tangent arc and line segment must share an endpoint. Constrain them with "
+"Constrain -> On Point before constraining tangent."
+msgstr ""
+"The tangent arc and line segment must share an endpoint. Constrain them with "
+"Constrain -> On Point before constraining tangent."
+
+#: constraint.cpp:659
+msgid ""
+"The tangent cubic and line segment must share an endpoint. Constrain them "
+"with Constrain -> On Point before constraining tangent."
+msgstr ""
+"The tangent cubic and line segment must share an endpoint. Constrain them "
+"with Constrain -> On Point before constraining tangent."
+
+#: constraint.cpp:669
+msgid "Curve-curve tangency must apply in workplane."
+msgstr "Curve-curve tangency must apply in workplane."
+
+#: constraint.cpp:687
+msgid ""
+"The curves must share an endpoint. Constrain them with Constrain -> On Point "
+"before constraining tangent."
+msgstr ""
+"The curves must share an endpoint. Constrain them with Constrain -> On Point "
+"before constraining tangent."
+
+#: constraint.cpp:696
+msgid ""
+"Bad selection for parallel / tangent constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments (parallel)\n"
+" * a line segment and a normal (parallel)\n"
+" * two normals (parallel)\n"
+" * two line segments, arcs, or beziers, that share an endpoint (tangent)\n"
+msgstr ""
+"Bad selection for parallel / tangent constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments (parallel)\n"
+" * a line segment and a normal (parallel)\n"
+" * two normals (parallel)\n"
+" * two line segments, arcs, or beziers, that share an endpoint (tangent)\n"
+
+#: constraint.cpp:714
+msgid ""
+"Bad selection for perpendicular constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+"Bad selection for perpendicular constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+
+#: constraint.cpp:729
+msgid ""
+"Bad selection for lock point where dragged constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * a point\n"
+msgstr ""
+"Bad selection for lock point where dragged constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * a point\n"
+
+#: constraint.cpp:740
+msgid "click center of comment text"
+msgstr "click center of comment text"
+
+#: export.cpp:19
+msgid ""
+"No solid model present; draw one with extrudes and revolves, or use Export "
+"2d View to export bare lines and curves."
+msgstr ""
+"No solid model present; draw one with extrudes and revolves, or use Export "
+"2d View to export bare lines and curves."
+
+#: export.cpp:61
+msgid ""
+"Bad selection for export section. Please select:\n"
+"\n"
+" * nothing, with an active workplane (workplane is section plane)\n"
+" * a face (section plane through face)\n"
+" * a point and two line segments (plane through point and parallel to "
+"lines)\n"
+msgstr ""
+"Bad selection for export section. Please select:\n"
+"\n"
+" * nothing, with an active workplane (workplane is section plane)\n"
+" * a face (section plane through face)\n"
+" * a point and two line segments (plane through point and parallel to "
+"lines)\n"
+
+#: export.cpp:822
+msgid "Active group mesh is empty; nothing to export."
+msgstr "Active group mesh is empty; nothing to export."
+
+#: exportvector.cpp:337
+msgid "freehand lines were replaced with continuous lines"
+msgstr "freehand lines were replaced with continuous lines"
+
+#: exportvector.cpp:339
+msgid "zigzag lines were replaced with continuous lines"
+msgstr "zigzag lines were replaced with continuous lines"
+
+#: exportvector.cpp:593
+msgid ""
+"Some aspects of the drawing have no DXF equivalent and were not exported:\n"
+msgstr ""
+"Some aspects of the drawing have no DXF equivalent and were not exported:\n"
+
+#: exportvector.cpp:839
+msgid ""
+"PDF page size exceeds 200 by 200 inches; many viewers may reject this file."
+msgstr ""
+"PDF page size exceeds 200 by 200 inches; many viewers may reject this file."
+
+#: file.cpp:44 group.cpp:91
+msgctxt "group-name"
+msgid "sketch-in-plane"
+msgstr "sketch-in-plane"
+
+#: file.cpp:62
+msgctxt "group-name"
+msgid "#references"
+msgstr "#references"
+
+#: file.cpp:549
+msgid ""
+"Unrecognized data in file. This file may be corrupt, or from a newer version "
+"of the program."
+msgstr ""
+"Unrecognized data in file. This file may be corrupt, or from a newer version "
+"of the program."
+
+#: file.cpp:859
+msgctxt "title"
+msgid "Missing File"
+msgstr "Missing File"
+
+#: file.cpp:860
+#, c-format
+msgctxt "dialog"
+msgid "The linked file “%s” is not present."
+msgstr "The linked file “%s” is not present."
+
+#: file.cpp:862
+msgctxt "dialog"
+msgid ""
+"Do you want to locate it manually?\n"
+"\n"
+"If you decline, any geometry that depends on the missing file will be "
+"permanently removed."
+msgstr ""
+"Do you want to locate it manually?\n"
+"\n"
+"If you decline, any geometry that depends on the missing file will be "
+"permanently removed."
+
+#: file.cpp:865
+msgctxt "button"
+msgid "&Yes"
+msgstr "&Yes"
+
+#: file.cpp:867
+msgctxt "button"
+msgid "&No"
+msgstr "&No"
+
+#: file.cpp:869
+msgctxt "button"
+msgid "&Cancel"
+msgstr "&Cancel"
+
+#: graphicswin.cpp:41
+msgid "&File"
+msgstr "&File"
+
+#: graphicswin.cpp:42
+msgid "&New"
+msgstr "&New"
+
+#: graphicswin.cpp:43
+msgid "&Open..."
+msgstr "&Open..."
+
+#: graphicswin.cpp:44
+msgid "Open &Recent"
+msgstr "Open &Recent"
+
+#: graphicswin.cpp:45
+msgid "&Save"
+msgstr "&Save"
+
+#: graphicswin.cpp:46
+msgid "Save &As..."
+msgstr "Save &As..."
+
+#: graphicswin.cpp:48
+msgid "Export &Image..."
+msgstr "Export &Image..."
+
+#: graphicswin.cpp:49
+msgid "Export 2d &View..."
+msgstr "Export 2d &View..."
+
+#: graphicswin.cpp:50
+msgid "Export 2d &Section..."
+msgstr "Export 2d &Section..."
+
+#: graphicswin.cpp:51
+msgid "Export 3d &Wireframe..."
+msgstr "Export 3d &Wireframe..."
+
+#: graphicswin.cpp:52
+msgid "Export Triangle &Mesh..."
+msgstr "Export Triangle &Mesh..."
+
+#: graphicswin.cpp:53
+msgid "Export &Surfaces..."
+msgstr "Export &Surfaces..."
+
+#: graphicswin.cpp:54
+msgid "Im&port..."
+msgstr "Im&port..."
+
+#: graphicswin.cpp:57
+msgid "E&xit"
+msgstr "E&xit"
+
+#: graphicswin.cpp:60
+msgid "&Edit"
+msgstr "&Edit"
+
+#: graphicswin.cpp:61
+msgid "&Undo"
+msgstr "&Undo"
+
+#: graphicswin.cpp:62
+msgid "&Redo"
+msgstr "&Redo"
+
+#: graphicswin.cpp:63
+msgid "Re&generate All"
+msgstr "Re&generate All"
+
+#: graphicswin.cpp:65
+msgid "Snap Selection to &Grid"
+msgstr "Snap Selection to &Grid"
+
+#: graphicswin.cpp:66
+msgid "Rotate Imported &90°"
+msgstr "Rotate Imported &90°"
+
+#: graphicswin.cpp:68
+msgid "Cu&t"
+msgstr "Cu&t"
+
+#: graphicswin.cpp:69
+msgid "&Copy"
+msgstr "&Copy"
+
+#: graphicswin.cpp:70
+msgid "&Paste"
+msgstr "&Paste"
+
+#: graphicswin.cpp:71
+msgid "Paste &Transformed..."
+msgstr "Paste &Transformed..."
+
+#: graphicswin.cpp:72
+msgid "&Delete"
+msgstr "&Delete"
+
+#: graphicswin.cpp:74
+msgid "Select &Edge Chain"
+msgstr "Select &Edge Chain"
+
+#: graphicswin.cpp:75
+msgid "Select &All"
+msgstr "Select &All"
+
+#: graphicswin.cpp:76
+msgid "&Unselect All"
+msgstr "&Unselect All"
+
+#: graphicswin.cpp:78
+msgid "&Line Styles..."
+msgstr "&Line Styles..."
+
+#: graphicswin.cpp:79
+msgid "&View Projection..."
+msgstr "&View Projection..."
+
+#: graphicswin.cpp:81
+msgid "Con&figuration..."
+msgstr "Con&figuration..."
+
+#: graphicswin.cpp:84
+msgid "&View"
+msgstr "&View"
+
+#: graphicswin.cpp:85
+msgid "Zoom &In"
+msgstr "Zoom &In"
+
+#: graphicswin.cpp:86
+msgid "Zoom &Out"
+msgstr "Zoom &Out"
+
+#: graphicswin.cpp:87
+msgid "Zoom To &Fit"
+msgstr "Zoom To &Fit"
+
+#: graphicswin.cpp:89
+msgid "Align View to &Workplane"
+msgstr "Align View to &Workplane"
+
+#: graphicswin.cpp:90
+msgid "Nearest &Ortho View"
+msgstr "Nearest &Ortho View"
+
+#: graphicswin.cpp:91
+msgid "Nearest &Isometric View"
+msgstr "Nearest &Isometric View"
+
+#: graphicswin.cpp:92
+msgid "&Center View At Point"
+msgstr "&Center View At Point"
+
+#: graphicswin.cpp:94
+msgid "Show Snap &Grid"
+msgstr "Show Snap &Grid"
+
+#: graphicswin.cpp:95
+msgid "Use &Perspective Projection"
+msgstr "Use &Perspective Projection"
+
+#: graphicswin.cpp:96
+msgid "Dimension &Units"
+msgstr "Dimension &Units"
+
+#: graphicswin.cpp:97
+msgid "Dimensions in &Millimeters"
+msgstr "Dimensions in &Millimeters"
+
+#: graphicswin.cpp:98
+msgid "Dimensions in M&eters"
+msgstr "Dimensions in M&eters"
+
+#: graphicswin.cpp:99
+msgid "Dimensions in &Inches"
+msgstr "Dimensions in &Inches"
+
+#: graphicswin.cpp:101
+msgid "Show &Toolbar"
+msgstr "Show &Toolbar"
+
+#: graphicswin.cpp:102
+msgid "Show Property Bro&wser"
+msgstr "Show Property Bro&wser"
+
+#: graphicswin.cpp:104
+msgid "&Full Screen"
+msgstr "&Full Screen"
+
+#: graphicswin.cpp:106
+msgid "&New Group"
+msgstr "&New Group"
+
+#: graphicswin.cpp:107
+msgid "Sketch In &3d"
+msgstr "Sketch In &3d"
+
+#: graphicswin.cpp:108
+msgid "Sketch In New &Workplane"
+msgstr "Sketch In New &Workplane"
+
+#: graphicswin.cpp:110
+msgid "Step &Translating"
+msgstr "Step &Translating"
+
+#: graphicswin.cpp:111
+msgid "Step &Rotating"
+msgstr "Step &Rotating"
+
+#: graphicswin.cpp:113
+msgid "E&xtrude"
+msgstr "E&xtrude"
+
+#: graphicswin.cpp:114
+msgid "&Helix"
+msgstr "&Helix"
+
+#: graphicswin.cpp:115
+msgid "&Lathe"
+msgstr "&Lathe"
+
+#: graphicswin.cpp:116
+msgid "Re&volve"
+msgstr "Re&volve"
+
+#: graphicswin.cpp:118
+msgid "Link / Assemble..."
+msgstr "Link / Assemble..."
+
+#: graphicswin.cpp:119
+msgid "Link Recent"
+msgstr "Link Recent"
+
+#: graphicswin.cpp:121
+msgid "&Sketch"
+msgstr "&Sketch"
+
+#: graphicswin.cpp:122
+msgid "In &Workplane"
+msgstr "In &Workplane"
+
+#: graphicswin.cpp:123
+msgid "Anywhere In &3d"
+msgstr "Anywhere In &3d"
+
+#: graphicswin.cpp:125
+msgid "Datum &Point"
+msgstr "Datum &Point"
+
+#: graphicswin.cpp:126
+msgid "&Workplane"
+msgstr "&Workplane"
+
+#: graphicswin.cpp:128
+msgid "Line &Segment"
+msgstr "Line &Segment"
+
+#: graphicswin.cpp:129
+msgid "C&onstruction Line Segment"
+msgstr "C&onstruction Line Segment"
+
+#: graphicswin.cpp:130
+msgid "&Rectangle"
+msgstr "&Rectangle"
+
+#: graphicswin.cpp:131
+msgid "&Circle"
+msgstr "&Circle"
+
+#: graphicswin.cpp:132
+msgid "&Arc of a Circle"
+msgstr "&Arc of a Circle"
+
+#: graphicswin.cpp:133
+msgid "&Bezier Cubic Spline"
+msgstr "&Bezier Cubic Spline"
+
+#: graphicswin.cpp:135
+msgid "&Text in TrueType Font"
+msgstr "&Text in TrueType Font"
+
+#: graphicswin.cpp:136
+msgid "&Image"
+msgstr "&Image"
+
+#: graphicswin.cpp:138
+msgid "To&ggle Construction"
+msgstr "To&ggle Construction"
+
+#: graphicswin.cpp:139
+msgid "Tangent &Arc at Point"
+msgstr "Tangent &Arc at Point"
+
+#: graphicswin.cpp:140
+msgid "Split Curves at &Intersection"
+msgstr "Split Curves at &Intersection"
+
+#: graphicswin.cpp:142
+msgid "&Constrain"
+msgstr "&Constrain"
+
+#: graphicswin.cpp:143
+msgid "&Distance / Diameter"
+msgstr "&Distance / Diameter"
+
+#: graphicswin.cpp:144
+msgid "Re&ference Dimension"
+msgstr "Re&ference Dimension"
+
+#: graphicswin.cpp:145
+msgid "A&ngle"
+msgstr "A&ngle"
+
+#: graphicswin.cpp:146
+msgid "Reference An&gle"
+msgstr "Reference An&gle"
+
+#: graphicswin.cpp:147
+msgid "Other S&upplementary Angle"
+msgstr "Other S&upplementary Angle"
+
+#: graphicswin.cpp:148
+msgid "Toggle R&eference Dim"
+msgstr "Toggle R&eference Dim"
+
+#: graphicswin.cpp:150
+msgid "&Horizontal"
+msgstr "&Horizontal"
+
+#: graphicswin.cpp:151
+msgid "&Vertical"
+msgstr "&Vertical"
+
+#: graphicswin.cpp:153
+msgid "&On Point / Curve / Plane"
+msgstr "&On Point / Curve / Plane"
+
+#: graphicswin.cpp:154
+msgid "E&qual Length / Radius / Angle"
+msgstr "E&qual Length / Radius / Angle"
+
+#: graphicswin.cpp:155
+msgid "Length Ra&tio"
+msgstr "Length Ra&tio"
+
+#: graphicswin.cpp:156
+msgid "Length Diff&erence"
+msgstr "Length Diff&erence"
+
+#: graphicswin.cpp:157
+msgid "At &Midpoint"
+msgstr "At &Midpoint"
+
+#: graphicswin.cpp:158
+msgid "S&ymmetric"
+msgstr "S&ymmetric"
+
+#: graphicswin.cpp:159
+msgid "Para&llel / Tangent"
+msgstr "Para&llel / Tangent"
+
+#: graphicswin.cpp:160
+msgid "&Perpendicular"
+msgstr "&Perpendicular"
+
+#: graphicswin.cpp:161
+msgid "Same Orient&ation"
+msgstr "Same Orient&ation"
+
+#: graphicswin.cpp:162
+msgid "Lock Point Where &Dragged"
+msgstr "Lock Point Where &Dragged"
+
+#: graphicswin.cpp:164
+msgid "Comment"
+msgstr "Comment"
+
+#: graphicswin.cpp:166
+msgid "&Analyze"
+msgstr "&Analyze"
+
+#: graphicswin.cpp:167
+msgid "Measure &Volume"
+msgstr "Measure &Volume"
+
+#: graphicswin.cpp:168
+msgid "Measure A&rea"
+msgstr "Measure A&rea"
+
+#: graphicswin.cpp:169
+msgid "Measure &Perimeter"
+msgstr "Measure &Perimeter"
+
+#: graphicswin.cpp:170
+msgid "Show &Interfering Parts"
+msgstr "Show &Interfering Parts"
+
+#: graphicswin.cpp:171
+msgid "Show &Naked Edges"
+msgstr "Show &Naked Edges"
+
+#: graphicswin.cpp:172
+msgid "Show &Center of Mass"
+msgstr "Show &Center of Mass"
+
+#: graphicswin.cpp:174
+msgid "Show &Underconstrained Points"
+msgstr "Show &Underconstrained Points"
+
+#: graphicswin.cpp:176
+msgid "&Trace Point"
+msgstr "&Trace Point"
+
+#: graphicswin.cpp:177
+msgid "&Stop Tracing..."
+msgstr "&Stop Tracing..."
+
+#: graphicswin.cpp:178
+msgid "Step &Dimension..."
+msgstr "Step &Dimension..."
+
+#: graphicswin.cpp:180
+msgid "&Help"
+msgstr "&Help"
+
+#: graphicswin.cpp:181
+msgid "&Language"
+msgstr "&Language"
+
+#: graphicswin.cpp:182
+msgid "&Website / Manual"
+msgstr "&Website / Manual"
+
+#: graphicswin.cpp:184
+msgid "&About"
+msgstr "&About"
+
+#: graphicswin.cpp:352
+msgid "(no recent files)"
+msgstr "(no recent files)"
+
+#: graphicswin.cpp:360
+#, c-format
+msgid "File '%s' does not exist."
+msgstr "File '%s' does not exist."
+
+#: graphicswin.cpp:721
+msgid "No workplane is active, so the grid will not appear."
+msgstr "No workplane is active, so the grid will not appear."
+
+#: graphicswin.cpp:730
+msgid ""
+"The perspective factor is set to zero, so the view will always be a parallel "
+"projection.\n"
+"\n"
+"For a perspective projection, modify the perspective factor in the "
+"configuration screen. A value around 0.3 is typical."
+msgstr ""
+"The perspective factor is set to zero, so the view will always be a parallel "
+"projection.\n"
+"\n"
+"For a perspective projection, modify the perspective factor in the "
+"configuration screen. A value around 0.3 is typical."
+
+#: graphicswin.cpp:809
+msgid ""
+"Select a point; this point will become the center of the view on screen."
+msgstr ""
+"Select a point; this point will become the center of the view on screen."
+
+#: graphicswin.cpp:1103
+msgid "No additional entities share endpoints with the selected entities."
+msgstr "No additional entities share endpoints with the selected entities."
+
+#: graphicswin.cpp:1121
+msgid ""
+"To use this command, select a point or other entity from an linked part, or "
+"make a link group the active group."
+msgstr ""
+"To use this command, select a point or other entity from an linked part, or "
+"make a link group the active group."
+
+#: graphicswin.cpp:1144
+msgid ""
+"No workplane is active. Activate a workplane (with Sketch -> In Workplane) "
+"to define the plane for the snap grid."
+msgstr ""
+"No workplane is active. Activate a workplane (with Sketch -> In Workplane) "
+"to define the plane for the snap grid."
+
+#: graphicswin.cpp:1151
+msgid ""
+"Can't snap these items to grid; select points, text comments, or constraints "
+"with a label. To snap a line, select its endpoints."
+msgstr ""
+"Can't snap these items to grid; select points, text comments, or constraints "
+"with a label. To snap a line, select its endpoints."
+
+#: graphicswin.cpp:1239
+msgid "No workplane selected. Activating default workplane for this group."
+msgstr "No workplane selected. Activating default workplane for this group."
+
+#: graphicswin.cpp:1242
+msgid ""
+"No workplane is selected, and the active group does not have a default "
+"workplane. Try selecting a workplane, or activating a sketch-in-new-"
+"workplane group."
+msgstr ""
+"No workplane is selected, and the active group does not have a default "
+"workplane. Try selecting a workplane, or activating a sketch-in-new-"
+"workplane group."
+
+#: graphicswin.cpp:1263
+msgid ""
+"Bad selection for tangent arc at point. Select a single point, or select "
+"nothing to set up arc parameters."
+msgstr ""
+"Bad selection for tangent arc at point. Select a single point, or select "
+"nothing to set up arc parameters."
+
+#: graphicswin.cpp:1274
+msgid "click point on arc (draws anti-clockwise)"
+msgstr "click point on arc (draws anti-clockwise)"
+
+#: graphicswin.cpp:1275
+msgid "click to place datum point"
+msgstr "click to place datum point"
+
+#: graphicswin.cpp:1276
+msgid "click first point of line segment"
+msgstr "click first point of line segment"
+
+#: graphicswin.cpp:1278
+msgid "click first point of construction line segment"
+msgstr "click first point of construction line segment"
+
+#: graphicswin.cpp:1279
+msgid "click first point of cubic segment"
+msgstr "click first point of cubic segment"
+
+#: graphicswin.cpp:1280
+msgid "click center of circle"
+msgstr "click center of circle"
+
+#: graphicswin.cpp:1281
+msgid "click origin of workplane"
+msgstr "click origin of workplane"
+
+#: graphicswin.cpp:1282
+msgid "click one corner of rectangle"
+msgstr "click one corner of rectangle"
+
+#: graphicswin.cpp:1283
+msgid "click top left of text"
+msgstr "click top left of text"
+
+#: graphicswin.cpp:1289
+msgid "click top left of image"
+msgstr "click top left of image"
+
+#: graphicswin.cpp:1301
+msgid ""
+"No entities are selected. Select entities before trying to toggle their "
+"construction state."
+msgstr ""
+"No entities are selected. Select entities before trying to toggle their "
+"construction state."
+
+#: group.cpp:86
+msgctxt "group-name"
+msgid "sketch-in-3d"
+msgstr "sketch-in-3d"
+
+#: group.cpp:142
+msgid ""
+"Bad selection for new sketch in workplane. This group can be created with:\n"
+"\n"
+" * a point (through the point, orthogonal to coordinate axes)\n"
+" * a point and two line segments (through the point, parallel to the "
+"lines)\n"
+" * a workplane (copy of the workplane)\n"
+msgstr ""
+"Bad selection for new sketch in workplane. This group can be created with:\n"
+"\n"
+" * a point (through the point, orthogonal to coordinate axes)\n"
+" * a point and two line segments (through the point, parallel to the "
+"lines)\n"
+" * a workplane (copy of the workplane)\n"
+
+#: group.cpp:154
+msgid ""
+"Activate a workplane (Sketch -> In Workplane) before extruding. The sketch "
+"will be extruded normal to the workplane."
+msgstr ""
+"Activate a workplane (Sketch -> In Workplane) before extruding. The sketch "
+"will be extruded normal to the workplane."
+
+#: group.cpp:163
+msgctxt "group-name"
+msgid "extrude"
+msgstr "extrude"
+
+#: group.cpp:168
+msgid "Lathe operation can only be applied to planar sketches."
+msgstr "Lathe operation can only be applied to planar sketches."
+
+#: group.cpp:179
+msgid ""
+"Bad selection for new lathe group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+"Bad selection for new lathe group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+
+#: group.cpp:189
+msgctxt "group-name"
+msgid "lathe"
+msgstr "lathe"
+
+#: group.cpp:194
+msgid "Revolve operation can only be applied to planar sketches."
+msgstr "Revolve operation can only be applied to planar sketches."
+
+#: group.cpp:205
+msgid ""
+"Bad selection for new revolve group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+"Bad selection for new revolve group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+
+#: group.cpp:217
+msgctxt "group-name"
+msgid "revolve"
+msgstr "revolve"
+
+#: group.cpp:222
+msgid "Helix operation can only be applied to planar sketches."
+msgstr "Helix operation can only be applied to planar sketches."
+
+#: group.cpp:233
+msgid ""
+"Bad selection for new helix group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+"Bad selection for new helix group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+
+#: group.cpp:245
+msgctxt "group-name"
+msgid "helix"
+msgstr "helix"
+
+#: group.cpp:258
+msgid ""
+"Bad selection for new rotation. This group can be created with:\n"
+"\n"
+" * a point, while locked in workplane (rotate in plane, about that "
+"point)\n"
+" * a point and a line or a normal (rotate about an axis through the "
+"point, and parallel to line / normal)\n"
+msgstr ""
+"Bad selection for new rotation. This group can be created with:\n"
+"\n"
+" * a point, while locked in workplane (rotate in plane, about that "
+"point)\n"
+" * a point and a line or a normal (rotate about an axis through the "
+"point, and parallel to line / normal)\n"
+
+#: group.cpp:271
+msgctxt "group-name"
+msgid "rotate"
+msgstr "rotate"
+
+#: group.cpp:282
+msgctxt "group-name"
+msgid "translate"
+msgstr "translate"
+
+#: group.cpp:400
+msgid "(unnamed)"
+msgstr "(unnamed)"
+
+#: groupmesh.cpp:708
+msgid "not closed contour, or not all same style!"
+msgstr "not closed contour, or not all same style!"
+
+#: groupmesh.cpp:721
+msgid "points not all coplanar!"
+msgstr "points not all coplanar!"
+
+#: groupmesh.cpp:723
+msgid "contour is self-intersecting!"
+msgstr "contour is self-intersecting!"
+
+#: groupmesh.cpp:725
+msgid "zero-length edge!"
+msgstr "zero-length edge!"
+
+#: modify.cpp:254
+msgid "Must be sketching in workplane to create tangent arc."
+msgstr "Must be sketching in workplane to create tangent arc."
+
+#: modify.cpp:301
+msgid ""
+"To create a tangent arc, select a point where two non-construction lines or "
+"circles in this group and workplane join."
+msgstr ""
+"To create a tangent arc, select a point where two non-construction lines or "
+"circles in this group and workplane join."
+
+#: modify.cpp:388
+msgid ""
+"Couldn't round this corner. Try a smaller radius, or try creating the "
+"desired geometry by hand with tangency constraints."
+msgstr ""
+"Couldn't round this corner. Try a smaller radius, or try creating the "
+"desired geometry by hand with tangency constraints."
+
+#: modify.cpp:597
+msgid "Couldn't split this entity; lines, circles, or cubics only."
+msgstr "Couldn't split this entity; lines, circles, or cubics only."
+
+#: modify.cpp:624
+msgid "Must be sketching in workplane to split."
+msgstr "Must be sketching in workplane to split."
+
+#: modify.cpp:631
+msgid ""
+"Select two entities that intersect each other (e.g. two lines/circles/arcs "
+"or a line/circle/arc and a point)."
+msgstr ""
+"Select two entities that intersect each other (e.g. two lines/circles/arcs "
+"or a line/circle/arc and a point)."
+
+#: modify.cpp:736
+msgid "Can't split; no intersection found."
+msgstr "Can't split; no intersection found."
+
+#: mouse.cpp:560
+msgid "Assign to Style"
+msgstr "Assign to Style"
+
+#: mouse.cpp:576
+msgid "No Style"
+msgstr "No Style"
+
+#: mouse.cpp:579
+msgid "Newly Created Custom Style..."
+msgstr "Newly Created Custom Style..."
+
+#: mouse.cpp:586
+msgid "Group Info"
+msgstr "Group Info"
+
+#: mouse.cpp:606
+msgid "Style Info"
+msgstr "Style Info"
+
+#: mouse.cpp:626
+msgid "Select Edge Chain"
+msgstr "Select Edge Chain"
+
+#: mouse.cpp:632
+msgid "Toggle Reference Dimension"
+msgstr "Toggle Reference Dimension"
+
+#: mouse.cpp:638
+msgid "Other Supplementary Angle"
+msgstr "Other Supplementary Angle"
+
+#: mouse.cpp:643
+msgid "Snap to Grid"
+msgstr "Snap to Grid"
+
+#: mouse.cpp:652
+msgid "Remove Spline Point"
+msgstr "Remove Spline Point"
+
+#: mouse.cpp:687
+msgid "Add Spline Point"
+msgstr "Add Spline Point"
+
+#: mouse.cpp:691
+msgid "Cannot add spline point: maximum number of points reached."
+msgstr "Cannot add spline point: maximum number of points reached."
+
+#: mouse.cpp:716
+msgid "Toggle Construction"
+msgstr "Toggle Construction"
+
+#: mouse.cpp:731
+msgid "Delete Point-Coincident Constraint"
+msgstr "Delete Point-Coincident Constraint"
+
+#: mouse.cpp:750
+msgid "Cut"
+msgstr "Cut"
+
+#: mouse.cpp:752
+msgid "Copy"
+msgstr "Copy"
+
+#: mouse.cpp:756
+msgid "Select All"
+msgstr "Select All"
+
+#: mouse.cpp:761
+msgid "Paste"
+msgstr "Paste"
+
+#: mouse.cpp:763
+msgid "Paste Transformed..."
+msgstr "Paste Transformed..."
+
+#: mouse.cpp:768
+msgid "Delete"
+msgstr "Delete"
+
+#: mouse.cpp:771
+msgid "Unselect All"
+msgstr "Unselect All"
+
+#: mouse.cpp:778
+msgid "Unselect Hovered"
+msgstr "Unselect Hovered"
+
+#: mouse.cpp:787
+msgid "Zoom to Fit"
+msgstr "Zoom to Fit"
+
+#: mouse.cpp:990 mouse.cpp:1277
+msgid "click next point of line, or press Esc"
+msgstr "click next point of line, or press Esc"
+
+#: mouse.cpp:996
+msgid ""
+"Can't draw rectangle in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+"Can't draw rectangle in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+
+#: mouse.cpp:1030
+msgid "click to place other corner of rectangle"
+msgstr "click to place other corner of rectangle"
+
+#: mouse.cpp:1050
+msgid "click to set radius"
+msgstr "click to set radius"
+
+#: mouse.cpp:1055
+msgid ""
+"Can't draw arc in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+"Can't draw arc in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+
+#: mouse.cpp:1074
+msgid "click to place point"
+msgstr "click to place point"
+
+#: mouse.cpp:1090
+msgid "click next point of cubic, or press Esc"
+msgstr "click next point of cubic, or press Esc"
+
+#: mouse.cpp:1095
+msgid ""
+"Sketching in a workplane already; sketch in 3d before creating new workplane."
+msgstr ""
+"Sketching in a workplane already; sketch in 3d before creating new workplane."
+
+#: mouse.cpp:1111
+msgid ""
+"Can't draw text in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+"Can't draw text in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+
+#: mouse.cpp:1128
+msgid "click to place bottom right of text"
+msgstr "click to place bottom right of text"
+
+#: mouse.cpp:1134
+msgid ""
+"Can't draw image in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+"Can't draw image in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+
+#: mouse.cpp:1161
+msgid "NEW COMMENT -- DOUBLE-CLICK TO EDIT"
+msgstr "NEW COMMENT -- DOUBLE-CLICK TO EDIT"
+
+#: platform/gui.cpp:85 platform/gui.cpp:89
+msgctxt "file-type"
+msgid "SolveSpace models"
+msgstr "SolveSpace models"
+
+#: platform/gui.cpp:90
+msgctxt "file-type"
+msgid "IDF circuit board"
+msgstr "IDF circuit board"
+
+#: platform/gui.cpp:94
+msgctxt "file-type"
+msgid "PNG image"
+msgstr "PNG image"
+
+#: platform/gui.cpp:98
+msgctxt "file-type"
+msgid "STL mesh"
+msgstr "STL mesh"
+
+#: platform/gui.cpp:99
+msgctxt "file-type"
+msgid "Wavefront OBJ mesh"
+msgstr "Wavefront OBJ mesh"
+
+#: platform/gui.cpp:100
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, with viewer"
+msgstr "Three.js-compatible mesh, with viewer"
+
+#: platform/gui.cpp:101
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, mesh only"
+msgstr "Three.js-compatible mesh, mesh only"
+
+#: platform/gui.cpp:102
+msgctxt "file-type"
+msgid "Q3D Object file"
+msgstr "Q3D Object file"
+
+#: platform/gui.cpp:103
+msgctxt "file-type"
+msgid "VRML text file"
+msgstr "VRML text file"
+
+#: platform/gui.cpp:107 platform/gui.cpp:114 platform/gui.cpp:121
+msgctxt "file-type"
+msgid "STEP file"
+msgstr "STEP file"
+
+#: platform/gui.cpp:111
+msgctxt "file-type"
+msgid "PDF file"
+msgstr "PDF file"
+
+#: platform/gui.cpp:112
+msgctxt "file-type"
+msgid "Encapsulated PostScript"
+msgstr "Encapsulated PostScript"
+
+#: platform/gui.cpp:113
+msgctxt "file-type"
+msgid "Scalable Vector Graphics"
+msgstr "Scalable Vector Graphics"
+
+#: platform/gui.cpp:115 platform/gui.cpp:122
+msgctxt "file-type"
+msgid "DXF file (AutoCAD 2007)"
+msgstr "DXF file (AutoCAD 2007)"
+
+#: platform/gui.cpp:116
+msgctxt "file-type"
+msgid "HPGL file"
+msgstr "HPGL file"
+
+#: platform/gui.cpp:117
+msgctxt "file-type"
+msgid "G Code"
+msgstr "G Code"
+
+#: platform/gui.cpp:126
+msgctxt "file-type"
+msgid "AutoCAD DXF and DWG files"
+msgstr "AutoCAD DXF and DWG files"
+
+#: platform/gui.cpp:130
+msgctxt "file-type"
+msgid "Comma-separated values"
+msgstr "Comma-separated values"
+
+#: platform/guigtk.cpp:1317 platform/guimac.mm:1360 platform/guiwin.cpp:1608
+msgid "untitled"
+msgstr "untitled"
+
+#: platform/guigtk.cpp:1328 platform/guigtk.cpp:1361 platform/guimac.mm:1318
+#: platform/guiwin.cpp:1555
+msgctxt "title"
+msgid "Save File"
+msgstr "Save File"
+
+#: platform/guigtk.cpp:1329 platform/guigtk.cpp:1362 platform/guimac.mm:1301
+#: platform/guiwin.cpp:1557
+msgctxt "title"
+msgid "Open File"
+msgstr "Open File"
+
+#: platform/guigtk.cpp:1332 platform/guigtk.cpp:1368
+msgctxt "button"
+msgid "_Cancel"
+msgstr "_Cancel"
+
+#: platform/guigtk.cpp:1333 platform/guigtk.cpp:1366
+msgctxt "button"
+msgid "_Save"
+msgstr "_Save"
+
+#: platform/guigtk.cpp:1334 platform/guigtk.cpp:1367
+msgctxt "button"
+msgid "_Open"
+msgstr "_Open"
+
+#: style.cpp:166
+msgid ""
+"Can't assign style to an entity that's derived from another entity; try "
+"assigning a style to this entity's parent."
+msgstr ""
+"Can't assign style to an entity that's derived from another entity; try "
+"assigning a style to this entity's parent."
+
+#: style.cpp:665
+msgid "Style name cannot be empty"
+msgstr "Style name cannot be empty"
+
+#: textscreens.cpp:741
+msgid "Can't repeat fewer than 1 time."
+msgstr "Can't repeat fewer than 1 time."
+
+#: textscreens.cpp:745
+msgid "Can't repeat more than 999 times."
+msgstr "Can't repeat more than 999 times."
+
+#: textscreens.cpp:770
+msgid "Group name cannot be empty"
+msgstr "Group name cannot be empty"
+
+#: textscreens.cpp:813
+msgid "Opacity must be between zero and one."
+msgstr "Opacity must be between zero and one."
+
+#: textscreens.cpp:848
+msgid "Radius cannot be zero or negative."
+msgstr "Radius cannot be zero or negative."
+
+#: toolbar.cpp:18
+msgid "Sketch line segment"
+msgstr "Sketch line segment"
+
+#: toolbar.cpp:20
+msgid "Sketch rectangle"
+msgstr "Sketch rectangle"
+
+#: toolbar.cpp:22
+msgid "Sketch circle"
+msgstr "Sketch circle"
+
+#: toolbar.cpp:24
+msgid "Sketch arc of a circle"
+msgstr "Sketch arc of a circle"
+
+#: toolbar.cpp:26
+msgid "Sketch curves from text in a TrueType font"
+msgstr "Sketch curves from text in a TrueType font"
+
+#: toolbar.cpp:28
+msgid "Sketch image from a file"
+msgstr "Sketch image from a file"
+
+#: toolbar.cpp:30
+msgid "Create tangent arc at selected point"
+msgstr "Create tangent arc at selected point"
+
+#: toolbar.cpp:32
+msgid "Sketch cubic Bezier spline"
+msgstr "Sketch cubic Bezier spline"
+
+#: toolbar.cpp:34
+msgid "Sketch datum point"
+msgstr "Sketch datum point"
+
+#: toolbar.cpp:36
+msgid "Toggle construction"
+msgstr "Toggle construction"
+
+#: toolbar.cpp:38
+msgid "Split lines / curves where they intersect"
+msgstr "Split lines / curves where they intersect"
+
+#: toolbar.cpp:42
+msgid "Constrain distance / diameter / length"
+msgstr "Constrain distance / diameter / length"
+
+#: toolbar.cpp:44
+msgid "Constrain angle"
+msgstr "Constrain angle"
+
+#: toolbar.cpp:46
+msgid "Constrain to be horizontal"
+msgstr "Constrain to be horizontal"
+
+#: toolbar.cpp:48
+msgid "Constrain to be vertical"
+msgstr "Constrain to be vertical"
+
+#: toolbar.cpp:50
+msgid "Constrain to be parallel or tangent"
+msgstr "Constrain to be parallel or tangent"
+
+#: toolbar.cpp:52
+msgid "Constrain to be perpendicular"
+msgstr "Constrain to be perpendicular"
+
+#: toolbar.cpp:54
+msgid "Constrain point on line / curve / plane / point"
+msgstr "Constrain point on line / curve / plane / point"
+
+#: toolbar.cpp:56
+msgid "Constrain symmetric"
+msgstr "Constrain symmetric"
+
+#: toolbar.cpp:58
+msgid "Constrain equal length / radius / angle"
+msgstr "Constrain equal length / radius / angle"
+
+#: toolbar.cpp:60
+msgid "Constrain normals in same orientation"
+msgstr "Constrain normals in same orientation"
+
+#: toolbar.cpp:62
+msgid "Other supplementary angle"
+msgstr "Other supplementary angle"
+
+#: toolbar.cpp:64
+msgid "Toggle reference dimension"
+msgstr "Toggle reference dimension"
+
+#: toolbar.cpp:68
+msgid "New group extruding active sketch"
+msgstr "New group extruding active sketch"
+
+#: toolbar.cpp:70
+msgid "New group rotating active sketch"
+msgstr "New group rotating active sketch"
+
+#: toolbar.cpp:72
+msgid "New group step and repeat rotating"
+msgstr "New group step and repeat rotating"
+
+#: toolbar.cpp:74
+msgid "New group step and repeat translating"
+msgstr "New group step and repeat translating"
+
+#: toolbar.cpp:76
+msgid "New group in new workplane (thru given entities)"
+msgstr "New group in new workplane (thru given entities)"
+
+#: toolbar.cpp:78
+msgid "New group in 3d"
+msgstr "New group in 3d"
+
+#: toolbar.cpp:80
+msgid "New group linking / assembling file"
+msgstr "New group linking / assembling file"
+
+#: toolbar.cpp:84
+msgid "Nearest isometric view"
+msgstr "Nearest isometric view"
+
+#: toolbar.cpp:86
+msgid "Align view to active workplane"
+msgstr "Align view to active workplane"
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Error"
+msgstr "Error"
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Message"
+msgstr "Message"
+
+#: util.cpp:170
+msgctxt "button"
+msgid "&OK"
+msgstr "&OK"
+
+#: view.cpp:78
+msgid "Scale cannot be zero or negative."
+msgstr "Scale cannot be zero or negative."
+
+#: view.cpp:90 view.cpp:99
+msgid "Bad format: specify x, y, z"
+msgstr "Bad format: specify x, y, z"
+
+#~ msgctxt "title"
+#~ msgid "(new sketch)"
+#~ msgstr "(new sketch)"
+
+#~ msgctxt "title"
+#~ msgid "Property Browser"
+#~ msgstr "Property Browser"
--- /dev/null
+# French translations for the SolveSpace package.
+# Copyright (C) 2018 the SolveSpace authors
+# This file is distributed under the same license as the SolveSpace package.
+# whitequark <whitequark@whitequark.org>, 2018. #zanata
+msgid ""
+msgstr ""
+"Project-Id-Version: SolveSpace 3.0\n"
+"Report-Msgid-Bugs-To: whitequark@whitequark.org\n"
+"POT-Creation-Date: 2020-11-17 20:50-0500\n"
+"PO-Revision-Date: 2018-07-14 06:12+0000\n"
+"Last-Translator: whitequark <whitequark@whitequark.org>\n"
+"Language-Team: none\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Zanata 4.4.5\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: clipboard.cpp:274
+msgid ""
+"Cut, paste, and copy work only in a workplane.\n"
+"\n"
+"Activate one with Sketch -> In Workplane."
+msgstr ""
+"Couper, coller et copier uniquement dans un plan de travail.\n"
+"\n"
+"Activez un plan avec \"Dessin -> Dans plan de travail\"."
+
+#: clipboard.cpp:291
+msgid "Clipboard is empty; nothing to paste."
+msgstr "Presse papier vide; rien à coller."
+
+#: clipboard.cpp:338
+msgid "Number of copies to paste must be at least one."
+msgstr "Le nombre de copies à coller doit être d'au moins un."
+
+#: clipboard.cpp:354 textscreens.cpp:783
+msgid "Scale cannot be zero."
+msgstr "L'échelle ne peut pas être zéro."
+
+#: clipboard.cpp:396
+msgid "Select one point to define origin of rotation."
+msgstr "Sélectionnez un point pour définir l'origine de la rotation."
+
+#: clipboard.cpp:408
+msgid "Select two points to define translation vector."
+msgstr "Sélectionnez deux points pour définir le vecteur de translation."
+
+#: clipboard.cpp:418
+msgid ""
+"Transformation is identity. So all copies will be exactly on top of each "
+"other."
+msgstr ""
+"Transformation identique. Donc, toutes les copies seront exactement les unes "
+"au-dessus des autres."
+
+#: clipboard.cpp:422
+msgid "Too many items to paste; split this into smaller pastes."
+msgstr "Trop d'éléments à coller; Divisez-les en plus petits groupes."
+
+#: clipboard.cpp:427
+msgid "No workplane active."
+msgstr "Pas d'espace de travail actif."
+
+#: confscreen.cpp:410
+msgid "Bad format: specify coordinates as x, y, z"
+msgstr "Mauvais format: spécifiez les coordonnées comme x, y, z"
+
+#: confscreen.cpp:420 style.cpp:659 textscreens.cpp:805
+msgid "Bad format: specify color as r, g, b"
+msgstr "Mauvais format; spécifiez la couleur comme r, v, b"
+
+#: confscreen.cpp:446
+msgid ""
+"The perspective factor will have no effect until you enable View -> Use "
+"Perspective Projection."
+msgstr ""
+"Le facteur de perspective n'aura aucun effet tant que vous n'aurez pas "
+"activé \"Affichage -> Utiliser la projection de perspective\"."
+
+#: confscreen.cpp:459 confscreen.cpp:469
+#, c-format
+msgid "Specify between 0 and %d digits after the decimal."
+msgstr ""
+
+#: confscreen.cpp:481
+msgid "Export scale must not be zero!"
+msgstr "L'échelle d'export ne doit pas être zéro!"
+
+#: confscreen.cpp:493
+msgid "Cutter radius offset must not be negative!"
+msgstr "Le décalage du rayon de coupe ne doit pas être négatif!"
+
+#: confscreen.cpp:547
+msgid "Bad value: autosave interval should be positive"
+msgstr ""
+"Mauvaise valeur: l'intervalle d'enregistrement automatique devrait être "
+"positif"
+
+#: confscreen.cpp:550
+msgid "Bad format: specify interval in integral minutes"
+msgstr "Mauvais format: spécifiez un nombre entier de minutes"
+
+#: constraint.cpp:12
+msgctxt "constr-name"
+msgid "pts-coincident"
+msgstr "pts-coïncidence"
+
+#: constraint.cpp:13
+msgctxt "constr-name"
+msgid "pt-pt-distance"
+msgstr "pt-pt-distance"
+
+#: constraint.cpp:14
+msgctxt "constr-name"
+msgid "pt-line-distance"
+msgstr "pt-ligne-distance"
+
+#: constraint.cpp:15
+msgctxt "constr-name"
+msgid "pt-plane-distance"
+msgstr "pt-plan-distance"
+
+#: constraint.cpp:16
+msgctxt "constr-name"
+msgid "pt-face-distance"
+msgstr "pt-face-distance"
+
+#: constraint.cpp:17
+msgctxt "constr-name"
+msgid "proj-pt-pt-distance"
+msgstr "proj-pt-pt-distance"
+
+#: constraint.cpp:18
+msgctxt "constr-name"
+msgid "pt-in-plane"
+msgstr "pt-dans-plan"
+
+#: constraint.cpp:19
+msgctxt "constr-name"
+msgid "pt-on-line"
+msgstr "pt-sur-ligne"
+
+#: constraint.cpp:20
+msgctxt "constr-name"
+msgid "pt-on-face"
+msgstr "pt-sur-face"
+
+#: constraint.cpp:21
+msgctxt "constr-name"
+msgid "eq-length"
+msgstr "eg-longueur"
+
+#: constraint.cpp:22
+msgctxt "constr-name"
+msgid "eq-length-and-pt-ln-dist"
+msgstr "eg-longueur-et-pt-dans-dist"
+
+#: constraint.cpp:23
+msgctxt "constr-name"
+msgid "eq-pt-line-distances"
+msgstr "eg-pt-ligne-distances"
+
+#: constraint.cpp:24
+msgctxt "constr-name"
+msgid "length-ratio"
+msgstr "longueur-ratio"
+
+#: constraint.cpp:25
+msgctxt "constr-name"
+msgid "length-difference"
+msgstr "longueur-difference"
+
+#: constraint.cpp:26
+msgctxt "constr-name"
+msgid "symmetric"
+msgstr "symétrique"
+
+#: constraint.cpp:27
+msgctxt "constr-name"
+msgid "symmetric-h"
+msgstr "symétrique-h"
+
+#: constraint.cpp:28
+msgctxt "constr-name"
+msgid "symmetric-v"
+msgstr "symétrique-v"
+
+#: constraint.cpp:29
+msgctxt "constr-name"
+msgid "symmetric-line"
+msgstr "symétrique-ligne"
+
+#: constraint.cpp:30
+msgctxt "constr-name"
+msgid "at-midpoint"
+msgstr "au-point-milieu"
+
+#: constraint.cpp:31
+msgctxt "constr-name"
+msgid "horizontal"
+msgstr "horizontal"
+
+#: constraint.cpp:32
+msgctxt "constr-name"
+msgid "vertical"
+msgstr "vertical"
+
+#: constraint.cpp:33
+msgctxt "constr-name"
+msgid "diameter"
+msgstr "diamètre"
+
+#: constraint.cpp:34
+msgctxt "constr-name"
+msgid "pt-on-circle"
+msgstr "pt-sur-cercle"
+
+#: constraint.cpp:35
+msgctxt "constr-name"
+msgid "same-orientation"
+msgstr "même-orientation"
+
+#: constraint.cpp:36
+msgctxt "constr-name"
+msgid "angle"
+msgstr "angle"
+
+#: constraint.cpp:37
+msgctxt "constr-name"
+msgid "parallel"
+msgstr "parallèle"
+
+#: constraint.cpp:38
+msgctxt "constr-name"
+msgid "arc-line-tangent"
+msgstr "arc-ligne-tangente"
+
+#: constraint.cpp:39
+msgctxt "constr-name"
+msgid "cubic-line-tangent"
+msgstr "cubique-ligne-tangente"
+
+#: constraint.cpp:40
+msgctxt "constr-name"
+msgid "curve-curve-tangent"
+msgstr "courbe-courbe-tangente"
+
+#: constraint.cpp:41
+msgctxt "constr-name"
+msgid "perpendicular"
+msgstr "perpendiculaire"
+
+#: constraint.cpp:42
+msgctxt "constr-name"
+msgid "eq-radius"
+msgstr "eg-rayon"
+
+#: constraint.cpp:43
+msgctxt "constr-name"
+msgid "eq-angle"
+msgstr "eg-angle"
+
+#: constraint.cpp:44
+msgctxt "constr-name"
+msgid "eq-line-len-arc-len"
+msgstr "eg-ligne-long-arc-long"
+
+#: constraint.cpp:45
+msgctxt "constr-name"
+msgid "lock-where-dragged"
+msgstr "verrouillé-où-déplacé"
+
+#: constraint.cpp:46
+msgctxt "constr-name"
+msgid "comment"
+msgstr "commentaire"
+
+#: constraint.cpp:171
+msgid ""
+"Bad selection for distance / diameter constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two points (distance between points)\n"
+" * a line segment (length)\n"
+" * two points and a line segment or normal (projected distance)\n"
+" * a workplane and a point (minimum distance)\n"
+" * a line segment and a point (minimum distance)\n"
+" * a plane face and a point (minimum distance)\n"
+" * a circle or an arc (diameter)\n"
+msgstr ""
+"Mauvaise sélection pour la contrainte distance / diamètre. Cette contrainte "
+"peut s'appliquer à:\n"
+"\n"
+" * Deux points (distance entre points)\n"
+" * Un segment de ligne (longueur)\n"
+" * Deux points et un segment de ligne ou normal (distance projetée)\n"
+" * Un plan de travail et un point (distance minimale)\n"
+" * Un segment de ligne et un point (distance minimale)\n"
+" * Une face plane et un point (distance minimale)\n"
+" * Un cercle ou un arc (diamètre)\n"
+
+#: constraint.cpp:224
+msgid ""
+"Bad selection for on point / curve / plane constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points (points coincident)\n"
+" * a point and a workplane (point in plane)\n"
+" * a point and a line segment (point on line)\n"
+" * a point and a circle or arc (point on curve)\n"
+" * a point and a plane face (point on face)\n"
+msgstr ""
+"Mauvaise sélection pour la contrainte point / courbe / plan. Cette "
+"contrainte peut s'appliquer à:\n"
+"\n"
+" * Deux points (points coïncidents)\n"
+" * Un point et un plan de travail (point dans le plan)\n"
+" * Un point et un segment de ligne (point en ligne)\n"
+" * Un point et un cercle ou un arc (point sur courbe)\n"
+" * Un point et une face plane (point sur une face)\n"
+
+#: constraint.cpp:286
+msgid ""
+"Bad selection for equal length / radius constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two line segments (equal length)\n"
+" * two line segments and two points (equal point-line distances)\n"
+" * a line segment and two points (equal point-line distances)\n"
+" * a line segment, and a point and line segment (point-line distance "
+"equals length)\n"
+" * four line segments or normals (equal angle between A,B and C,D)\n"
+" * three line segments or normals (equal angle between A,B and B,C)\n"
+" * two circles or arcs (equal radius)\n"
+" * a line segment and an arc (line segment length equals arc length)\n"
+msgstr ""
+"Mauvaise sélection pour une contrainte de longueur / rayon égale. Cette "
+"contrainte peut s'appliquer à:\n"
+"\n"
+" * Deux segments de ligne (longueur égale)\n"
+" * Deux segments de ligne et deux points (distances point-ligne égales)\n"
+" * Un segment de ligne et deux points (distances point-ligne égales)\n"
+" * Un segment de ligne ou un segment de ligne et point (distance point-"
+"ligne de longueur égale)\n"
+" * Quatre segments de ligne ou des normales (angle entre A, B et C, D "
+"égaux)\n"
+" * Trois segments de ligne ou des normales (angle entre A, B et B, C "
+"égaux)\n"
+" * Deux cercles ou arcs (rayon égaux)\n"
+" * Un segment de ligne et un arc (la longueur de segment de ligne est "
+"égale à la longueur d'arc)\n"
+
+#: constraint.cpp:325
+msgid ""
+"Bad selection for length ratio constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+"Mauvaise sélection pour la contrainte du rapport de longueur. Cette "
+"contrainte peut s'appliquer à:\n"
+"\n"
+" * Deux segments de ligne\n"
+
+#: constraint.cpp:342
+msgid ""
+"Bad selection for length difference constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+"Mauvaise sélection pour la contrainte de différence de longueur. Cette "
+"contrainte peut s'appliquer à:\n"
+"\n"
+" * Deux segments de ligne\n"
+
+#: constraint.cpp:368
+msgid ""
+"Bad selection for at midpoint constraint. This constraint can apply to:\n"
+"\n"
+" * a line segment and a point (point at midpoint)\n"
+" * a line segment and a workplane (line's midpoint on plane)\n"
+msgstr ""
+"Mauvaise sélection pour une contrainte de point médian. Cette contrainte "
+"peut s'appliquer à:\n"
+"\n"
+" * Un segment de ligne et un point (point au milieu)\n"
+" * Un segment de ligne et un plan de travail (point médian dans le plan)\n"
+
+#: constraint.cpp:426
+msgid ""
+"Bad selection for symmetric constraint. This constraint can apply to:\n"
+"\n"
+" * two points or a line segment (symmetric about workplane's coordinate "
+"axis)\n"
+" * line segment, and two points or a line segment (symmetric about line "
+"segment)\n"
+" * workplane, and two points or a line segment (symmetric about "
+"workplane)\n"
+msgstr ""
+"Mauvaise sélection pour la contrainte symétrique. Cette contrainte peut "
+"s'appliquer à:\n"
+"\n"
+" * Deux points ou un segment de ligne (symétrique à l'axe des coordonnées "
+"du plan de travail)\n"
+" * Segment de ligne, et deux points ou un segment de ligne (symétrique "
+"sur le segment de ligne)\n"
+" * Plan de travail, et deux points ou un segment de ligne (symétrique au "
+"plan de travail)\n"
+
+#: constraint.cpp:440
+msgid ""
+"A workplane must be active when constraining symmetric without an explicit "
+"symmetry plane."
+msgstr ""
+"Un plan de travail doit être actif lors d'une contrainte de symétrie sans "
+"plan de symétrie explicite."
+
+#: constraint.cpp:470
+msgid ""
+"Activate a workplane (with Sketch -> In Workplane) before applying a "
+"horizontal or vertical constraint."
+msgstr ""
+"Activez un plan de travail (avec Dessin -> Dans plan de travail) avant "
+"d'appliquer une contrainte horizontale ou verticale."
+
+#: constraint.cpp:483
+msgid ""
+"Bad selection for horizontal / vertical constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points\n"
+" * a line segment\n"
+msgstr ""
+"Mauvaise sélection pour la contrainte horizontale / verticale. Cette "
+"contrainte peut s'appliquer à:\n"
+"\n"
+" * deux points\n"
+" * Un segment de ligne\n"
+
+#: constraint.cpp:504
+msgid ""
+"Bad selection for same orientation constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two normals\n"
+msgstr ""
+"Mauvaise sélection pour la même contrainte d'orientation. Cette contrainte "
+"peut s'appliquer à:\n"
+"\n"
+" * Deux normales\n"
+
+#: constraint.cpp:554
+msgid "Must select an angle constraint."
+msgstr "Vous devez sélectionner une contrainte d'angle."
+
+#: constraint.cpp:567
+msgid "Must select a constraint with associated label."
+msgstr "Vous devez sélectionner une contrainte avec une étiquette associée."
+
+#: constraint.cpp:578
+msgid ""
+"Bad selection for angle constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+"Mauvaise sélection pour une contrainte d'angle. Cette contrainte peut "
+"s'appliquer à:\n"
+"\n"
+" * Deux segments de ligne\n"
+" * Un segment de ligne et une normale\n"
+" * Deux normales\n"
+
+#: constraint.cpp:635
+msgid ""
+"The tangent arc and line segment must share an endpoint. Constrain them with "
+"Constrain -> On Point before constraining tangent."
+msgstr ""
+"L'arc tangent et le segment de ligne doivent partager un point final. "
+"Contraignez-les avec \"Contrainte -> Sur point avant de contraindre la "
+"tangente\"."
+
+#: constraint.cpp:659
+msgid ""
+"The tangent cubic and line segment must share an endpoint. Constrain them "
+"with Constrain -> On Point before constraining tangent."
+msgstr ""
+"La tangente cubique et le segment de ligne doivent partager un point final. "
+"Contraignez-les avec \"Contrainte -> Sur point avant de contraindre la "
+"tangente\"."
+
+#: constraint.cpp:669
+msgid "Curve-curve tangency must apply in workplane."
+msgstr "Courbe-Courbe tangence doit s'appliquer dans le plan de travail."
+
+#: constraint.cpp:687
+msgid ""
+"The curves must share an endpoint. Constrain them with Constrain -> On Point "
+"before constraining tangent."
+msgstr ""
+"Les courbes doivent partager un point final. Contraignez-les avec "
+"\"Contrainte -> Sur point avant de contraindre la tangente\"."
+
+#: constraint.cpp:696
+msgid ""
+"Bad selection for parallel / tangent constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments (parallel)\n"
+" * a line segment and a normal (parallel)\n"
+" * two normals (parallel)\n"
+" * two line segments, arcs, or beziers, that share an endpoint (tangent)\n"
+msgstr ""
+"Mauvaise sélection pour la contrainte parallèle / tangente. Cette contrainte "
+"peut s'appliquer à:\n"
+"\n"
+" * Deux segments de ligne (parallèles)\n"
+" * Un segment de ligne et un parallèle (parallèle)\n"
+" * Deux normales (parallèles)\n"
+" * Deux segments de ligne, des arcs ou des Béziers, qui partagent un "
+"point final (tangent)\n"
+
+#: constraint.cpp:714
+msgid ""
+"Bad selection for perpendicular constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+"Mauvaise sélection pour une contrainte perpendiculaire. Cette contrainte "
+"peut s'appliquer à:\n"
+"\n"
+" * Deux segments de ligne\n"
+" * Un segment de ligne et une normale\n"
+" * Deux normales\n"
+
+#: constraint.cpp:729
+msgid ""
+"Bad selection for lock point where dragged constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * a point\n"
+msgstr ""
+"Mauvaise sélection pour le point de verrouillage où la contrainte déplacé. "
+"Cette contrainte peut s'appliquer à:\n"
+"\n"
+" * un point\n"
+
+#: constraint.cpp:740
+msgid "click center of comment text"
+msgstr "cliquez le centre du texte de commentaire"
+
+#: export.cpp:19
+msgid ""
+"No solid model present; draw one with extrudes and revolves, or use Export "
+"2d View to export bare lines and curves."
+msgstr ""
+"Aucun modèle solide présent; Dessinez-en un avec une extrusion et "
+"révolution, ou utilisez \"Exporter vue 2d\" pour exporter les lignes et les "
+"courbes dépouillées."
+
+#: export.cpp:61
+msgid ""
+"Bad selection for export section. Please select:\n"
+"\n"
+" * nothing, with an active workplane (workplane is section plane)\n"
+" * a face (section plane through face)\n"
+" * a point and two line segments (plane through point and parallel to "
+"lines)\n"
+msgstr ""
+"Mauvaise sélection pour la section export. Sélectionnez:\n"
+"\n"
+" * Rien, avec un plan de travail actif (plan de travail est un plan de "
+"section)\n"
+" * Une face (plan de coupe au-travers d'une face)\n"
+" * Un point et deux segments de ligne (plan au-travers d'un point et "
+"parallèle aux lignes)\n"
+
+#: export.cpp:822
+msgid "Active group mesh is empty; nothing to export."
+msgstr "Le maillage du groupe actif est vide; Rien à exporter."
+
+#: exportvector.cpp:337
+msgid "freehand lines were replaced with continuous lines"
+msgstr "les lignes à main levée ont été remplacées par des lignes continues"
+
+#: exportvector.cpp:339
+msgid "zigzag lines were replaced with continuous lines"
+msgstr "les lignes en zigzag ont été remplacées par des lignes continues"
+
+#: exportvector.cpp:593
+msgid ""
+"Some aspects of the drawing have no DXF equivalent and were not exported:\n"
+msgstr ""
+"Certains aspects du dessin n'ont pas d'équivalent DXF et n'ont pas été "
+"exportés:\n"
+
+#: exportvector.cpp:839
+msgid ""
+"PDF page size exceeds 200 by 200 inches; many viewers may reject this file."
+msgstr ""
+"La taille de la page PDF dépasse 200 par 200 pouces; De nombreux lecteurs "
+"peuvent rejeter ce fichier."
+
+#: file.cpp:44 group.cpp:91
+msgctxt "group-name"
+msgid "sketch-in-plane"
+msgstr "dessin-dans-plan"
+
+#: file.cpp:62
+msgctxt "group-name"
+msgid "#references"
+msgstr "#références"
+
+#: file.cpp:549
+msgid ""
+"Unrecognized data in file. This file may be corrupt, or from a newer version "
+"of the program."
+msgstr ""
+"Données non reconnues dans le fichier. Ce fichier peut être corrompu ou "
+"depuis une version plus récente du programme."
+
+#: file.cpp:859
+msgctxt "title"
+msgid "Missing File"
+msgstr "Fichier manquant"
+
+#: file.cpp:860
+#, c-format
+msgctxt "dialog"
+msgid "The linked file “%s” is not present."
+msgstr ""
+
+#: file.cpp:862
+msgctxt "dialog"
+msgid ""
+"Do you want to locate it manually?\n"
+"\n"
+"If you decline, any geometry that depends on the missing file will be "
+"permanently removed."
+msgstr ""
+
+#: file.cpp:865
+msgctxt "button"
+msgid "&Yes"
+msgstr ""
+
+#: file.cpp:867
+msgctxt "button"
+msgid "&No"
+msgstr ""
+
+#: file.cpp:869
+msgctxt "button"
+msgid "&Cancel"
+msgstr ""
+
+#: graphicswin.cpp:41
+msgid "&File"
+msgstr "&Fichier"
+
+#: graphicswin.cpp:42
+msgid "&New"
+msgstr "&Nouveau"
+
+#: graphicswin.cpp:43
+msgid "&Open..."
+msgstr "&Ouvrir..."
+
+#: graphicswin.cpp:44
+msgid "Open &Recent"
+msgstr "Ouvrir &Récent"
+
+#: graphicswin.cpp:45
+msgid "&Save"
+msgstr "&Sauver"
+
+#: graphicswin.cpp:46
+msgid "Save &As..."
+msgstr "Sauver &Comme..."
+
+#: graphicswin.cpp:48
+msgid "Export &Image..."
+msgstr "Exporter &Image..."
+
+#: graphicswin.cpp:49
+msgid "Export 2d &View..."
+msgstr "Exporter &vue 2D..."
+
+#: graphicswin.cpp:50
+msgid "Export 2d &Section..."
+msgstr "Exporter &Section 2d..."
+
+#: graphicswin.cpp:51
+msgid "Export 3d &Wireframe..."
+msgstr "Exporter &Fil de fer 3d..."
+
+#: graphicswin.cpp:52
+msgid "Export Triangle &Mesh..."
+msgstr "Exporter Triangle &Maillage..."
+
+#: graphicswin.cpp:53
+msgid "Export &Surfaces..."
+msgstr "Export &Surfaces..."
+
+#: graphicswin.cpp:54
+msgid "Im&port..."
+msgstr "Im&porter..."
+
+#: graphicswin.cpp:57
+msgid "E&xit"
+msgstr "&Quitter"
+
+#: graphicswin.cpp:60
+msgid "&Edit"
+msgstr "&Editer"
+
+#: graphicswin.cpp:61
+msgid "&Undo"
+msgstr "&Annuler"
+
+#: graphicswin.cpp:62
+msgid "&Redo"
+msgstr "&Refaire"
+
+#: graphicswin.cpp:63
+msgid "Re&generate All"
+msgstr "Re&générer Tout"
+
+#: graphicswin.cpp:65
+msgid "Snap Selection to &Grid"
+msgstr "Accrocher la sélection à la &Grille"
+
+#: graphicswin.cpp:66
+msgid "Rotate Imported &90°"
+msgstr "Rotation importation &90°"
+
+#: graphicswin.cpp:68
+msgid "Cu&t"
+msgstr "Co&uper"
+
+#: graphicswin.cpp:69
+msgid "&Copy"
+msgstr "&Copier"
+
+#: graphicswin.cpp:70
+msgid "&Paste"
+msgstr "Co&ller"
+
+#: graphicswin.cpp:71
+msgid "Paste &Transformed..."
+msgstr "Coller &Transformer..."
+
+#: graphicswin.cpp:72
+msgid "&Delete"
+msgstr "&Effacer"
+
+#: graphicswin.cpp:74
+msgid "Select &Edge Chain"
+msgstr "Sélectionner une Chaîne d'&Arêtes"
+
+#: graphicswin.cpp:75
+msgid "Select &All"
+msgstr "Sélectionner &Tout"
+
+#: graphicswin.cpp:76
+msgid "&Unselect All"
+msgstr "&Désélectionner Tout"
+
+#: graphicswin.cpp:78
+msgid "&Line Styles..."
+msgstr "&Ligne styles..."
+
+#: graphicswin.cpp:79
+msgid "&View Projection..."
+msgstr "&Affichage Perspective..."
+
+#: graphicswin.cpp:81
+msgid "Con&figuration..."
+msgstr "Con&figuration..."
+
+#: graphicswin.cpp:84
+msgid "&View"
+msgstr "&Affichage"
+
+#: graphicswin.cpp:85
+msgid "Zoom &In"
+msgstr "Zoom &Avant"
+
+#: graphicswin.cpp:86
+msgid "Zoom &Out"
+msgstr "Zoom A&rrière"
+
+#: graphicswin.cpp:87
+msgid "Zoom To &Fit"
+msgstr "Zoom A&justé"
+
+#: graphicswin.cpp:89
+msgid "Align View to &Workplane"
+msgstr "Aligner la vue au &Plan de travail"
+
+#: graphicswin.cpp:90
+msgid "Nearest &Ortho View"
+msgstr "Vue &Orthogonale la plus proche"
+
+#: graphicswin.cpp:91
+msgid "Nearest &Isometric View"
+msgstr "Vue &Isométrique la plus proche"
+
+#: graphicswin.cpp:92
+msgid "&Center View At Point"
+msgstr "&Centrer la Vue sur le Point"
+
+#: graphicswin.cpp:94
+msgid "Show Snap &Grid"
+msgstr "Afficher la &grille d'accrochage"
+
+#: graphicswin.cpp:95
+msgid "Use &Perspective Projection"
+msgstr "Utiliser la vue en &Perspective"
+
+#: graphicswin.cpp:96
+msgid "Dimension &Units"
+msgstr "&Unités de dimensions"
+
+#: graphicswin.cpp:97
+msgid "Dimensions in &Millimeters"
+msgstr "Dimensions en &Millimètres"
+
+#: graphicswin.cpp:98
+msgid "Dimensions in M&eters"
+msgstr "Dimensions en &Mètres"
+
+#: graphicswin.cpp:99
+msgid "Dimensions in &Inches"
+msgstr "Dimensions en &Pouces"
+
+#: graphicswin.cpp:101
+msgid "Show &Toolbar"
+msgstr "Affichage &Barre d'outils"
+
+#: graphicswin.cpp:102
+msgid "Show Property Bro&wser"
+msgstr "Affichage du &Navigateur de Propriété"
+
+#: graphicswin.cpp:104
+msgid "&Full Screen"
+msgstr "&Plein Ecran"
+
+#: graphicswin.cpp:106
+msgid "&New Group"
+msgstr "&Nouveau Groupe"
+
+#: graphicswin.cpp:107
+msgid "Sketch In &3d"
+msgstr "Dessin en &3d"
+
+#: graphicswin.cpp:108
+msgid "Sketch In New &Workplane"
+msgstr "Dessin dans un nouveau &Plan de travail"
+
+#: graphicswin.cpp:110
+msgid "Step &Translating"
+msgstr "Espacement &Linéaire"
+
+#: graphicswin.cpp:111
+msgid "Step &Rotating"
+msgstr "Espacement &Circulaire"
+
+#: graphicswin.cpp:113
+msgid "E&xtrude"
+msgstr "E&xtruder"
+
+#: graphicswin.cpp:114
+msgid "&Helix"
+msgstr "&Helix"
+
+#: graphicswin.cpp:115
+msgid "&Lathe"
+msgstr "&Lathe"
+
+#: graphicswin.cpp:116
+msgid "Re&volve"
+msgstr "Ré&volution"
+
+#: graphicswin.cpp:118
+msgid "Link / Assemble..."
+msgstr "Lié / Assembler..."
+
+#: graphicswin.cpp:119
+msgid "Link Recent"
+msgstr "Lié Récent"
+
+#: graphicswin.cpp:121
+msgid "&Sketch"
+msgstr "&Dessin"
+
+#: graphicswin.cpp:122
+msgid "In &Workplane"
+msgstr "Dans le &Plan de travail"
+
+#: graphicswin.cpp:123
+msgid "Anywhere In &3d"
+msgstr "N'importe où dans la &3d"
+
+#: graphicswin.cpp:125
+msgid "Datum &Point"
+msgstr "&Point"
+
+#: graphicswin.cpp:126
+msgid "&Workplane"
+msgstr "&Plan de travail"
+
+#: graphicswin.cpp:128
+msgid "Line &Segment"
+msgstr "Ligne - &Polyligne"
+
+#: graphicswin.cpp:129
+msgid "C&onstruction Line Segment"
+msgstr "Ligne de C&onstruction"
+
+#: graphicswin.cpp:130
+msgid "&Rectangle"
+msgstr "&Rectangle"
+
+#: graphicswin.cpp:131
+msgid "&Circle"
+msgstr "&Cercle"
+
+#: graphicswin.cpp:132
+msgid "&Arc of a Circle"
+msgstr "&Arc de Cercle"
+
+#: graphicswin.cpp:133
+msgid "&Bezier Cubic Spline"
+msgstr "Spline Cubique de &Beziers"
+
+#: graphicswin.cpp:135
+msgid "&Text in TrueType Font"
+msgstr "&Texte en Police TrueType"
+
+#: graphicswin.cpp:136
+msgid "&Image"
+msgstr "&Image"
+
+#: graphicswin.cpp:138
+msgid "To&ggle Construction"
+msgstr "&Basculer en mode \"Construction\""
+
+#: graphicswin.cpp:139
+msgid "Tangent &Arc at Point"
+msgstr "&Arc Tangent au Point"
+
+#: graphicswin.cpp:140
+msgid "Split Curves at &Intersection"
+msgstr "Diviser les Courbes à l'&Intersection"
+
+#: graphicswin.cpp:142
+msgid "&Constrain"
+msgstr "&Constraintes"
+
+#: graphicswin.cpp:143
+msgid "&Distance / Diameter"
+msgstr "&Distance / Diamètre"
+
+#: graphicswin.cpp:144
+msgid "Re&ference Dimension"
+msgstr "Dimension Maîtresse / Indicative"
+
+#: graphicswin.cpp:145
+msgid "A&ngle"
+msgstr "A&ngle"
+
+#: graphicswin.cpp:146
+msgid "Reference An&gle"
+msgstr "An&gle Maître / Indicatif"
+
+#: graphicswin.cpp:147
+msgid "Other S&upplementary Angle"
+msgstr "Autre angle S&upplémentaire"
+
+#: graphicswin.cpp:148
+msgid "Toggle R&eference Dim"
+msgstr "Basculer cote Maîtresse / cote Indicative"
+
+#: graphicswin.cpp:150
+msgid "&Horizontal"
+msgstr "&Horizontal"
+
+#: graphicswin.cpp:151
+msgid "&Vertical"
+msgstr "&Vertical"
+
+#: graphicswin.cpp:153
+msgid "&On Point / Curve / Plane"
+msgstr "&Sur Point / Courbe / Plan"
+
+#: graphicswin.cpp:154
+msgid "E&qual Length / Radius / Angle"
+msgstr "&Egale Longueur / Rayon / Angle"
+
+#: graphicswin.cpp:155
+msgid "Length Ra&tio"
+msgstr "R&apport de Longueur"
+
+#: graphicswin.cpp:156
+msgid "Length Diff&erence"
+msgstr "D&ifférence de Longueur"
+
+#: graphicswin.cpp:157
+msgid "At &Midpoint"
+msgstr "Au &Milieu"
+
+#: graphicswin.cpp:158
+msgid "S&ymmetric"
+msgstr "&Symétrique"
+
+#: graphicswin.cpp:159
+msgid "Para&llel / Tangent"
+msgstr "Para&llèle / Tangent"
+
+#: graphicswin.cpp:160
+msgid "&Perpendicular"
+msgstr "&Perpendiculaire"
+
+#: graphicswin.cpp:161
+msgid "Same Orient&ation"
+msgstr "Même Orient&ation"
+
+#: graphicswin.cpp:162
+msgid "Lock Point Where &Dragged"
+msgstr "Accrocher le point à l'&Emplacement"
+
+#: graphicswin.cpp:164
+msgid "Comment"
+msgstr "Commentaire"
+
+#: graphicswin.cpp:166
+msgid "&Analyze"
+msgstr "&Analyse"
+
+#: graphicswin.cpp:167
+msgid "Measure &Volume"
+msgstr "Mesure &Volume"
+
+#: graphicswin.cpp:168
+msgid "Measure A&rea"
+msgstr "Mesure &Aire"
+
+#: graphicswin.cpp:169
+msgid "Measure &Perimeter"
+msgstr "Mesure &Périmètre"
+
+#: graphicswin.cpp:170
+msgid "Show &Interfering Parts"
+msgstr "Montrer les Pièces &Interférentes"
+
+#: graphicswin.cpp:171
+msgid "Show &Naked Edges"
+msgstr "Montrer les Arêtes &Nues"
+
+#: graphicswin.cpp:172
+msgid "Show &Center of Mass"
+msgstr "Montrer le &Centre de Gravité"
+
+#: graphicswin.cpp:174
+msgid "Show &Underconstrained Points"
+msgstr "Montrer les &sous-contraintes Points"
+
+#: graphicswin.cpp:176
+msgid "&Trace Point"
+msgstr "&Tracer Point"
+
+#: graphicswin.cpp:177
+msgid "&Stop Tracing..."
+msgstr "&Arrêt Tracé..."
+
+#: graphicswin.cpp:178
+msgid "Step &Dimension..."
+msgstr "Espacement &Dimension..."
+
+#: graphicswin.cpp:180
+msgid "&Help"
+msgstr "&Aide"
+
+#: graphicswin.cpp:181
+msgid "&Language"
+msgstr "&Langue"
+
+#: graphicswin.cpp:182
+msgid "&Website / Manual"
+msgstr "&Site web / Manuel"
+
+#: graphicswin.cpp:184
+msgid "&About"
+msgstr "&A propos"
+
+#: graphicswin.cpp:352
+msgid "(no recent files)"
+msgstr "(pas de fichier récent)"
+
+#: graphicswin.cpp:360
+#, c-format
+msgid "File '%s' does not exist."
+msgstr ""
+
+#: graphicswin.cpp:721
+msgid "No workplane is active, so the grid will not appear."
+msgstr "Pas de plan de travail actif, donc la grille ne va pas apparaître."
+
+#: graphicswin.cpp:730
+msgid ""
+"The perspective factor is set to zero, so the view will always be a parallel "
+"projection.\n"
+"\n"
+"For a perspective projection, modify the perspective factor in the "
+"configuration screen. A value around 0.3 is typical."
+msgstr ""
+"Le facteur de perspective est réglé à 0, donc la vue restera une projection "
+"parallèle.\n"
+"\n"
+"Pour une projection en perspective, modifiez le facteur de perspective dans "
+"l'écran de configuration. Une valeur d'environ 0,3 est typique."
+
+#: graphicswin.cpp:809
+msgid ""
+"Select a point; this point will become the center of the view on screen."
+msgstr ""
+"Sélectionnez un point. Ce point deviendra le centre de la vue à l'écran."
+
+#: graphicswin.cpp:1103
+msgid "No additional entities share endpoints with the selected entities."
+msgstr ""
+"Aucune entité supplémentaire ne partage des points d'extrémité avec les "
+"entités sélectionnées."
+
+#: graphicswin.cpp:1121
+msgid ""
+"To use this command, select a point or other entity from an linked part, or "
+"make a link group the active group."
+msgstr ""
+"Pour utiliser cette commande, sélectionnez un point ou une autre entité à "
+"partir d'une pièce liée ou créez un groupe de liens dans le groupe actif."
+
+#: graphicswin.cpp:1144
+msgid ""
+"No workplane is active. Activate a workplane (with Sketch -> In Workplane) "
+"to define the plane for the snap grid."
+msgstr ""
+"Aucun plan de travail n'est actif. Activez un plan de travail (avec Dessin -"
+"> Dans plan de travail) pour définir le plan pour la grille d'accrochage."
+
+#: graphicswin.cpp:1151
+msgid ""
+"Can't snap these items to grid; select points, text comments, or constraints "
+"with a label. To snap a line, select its endpoints."
+msgstr ""
+"Impossible d'accrocher ces éléments à la grille. Sélectionnez des points, "
+"des textes de commentaires ou des contraintes avec une étiquette. Pour "
+"accrocher une ligne, sélectionnez ses points d'extrémité."
+
+#: graphicswin.cpp:1239
+msgid "No workplane selected. Activating default workplane for this group."
+msgstr ""
+"Aucun plan de travail sélectionné. Activation du plan de travail par défaut "
+"pour ce groupe."
+
+#: graphicswin.cpp:1242
+msgid ""
+"No workplane is selected, and the active group does not have a default "
+"workplane. Try selecting a workplane, or activating a sketch-in-new-"
+"workplane group."
+msgstr ""
+"Aucun plan de travail n'est sélectionné et le groupe actif n'a pas de plan "
+"de travail par défaut. Essayez de sélectionner un plan de travail ou "
+"d'activer un groupe de \"Dessin dans nouveau plan travail\"."
+
+#: graphicswin.cpp:1263
+msgid ""
+"Bad selection for tangent arc at point. Select a single point, or select "
+"nothing to set up arc parameters."
+msgstr ""
+"Mauvaise sélection pour l'arc tangent au point. Sélectionnez un seul point, "
+"ou ne sélectionnez rien pour configurer les paramètres de l'arc."
+
+#: graphicswin.cpp:1274
+msgid "click point on arc (draws anti-clockwise)"
+msgstr ""
+"cliquez un point sur l'arc (dessine dans le sens inverse des aiguilles d'une "
+"montre)"
+
+#: graphicswin.cpp:1275
+msgid "click to place datum point"
+msgstr "cliquez pour placer un point"
+
+#: graphicswin.cpp:1276
+msgid "click first point of line segment"
+msgstr "cliquez le premier point du segment de ligne"
+
+#: graphicswin.cpp:1278
+msgid "click first point of construction line segment"
+msgstr "cliquez le premier point de la ligne de construction"
+
+#: graphicswin.cpp:1279
+msgid "click first point of cubic segment"
+msgstr "cliquez le premier point du segment cubique"
+
+#: graphicswin.cpp:1280
+msgid "click center of circle"
+msgstr "cliquez pour placer le centre du cercle"
+
+#: graphicswin.cpp:1281
+msgid "click origin of workplane"
+msgstr "cliquez pour placer l'origine du plan de travail"
+
+#: graphicswin.cpp:1282
+msgid "click one corner of rectangle"
+msgstr "cliquez un coin du rectangle"
+
+#: graphicswin.cpp:1283
+msgid "click top left of text"
+msgstr "cliquez le haut à gauche du texte"
+
+#: graphicswin.cpp:1289
+msgid "click top left of image"
+msgstr "cliquez le haut à gauche de l'image"
+
+#: graphicswin.cpp:1301
+msgid ""
+"No entities are selected. Select entities before trying to toggle their "
+"construction state."
+msgstr ""
+"Aucune entité n'est sélectionnée. Sélectionnez les entités avant d'essayer "
+"de basculer leurs états de construction."
+
+#: group.cpp:86
+msgctxt "group-name"
+msgid "sketch-in-3d"
+msgstr "dessin-en-3d"
+
+#: group.cpp:142
+msgid ""
+"Bad selection for new sketch in workplane. This group can be created with:\n"
+"\n"
+" * a point (through the point, orthogonal to coordinate axes)\n"
+" * a point and two line segments (through the point, parallel to the "
+"lines)\n"
+" * a workplane (copy of the workplane)\n"
+msgstr ""
+"Mauvaise sélection pour un nouveau dessin dans le plan de travail. Ce groupe "
+"peut être créé avec:\n"
+"\n"
+" * Un point (par le point, orthogonal aux axes de coordonnées)\n"
+" * Un point et deux segments de ligne (par le point, parallèle aux "
+"lignes)\n"
+" * Un plan de travail (copie du plan de travail)\n"
+
+#: group.cpp:154
+msgid ""
+"Activate a workplane (Sketch -> In Workplane) before extruding. The sketch "
+"will be extruded normal to the workplane."
+msgstr ""
+"Activez un plan de travail (Dessin -> Dans plan de travail) avant "
+"l'extrusion. Le croquis sera extrudé normalement au plan de travail."
+
+#: group.cpp:163
+msgctxt "group-name"
+msgid "extrude"
+msgstr "extruder"
+
+#: group.cpp:168
+msgid "Lathe operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:179
+msgid ""
+"Bad selection for new lathe group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+"Mauvaise sélection pour un nouveau groupe de révolution. Ce groupe peut être "
+"créé avec:\n"
+"\n"
+" * Un point et un segment de ligne ou normal (révolution autour d'un axe "
+"parallèle à la ligne / point normal, par le point)\n"
+" * Un segment de ligne (révolution sur le segment de ligne)\n"
+
+#: group.cpp:189
+msgctxt "group-name"
+msgid "lathe"
+msgstr "révolution"
+
+#: group.cpp:194
+msgid "Revolve operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:205
+msgid ""
+"Bad selection for new revolve group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:217
+msgctxt "group-name"
+msgid "revolve"
+msgstr ""
+
+#: group.cpp:222
+msgid "Helix operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:233
+msgid ""
+"Bad selection for new helix group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:245
+msgctxt "group-name"
+msgid "helix"
+msgstr ""
+
+#: group.cpp:258
+msgid ""
+"Bad selection for new rotation. This group can be created with:\n"
+"\n"
+" * a point, while locked in workplane (rotate in plane, about that "
+"point)\n"
+" * a point and a line or a normal (rotate about an axis through the "
+"point, and parallel to line / normal)\n"
+msgstr ""
+"Mauvaise sélection pour une nouvelle rotation. Ce groupe peut être créé "
+"avec:\n"
+"\n"
+" * Un point, lorsqu'il est verrouillé dans un plan de travail (rotation "
+"dans le plan, autour de ce point)\n"
+" * Un point et une ligne ou une normale (tourner autour d'un axe par le "
+"point et parallèle à la ligne / normale)\n"
+
+#: group.cpp:271
+msgctxt "group-name"
+msgid "rotate"
+msgstr "rotation"
+
+#: group.cpp:282
+msgctxt "group-name"
+msgid "translate"
+msgstr "translation"
+
+#: group.cpp:400
+msgid "(unnamed)"
+msgstr "(sans nom)"
+
+#: groupmesh.cpp:708
+msgid "not closed contour, or not all same style!"
+msgstr "contour non fermé ou tout n'est pas du même style!"
+
+#: groupmesh.cpp:721
+msgid "points not all coplanar!"
+msgstr "les points ne sont pas tous coplanaires!"
+
+#: groupmesh.cpp:723
+msgid "contour is self-intersecting!"
+msgstr "le contour s'entrecroise!"
+
+#: groupmesh.cpp:725
+msgid "zero-length edge!"
+msgstr "arête de longueur nulle!"
+
+#: modify.cpp:254
+msgid "Must be sketching in workplane to create tangent arc."
+msgstr "Vous devez dessiner dans un plan pour créer un arc tangent."
+
+#: modify.cpp:301
+msgid ""
+"To create a tangent arc, select a point where two non-construction lines or "
+"circles in this group and workplane join."
+msgstr ""
+"Pour créer un arc tangent, sélectionnez un point où deux lignes (pas de "
+"construction) ou cercles de ce groupe et de ce plan se joignent."
+
+#: modify.cpp:388
+msgid ""
+"Couldn't round this corner. Try a smaller radius, or try creating the "
+"desired geometry by hand with tangency constraints."
+msgstr ""
+"Impossible d'arrondir ce coin. Essayez un rayon plus petit, ou essayez de "
+"créer la géométrie souhaitée à la main avec des contraintes tangentielles."
+
+#: modify.cpp:597
+msgid "Couldn't split this entity; lines, circles, or cubics only."
+msgstr ""
+"Impossible de diviser cette entité; Lignes, cercles ou cubiques uniquement."
+
+#: modify.cpp:624
+msgid "Must be sketching in workplane to split."
+msgstr "Vous devez dessiner dans un plan de travail pour diviser."
+
+#: modify.cpp:631
+msgid ""
+"Select two entities that intersect each other (e.g. two lines/circles/arcs "
+"or a line/circle/arc and a point)."
+msgstr ""
+
+#: modify.cpp:736
+msgid "Can't split; no intersection found."
+msgstr "Impossible de diviser; pas d'intersection trouvée."
+
+#: mouse.cpp:560
+msgid "Assign to Style"
+msgstr "Appliquer au style"
+
+#: mouse.cpp:576
+msgid "No Style"
+msgstr "Pas de style"
+
+#: mouse.cpp:579
+msgid "Newly Created Custom Style..."
+msgstr "Style personnalisé nouvellement créé ..."
+
+#: mouse.cpp:586
+msgid "Group Info"
+msgstr "Info Groupe"
+
+#: mouse.cpp:606
+msgid "Style Info"
+msgstr "Info Style"
+
+#: mouse.cpp:626
+msgid "Select Edge Chain"
+msgstr "Sélection Chaîne d'arêtes"
+
+#: mouse.cpp:632
+msgid "Toggle Reference Dimension"
+msgstr "Basculer cote maîtresse / cote indicative"
+
+#: mouse.cpp:638
+msgid "Other Supplementary Angle"
+msgstr "Autre angle supplémentaire"
+
+#: mouse.cpp:643
+msgid "Snap to Grid"
+msgstr "Accrocher à la grille"
+
+#: mouse.cpp:652
+msgid "Remove Spline Point"
+msgstr "Effacer le point de la Spline"
+
+#: mouse.cpp:687
+msgid "Add Spline Point"
+msgstr "Ajouter un point à la Spline"
+
+#: mouse.cpp:691
+msgid "Cannot add spline point: maximum number of points reached."
+msgstr ""
+"Impossible d'ajouter le point spline: nombre maximum de points atteints."
+
+#: mouse.cpp:716
+msgid "Toggle Construction"
+msgstr "Basculer en mode \"construction\"."
+
+#: mouse.cpp:731
+msgid "Delete Point-Coincident Constraint"
+msgstr "Effacer la contraint Point-Coïncident"
+
+#: mouse.cpp:750
+msgid "Cut"
+msgstr "Couper"
+
+#: mouse.cpp:752
+msgid "Copy"
+msgstr "Copier"
+
+#: mouse.cpp:756
+msgid "Select All"
+msgstr "Sélectionner tout"
+
+#: mouse.cpp:761
+msgid "Paste"
+msgstr "Coller"
+
+#: mouse.cpp:763
+msgid "Paste Transformed..."
+msgstr "Coller transformé..."
+
+#: mouse.cpp:768
+msgid "Delete"
+msgstr "Effacer"
+
+#: mouse.cpp:771
+msgid "Unselect All"
+msgstr "Désélectionner tout"
+
+#: mouse.cpp:778
+msgid "Unselect Hovered"
+msgstr "Désélectionner survolé"
+
+#: mouse.cpp:787
+msgid "Zoom to Fit"
+msgstr "Zoom pour ajuster"
+
+#: mouse.cpp:990 mouse.cpp:1277
+msgid "click next point of line, or press Esc"
+msgstr "cliquez pou le prochain point de ligne or appuyez sur Esc"
+
+#: mouse.cpp:996
+msgid ""
+"Can't draw rectangle in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+"Impossible de dessiner un rectangle en 3d; D'abord, activez un plan de "
+"travail avec \"Dessin -> Dans plan de travail\"."
+
+#: mouse.cpp:1030
+msgid "click to place other corner of rectangle"
+msgstr "cliquez pour placer un autre coin de rectangle"
+
+#: mouse.cpp:1050
+msgid "click to set radius"
+msgstr "cliquez pour ajuster le rayon"
+
+#: mouse.cpp:1055
+msgid ""
+"Can't draw arc in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+"Ne peut pas dessiner l'arc en 3d; D'abord, activez un plan de travail avec "
+"\"Dessin -> Dans plan de travail\"."
+
+#: mouse.cpp:1074
+msgid "click to place point"
+msgstr "cliquez pour placer un point"
+
+#: mouse.cpp:1090
+msgid "click next point of cubic, or press Esc"
+msgstr "cliquez le prochain point cubique ou appuyez sur Esc"
+
+#: mouse.cpp:1095
+msgid ""
+"Sketching in a workplane already; sketch in 3d before creating new workplane."
+msgstr ""
+"Vous dessinez déjà dans un plan de travail; Sélectionner \"Dessiner en 3d\" "
+"avant de créer un nouveau plan de travail."
+
+#: mouse.cpp:1111
+msgid ""
+"Can't draw text in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+"Impossible de dessiner du texte en 3d; D'abord, activer un plan de travail "
+"avec \"Dessin -> Dans plan de travail\"."
+
+#: mouse.cpp:1128
+msgid "click to place bottom right of text"
+msgstr ""
+
+#: mouse.cpp:1134
+msgid ""
+"Can't draw image in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+"Impossible de dessiner l'image en 3d; D'abord, activez un plan de travail "
+"avec \"Dessin -> Dans plan de travail\"."
+
+#: mouse.cpp:1161
+msgid "NEW COMMENT -- DOUBLE-CLICK TO EDIT"
+msgstr "NOUVEAU COMMENTAIRE - DOUBLE-CLIQUE POUR EDITER"
+
+#: platform/gui.cpp:85 platform/gui.cpp:89
+msgctxt "file-type"
+msgid "SolveSpace models"
+msgstr ""
+
+#: platform/gui.cpp:90
+msgctxt "file-type"
+msgid "IDF circuit board"
+msgstr ""
+
+#: platform/gui.cpp:94
+msgctxt "file-type"
+msgid "PNG image"
+msgstr ""
+
+#: platform/gui.cpp:98
+msgctxt "file-type"
+msgid "STL mesh"
+msgstr ""
+
+#: platform/gui.cpp:99
+msgctxt "file-type"
+msgid "Wavefront OBJ mesh"
+msgstr ""
+
+#: platform/gui.cpp:100
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, with viewer"
+msgstr ""
+
+#: platform/gui.cpp:101
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, mesh only"
+msgstr ""
+
+#: platform/gui.cpp:102
+msgctxt "file-type"
+msgid "Q3D Object file"
+msgstr ""
+
+#: platform/gui.cpp:103
+msgctxt "file-type"
+msgid "VRML text file"
+msgstr ""
+
+#: platform/gui.cpp:107 platform/gui.cpp:114 platform/gui.cpp:121
+msgctxt "file-type"
+msgid "STEP file"
+msgstr ""
+
+#: platform/gui.cpp:111
+msgctxt "file-type"
+msgid "PDF file"
+msgstr ""
+
+#: platform/gui.cpp:112
+msgctxt "file-type"
+msgid "Encapsulated PostScript"
+msgstr ""
+
+#: platform/gui.cpp:113
+msgctxt "file-type"
+msgid "Scalable Vector Graphics"
+msgstr ""
+
+#: platform/gui.cpp:115 platform/gui.cpp:122
+msgctxt "file-type"
+msgid "DXF file (AutoCAD 2007)"
+msgstr ""
+
+#: platform/gui.cpp:116
+msgctxt "file-type"
+msgid "HPGL file"
+msgstr ""
+
+#: platform/gui.cpp:117
+msgctxt "file-type"
+msgid "G Code"
+msgstr ""
+
+#: platform/gui.cpp:126
+msgctxt "file-type"
+msgid "AutoCAD DXF and DWG files"
+msgstr ""
+
+#: platform/gui.cpp:130
+msgctxt "file-type"
+msgid "Comma-separated values"
+msgstr ""
+
+#: platform/guigtk.cpp:1317 platform/guimac.mm:1360 platform/guiwin.cpp:1608
+msgid "untitled"
+msgstr "sans nom"
+
+#: platform/guigtk.cpp:1328 platform/guigtk.cpp:1361 platform/guimac.mm:1318
+#: platform/guiwin.cpp:1555
+msgctxt "title"
+msgid "Save File"
+msgstr "Sauver fichier"
+
+#: platform/guigtk.cpp:1329 platform/guigtk.cpp:1362 platform/guimac.mm:1301
+#: platform/guiwin.cpp:1557
+msgctxt "title"
+msgid "Open File"
+msgstr "Ouvrir Fichier"
+
+#: platform/guigtk.cpp:1332 platform/guigtk.cpp:1368
+msgctxt "button"
+msgid "_Cancel"
+msgstr "_Annuler"
+
+#: platform/guigtk.cpp:1333 platform/guigtk.cpp:1366
+msgctxt "button"
+msgid "_Save"
+msgstr "_Sauver"
+
+#: platform/guigtk.cpp:1334 platform/guigtk.cpp:1367
+msgctxt "button"
+msgid "_Open"
+msgstr ""
+
+#: style.cpp:166
+msgid ""
+"Can't assign style to an entity that's derived from another entity; try "
+"assigning a style to this entity's parent."
+msgstr ""
+"Impossible d'attribuer le style à une entité dérivée d'une autre entité; "
+"Essayez d'attribuer un style au parent de cette entité."
+
+#: style.cpp:665
+msgid "Style name cannot be empty"
+msgstr "Le nom d'un style ne peut pas être vide"
+
+#: textscreens.cpp:741
+msgid "Can't repeat fewer than 1 time."
+msgstr "Je ne peux pas répéter moins de 1 fois."
+
+#: textscreens.cpp:745
+msgid "Can't repeat more than 999 times."
+msgstr "Je ne peux pas répéter plus de 999 fois."
+
+#: textscreens.cpp:770
+msgid "Group name cannot be empty"
+msgstr "Un nom de groupe ne peut pas être vide"
+
+#: textscreens.cpp:813
+msgid "Opacity must be between zero and one."
+msgstr "L'opacité doit être entre 0 et 1."
+
+#: textscreens.cpp:848
+msgid "Radius cannot be zero or negative."
+msgstr "Le rayon ne peut pas être zéro ou négatif."
+
+#: toolbar.cpp:18
+msgid "Sketch line segment"
+msgstr "Dessin ligne - polyligne"
+
+#: toolbar.cpp:20
+msgid "Sketch rectangle"
+msgstr "Dessin d'un rectangle"
+
+#: toolbar.cpp:22
+msgid "Sketch circle"
+msgstr "Dessin d'un cercle"
+
+#: toolbar.cpp:24
+msgid "Sketch arc of a circle"
+msgstr "Dessin d'un arc de cercle"
+
+#: toolbar.cpp:26
+msgid "Sketch curves from text in a TrueType font"
+msgstr "Dessin de courbes depuis un texte en police TrueType"
+
+#: toolbar.cpp:28
+msgid "Sketch image from a file"
+msgstr "Dessin d'une image depuis un fichier"
+
+#: toolbar.cpp:30
+msgid "Create tangent arc at selected point"
+msgstr "Créer un arc tangent au point sélectionné"
+
+#: toolbar.cpp:32
+msgid "Sketch cubic Bezier spline"
+msgstr "Dessin d'une spline cubique de Bezier"
+
+#: toolbar.cpp:34
+msgid "Sketch datum point"
+msgstr "Dessin d'un point"
+
+#: toolbar.cpp:36
+msgid "Toggle construction"
+msgstr "Basculer en mode \"construction\""
+
+#: toolbar.cpp:38
+msgid "Split lines / curves where they intersect"
+msgstr "Diviser lignes / courbes où elles se croisent"
+
+#: toolbar.cpp:42
+msgid "Constrain distance / diameter / length"
+msgstr "Contraindre distance / diamètre / longueur"
+
+#: toolbar.cpp:44
+msgid "Constrain angle"
+msgstr "Contraindre angle"
+
+#: toolbar.cpp:46
+msgid "Constrain to be horizontal"
+msgstr "Contraindre à être horizontal"
+
+#: toolbar.cpp:48
+msgid "Constrain to be vertical"
+msgstr "Contraindre à être vertical"
+
+#: toolbar.cpp:50
+msgid "Constrain to be parallel or tangent"
+msgstr "Contraindre à être parallèle ou tangent"
+
+#: toolbar.cpp:52
+msgid "Constrain to be perpendicular"
+msgstr "Contraindre à être perpendiculaire"
+
+#: toolbar.cpp:54
+msgid "Constrain point on line / curve / plane / point"
+msgstr "Contraindre point sur ligne / courbe / plan / point"
+
+#: toolbar.cpp:56
+msgid "Constrain symmetric"
+msgstr "Contrainte symétrique"
+
+#: toolbar.cpp:58
+msgid "Constrain equal length / radius / angle"
+msgstr "Contrainte égale longueur / rayon / angle"
+
+#: toolbar.cpp:60
+msgid "Constrain normals in same orientation"
+msgstr "Contrainte normales dans la même direction"
+
+#: toolbar.cpp:62
+msgid "Other supplementary angle"
+msgstr "Autre angle supplémentaire"
+
+#: toolbar.cpp:64
+msgid "Toggle reference dimension"
+msgstr "Basculer cote maîtresse / cote indicative"
+
+#: toolbar.cpp:68
+msgid "New group extruding active sketch"
+msgstr "Nouveau groupe d'extrusion du dessin actif"
+
+#: toolbar.cpp:70
+msgid "New group rotating active sketch"
+msgstr "Nouveau groupe de révolution du dessin actif"
+
+#: toolbar.cpp:72
+msgid "New group step and repeat rotating"
+msgstr "Nouveau groupe de répétition circulaire"
+
+#: toolbar.cpp:74
+msgid "New group step and repeat translating"
+msgstr "Nouveau groupe de répétition linéaire"
+
+#: toolbar.cpp:76
+msgid "New group in new workplane (thru given entities)"
+msgstr ""
+"Nouveau groupe dans un nouveau plan de travail (Par des entités données)"
+
+#: toolbar.cpp:78
+msgid "New group in 3d"
+msgstr "Nouveau groupe en 3d"
+
+#: toolbar.cpp:80
+msgid "New group linking / assembling file"
+msgstr "Nouveau groupe lié / assemblage"
+
+#: toolbar.cpp:84
+msgid "Nearest isometric view"
+msgstr "Vue isométrique la plus proche"
+
+#: toolbar.cpp:86
+msgid "Align view to active workplane"
+msgstr "Aligner la vue sur le plan de travail actif"
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Error"
+msgstr "Erreur"
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Message"
+msgstr "Message"
+
+#: util.cpp:170
+msgctxt "button"
+msgid "&OK"
+msgstr ""
+
+#: view.cpp:78
+msgid "Scale cannot be zero or negative."
+msgstr "L'échelle ne peut pas être zéro ou négative."
+
+#: view.cpp:90 view.cpp:99
+msgid "Bad format: specify x, y, z"
+msgstr "Mauvais format: Spécifiez x, y, z"
+
+#~ msgctxt "title"
+#~ msgid "(new sketch)"
+#~ msgstr "(nouveau dessin)"
+
+#~ msgctxt "title"
+#~ msgid "Property Browser"
+#~ msgstr "Navigateur de propriété"
+
+#~ msgid "Specify between 0 and 8 digits after the decimal."
+#~ msgstr "Spécifiez entre 0 et 8 chiffres après la virgule."
+
+#~ msgid "Show Degrees of &Freedom"
+#~ msgstr "Montrer les Degrés de &Liberté"
+
+#~ msgid "click to place bottom left of text"
+#~ msgstr "cliquez pour placer le bas gauche du texte"
+
+#~ msgid "Do you want to save the changes you made to the new sketch?"
+#~ msgstr ""
+#~ "Voulez-vous enregistrer les modifications que vous avez apportées au "
+#~ "nouveau dessin?"
+
+#~ msgid "Your changes will be lost if you don't save them."
+#~ msgstr "Vos modifications seront perdues si vous ne les enregistrez pas."
+
+#~ msgctxt "button"
+#~ msgid "Save"
+#~ msgstr "Sauver"
+
+#~ msgctxt "button"
+#~ msgid "Cancel"
+#~ msgstr "Annuler"
+
+#~ msgctxt "button"
+#~ msgid "Don't Save"
+#~ msgstr "Ne pas sauver"
+
+#~ msgid "Do you want to load the autosave file instead?"
+#~ msgstr "Voulez-vous charger le fichier de sauvegarde à la place?"
+
+#~ msgctxt "button"
+#~ msgid "Load"
+#~ msgstr "Charger"
+
+#~ msgctxt "button"
+#~ msgid "Don't Load"
+#~ msgstr "Ne pas charger"
+
+#~ msgid ""
+#~ "Do you want to locate it manually?\n"
+#~ "If you select “No”, any geometry that depends on the missing file will be "
+#~ "removed."
+#~ msgstr ""
+#~ "Voulez-vous le localiser manuellement?\n"
+#~ "Si vous sélectionnez \"Non\", toute géométrie qui dépend du fichier "
+#~ "manquant sera supprimée."
+
+#~ msgctxt "button"
+#~ msgid "Yes"
+#~ msgstr "Oui"
+
+#~ msgctxt "button"
+#~ msgid "No"
+#~ msgstr "Non"
+
+#~ msgctxt "button"
+#~ msgid "OK"
+#~ msgstr "Valider"
+
+#~ msgid "_Cancel"
+#~ msgstr "_Annuler"
+
+#~ msgid "_Open"
+#~ msgstr "_Ouvrir"
+
+#~ msgid ""
+#~ "The file has changed since it was last saved.\n"
+#~ "\n"
+#~ "Do you want to save the changes?"
+#~ msgstr ""
+#~ "Le fichier a changé depuis sa dernière sauvegarde.\n"
+#~ "\n"
+#~ "Voulez-vous enregistrer les modifications?"
+
+#~ msgctxt "title"
+#~ msgid "Modified File"
+#~ msgstr "Fichier modifié"
+
+#~ msgctxt "button"
+#~ msgid "Do_n't Save"
+#~ msgstr "_Ne pas sauver"
+
+#~ msgctxt "title"
+#~ msgid "Autosave Available"
+#~ msgstr "Sauvegarde automatique existante"
+
+#~ msgctxt "button"
+#~ msgid "_Load autosave"
+#~ msgstr "_Charger la sauvegarde automatique"
+
+#~ msgctxt "button"
+#~ msgid "Do_n't Load"
+#~ msgstr "_Ne pas charger"
+
+#~ msgctxt "button"
+#~ msgid "_Yes"
+#~ msgstr "_Oui"
+
+#~ msgctxt "button"
+#~ msgid "_No"
+#~ msgstr "_Non"
+
+#~ msgid "SolveSpace models"
+#~ msgstr "Modèles SolveSpace"
+
+#~ msgid "PNG file"
+#~ msgstr "Fichier PNG"
+
+#~ msgid "STL mesh"
+#~ msgstr "Maillage STL"
+
+#~ msgid "Wavefront OBJ mesh"
+#~ msgstr "Maillage Wavefront OBJ"
+
+#~ msgid "Three.js-compatible mesh, with viewer"
+#~ msgstr "Maillage Three-js-compatible, avec visualisateur"
+
+#~ msgid "Three.js-compatible mesh, mesh only"
+#~ msgstr "Maillage Three-js-compatible, maillage seul"
+
+#~ msgid "STEP file"
+#~ msgstr "Fichier STEP"
+
+#~ msgid "PDF file"
+#~ msgstr "Fichier PDF"
+
+#~ msgid "Encapsulated PostScript"
+#~ msgstr "Encapsulated PostScript"
+
+#~ msgid "Scalable Vector Graphics"
+#~ msgstr "Scalable Vector Graphics"
+
+#~ msgid "DXF file (AutoCAD 2007)"
+#~ msgstr "Fichier DXF (AutoCAD 2007)"
+
+#~ msgid "HPGL file"
+#~ msgstr "Fichier HPGL"
+
+#~ msgid "G Code"
+#~ msgstr "G Code"
+
+#~ msgid "AutoCAD DXF and DWG files"
+#~ msgstr "Fichiers AutoCAD DXF et DWG"
+
+#~ msgid "Comma-separated values"
+#~ msgstr "Valeurs CSV séparées par des virgules"
--- /dev/null
+# Russian translations for SolveSpace package.
+# Copyright (C) 2017 the SolveSpace authors
+# This file is distributed under the same license as the SolveSpace package.
+# EvilSpirit <evilspirit@evilspirit.org>, 2017.
+msgid ""
+msgstr ""
+"Project-Id-Version: SolveSpace 3.0\n"
+"Report-Msgid-Bugs-To: whitequark@whitequark.org\n"
+"POT-Creation-Date: 2020-11-17 20:50-0500\n"
+"PO-Revision-Date: 2017-04-21 10:29+0700\n"
+"Last-Translator: evilspirit@evilspirit.org\n"
+"Language-Team: EvilSpirit\n"
+"Language: ru_RU\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.0.1\n"
+
+#: clipboard.cpp:274
+msgid ""
+"Cut, paste, and copy work only in a workplane.\n"
+"\n"
+"Activate one with Sketch -> In Workplane."
+msgstr ""
+"Копировать, вставить или вырезать\n"
+"можно только находясь в рабочей плоскости.\n"
+"Активируйте рабочую плоскость через Эскиз->В Рабочей Плоскости"
+
+#: clipboard.cpp:291
+msgid "Clipboard is empty; nothing to paste."
+msgstr "Буфер обмена пуст; нечего вставлять."
+
+#: clipboard.cpp:338
+msgid "Number of copies to paste must be at least one."
+msgstr "Укажите в поле 'количество' хотя бы одну копию для вставки."
+
+#: clipboard.cpp:354 textscreens.cpp:783
+msgid "Scale cannot be zero."
+msgstr "Масштабный коэффициент не может быть нулевым."
+
+#: clipboard.cpp:396
+msgid "Select one point to define origin of rotation."
+msgstr "Выберите одну точку в качестве центра вращения."
+
+#: clipboard.cpp:408
+msgid "Select two points to define translation vector."
+msgstr "Выберите две точки, чтобы задать вектор смещения."
+
+#: clipboard.cpp:418
+msgid ""
+"Transformation is identity. So all copies will be exactly on top of each "
+"other."
+msgstr ""
+"Трансформация не задана. Все копии будут расположены в одном и том же месте."
+
+#: clipboard.cpp:422
+msgid "Too many items to paste; split this into smaller pastes."
+msgstr "Слишком много элементов для вставки; разбейте на несколько частей."
+
+#: clipboard.cpp:427
+msgid "No workplane active."
+msgstr "Рабочая плоскость не активна"
+
+#: confscreen.cpp:410
+msgid "Bad format: specify coordinates as x, y, z"
+msgstr "Неверный формат: введите координаты как x, y, z"
+
+#: confscreen.cpp:420 style.cpp:659 textscreens.cpp:805
+msgid "Bad format: specify color as r, g, b"
+msgstr "Неверный формат: введите цвет как r, g, b"
+
+#: confscreen.cpp:446
+msgid ""
+"The perspective factor will have no effect until you enable View -> Use "
+"Perspective Projection."
+msgstr ""
+"Коэффициент перспективы не будет иметь эффект, пока вы не включите Вид-"
+">Перспективная Проекция."
+
+#: confscreen.cpp:459 confscreen.cpp:469
+#, c-format
+msgid "Specify between 0 and %d digits after the decimal."
+msgstr "Введите число от 0 до %d."
+
+#: confscreen.cpp:481
+msgid "Export scale must not be zero!"
+msgstr "Масштабный коэффициент не может быть нулевым!"
+
+#: confscreen.cpp:493
+msgid "Cutter radius offset must not be negative!"
+msgstr "Радиус режущего инструмента не может быть отрицательным!"
+
+#: confscreen.cpp:547
+msgid "Bad value: autosave interval should be positive"
+msgstr ""
+"Неверное значение: интервал автосохранения должен быть положительным числом"
+
+#: confscreen.cpp:550
+msgid "Bad format: specify interval in integral minutes"
+msgstr ""
+"Неверный формат: введите целое число, чтобы задать интервал автосохранения"
+
+#: constraint.cpp:12
+msgctxt "constr-name"
+msgid "pts-coincident"
+msgstr "тчк-тчк-совпадение"
+
+#: constraint.cpp:13
+msgctxt "constr-name"
+msgid "pt-pt-distance"
+msgstr "тчк-тчк-расстояние"
+
+#: constraint.cpp:14
+msgctxt "constr-name"
+msgid "pt-line-distance"
+msgstr "тчк-линия-расстояние"
+
+#: constraint.cpp:15
+msgctxt "constr-name"
+msgid "pt-plane-distance"
+msgstr "тчк-плоск-расстояние"
+
+#: constraint.cpp:16
+msgctxt "constr-name"
+msgid "pt-face-distance"
+msgstr "тчк-грань-расстояние"
+
+#: constraint.cpp:17
+msgctxt "constr-name"
+msgid "proj-pt-pt-distance"
+msgstr "проекц-тчк-тчк-расст"
+
+#: constraint.cpp:18
+msgctxt "constr-name"
+msgid "pt-in-plane"
+msgstr "тчк-на-плоск"
+
+#: constraint.cpp:19
+msgctxt "constr-name"
+msgid "pt-on-line"
+msgstr "тчк-на-линии"
+
+#: constraint.cpp:20
+msgctxt "constr-name"
+msgid "pt-on-face"
+msgstr "тчк-на-грани"
+
+#: constraint.cpp:21
+msgctxt "constr-name"
+msgid "eq-length"
+msgstr "равенство-длин"
+
+#: constraint.cpp:22
+msgctxt "constr-name"
+msgid "eq-length-and-pt-ln-dist"
+msgstr "равен-длины-и-тчк-лин-расст"
+
+#: constraint.cpp:23
+msgctxt "constr-name"
+msgid "eq-pt-line-distances"
+msgstr "равен-тчк-линия-расстояний"
+
+#: constraint.cpp:24
+msgctxt "constr-name"
+msgid "length-ratio"
+msgstr "отношение-длин"
+
+#: constraint.cpp:25
+msgctxt "constr-name"
+msgid "length-difference"
+msgstr "разность-длин"
+
+#: constraint.cpp:26
+msgctxt "constr-name"
+msgid "symmetric"
+msgstr "симметричность"
+
+#: constraint.cpp:27
+msgctxt "constr-name"
+msgid "symmetric-h"
+msgstr "симметричность-гориз"
+
+#: constraint.cpp:28
+msgctxt "constr-name"
+msgid "symmetric-v"
+msgstr "симметричность-верт"
+
+#: constraint.cpp:29
+msgctxt "constr-name"
+msgid "symmetric-line"
+msgstr "симметричность-по-оси"
+
+#: constraint.cpp:30
+msgctxt "constr-name"
+msgid "at-midpoint"
+msgstr "на-середине"
+
+#: constraint.cpp:31
+msgctxt "constr-name"
+msgid "horizontal"
+msgstr "горизонтальность"
+
+#: constraint.cpp:32
+msgctxt "constr-name"
+msgid "vertical"
+msgstr "вертикальность"
+
+#: constraint.cpp:33
+msgctxt "constr-name"
+msgid "diameter"
+msgstr "диаметр"
+
+#: constraint.cpp:34
+msgctxt "constr-name"
+msgid "pt-on-circle"
+msgstr "тчк-на-окружности"
+
+#: constraint.cpp:35
+msgctxt "constr-name"
+msgid "same-orientation"
+msgstr "идентичная-ориентация"
+
+#: constraint.cpp:36
+msgctxt "constr-name"
+msgid "angle"
+msgstr "угол"
+
+#: constraint.cpp:37
+msgctxt "constr-name"
+msgid "parallel"
+msgstr "параллельность"
+
+#: constraint.cpp:38
+msgctxt "constr-name"
+msgid "arc-line-tangent"
+msgstr "кас-дуга-линия"
+
+#: constraint.cpp:39
+msgctxt "constr-name"
+msgid "cubic-line-tangent"
+msgstr "кас-сплайн-линия"
+
+#: constraint.cpp:40
+msgctxt "constr-name"
+msgid "curve-curve-tangent"
+msgstr "кас-кривых"
+
+#: constraint.cpp:41
+msgctxt "constr-name"
+msgid "perpendicular"
+msgstr "перпендикулярность"
+
+#: constraint.cpp:42
+msgctxt "constr-name"
+msgid "eq-radius"
+msgstr "равенство-радиусов"
+
+#: constraint.cpp:43
+msgctxt "constr-name"
+msgid "eq-angle"
+msgstr "равенство-углов"
+
+#: constraint.cpp:44
+msgctxt "constr-name"
+msgid "eq-line-len-arc-len"
+msgstr "равен-длины-линии-длины-дуги"
+
+#: constraint.cpp:45
+msgctxt "constr-name"
+msgid "lock-where-dragged"
+msgstr "фиксация"
+
+#: constraint.cpp:46
+msgctxt "constr-name"
+msgid "comment"
+msgstr "комментарий"
+
+#: constraint.cpp:171
+msgid ""
+"Bad selection for distance / diameter constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two points (distance between points)\n"
+" * a line segment (length)\n"
+" * two points and a line segment or normal (projected distance)\n"
+" * a workplane and a point (minimum distance)\n"
+" * a line segment and a point (minimum distance)\n"
+" * a plane face and a point (minimum distance)\n"
+" * a circle or an arc (diameter)\n"
+msgstr ""
+"Неправильное выделение для ограничения расстояния / диаметра.\n"
+"Ограничение может принимать в качестве выделения следующие примитивы:\n"
+"\n"
+" * две точки (расстояние между точками)\n"
+" * отрезок (длина отрезка)\n"
+" * две точки и отрезок / нормаль (расстояние между точками, "
+"спроецированное на линию / нормаль)\n"
+" * рабочую плоскость и точку (расстояние от точки до плоскости)\n"
+" * отрезок и точку (расстояние от точки до линии)\n"
+" * грань и точку (расстояние от точки до плоскости грани)\n"
+" * окружность или дугу (диаметр / радиус)\n"
+
+#: constraint.cpp:224
+msgid ""
+"Bad selection for on point / curve / plane constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points (points coincident)\n"
+" * a point and a workplane (point in plane)\n"
+" * a point and a line segment (point on line)\n"
+" * a point and a circle or arc (point on curve)\n"
+" * a point and a plane face (point on face)\n"
+msgstr ""
+"Неправильное выделение для ограничения 'точка на примитиве'.\n"
+"Ограничение может принимать в качестве выделения следующие примитивы:\n"
+"\n"
+" * две точки (совпадение точек)\n"
+" * точку и рабочую плоскость (точка в плоскости)\n"
+" * точку и отрезок (точка на линии)\n"
+" * точку и окружность / дугу / сплайн (точка на кривой)\n"
+" * точку и грань (точка на грани)\n"
+
+#: constraint.cpp:286
+msgid ""
+"Bad selection for equal length / radius constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two line segments (equal length)\n"
+" * two line segments and two points (equal point-line distances)\n"
+" * a line segment and two points (equal point-line distances)\n"
+" * a line segment, and a point and line segment (point-line distance "
+"equals length)\n"
+" * four line segments or normals (equal angle between A,B and C,D)\n"
+" * three line segments or normals (equal angle between A,B and B,C)\n"
+" * two circles or arcs (equal radius)\n"
+" * a line segment and an arc (line segment length equals arc length)\n"
+msgstr ""
+"Неправильное выделение для ограничения 'равенство примитивов'.\n"
+"Ограничение может принимать в качестве выделения следующие примитивы:\n"
+"\n"
+" * два отрезка (равенство длин отрезков)\n"
+" * два отрезка и две точки (равенство расстояний от точек до линий)\n"
+" * отрезок и две точки (равенство расстояний от точек до линии)\n"
+" * отрезок, точку и отрезок (равенство длины отрезка расстоянию от точки "
+"до линии)\n"
+" * четыре отрезка или нормали (равенство углов A,B и C,D)\n"
+" * три отрезка или нормали (равенство углов A,B and B,C)\n"
+" * две окружности / дуги (равенство радиусов)\n"
+" * отрезок и дугу (равенство длины отрезка и длины дуги)\n"
+
+#: constraint.cpp:325
+msgid ""
+"Bad selection for length ratio constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+"Неправильное выделение для ограничения 'отношение длин'.\n"
+"Ограничение может принимать в качестве выделения следующие примитивы:\n"
+"\n"
+" * два отрезка\n"
+
+#: constraint.cpp:342
+msgid ""
+"Bad selection for length difference constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+"Неправильное выделение для ограничения 'разница длин'.\n"
+"Ограничение может принимать в качестве выделения следующие примитивы:\n"
+"\n"
+" * два отрезка\n"
+
+#: constraint.cpp:368
+msgid ""
+"Bad selection for at midpoint constraint. This constraint can apply to:\n"
+"\n"
+" * a line segment and a point (point at midpoint)\n"
+" * a line segment and a workplane (line's midpoint on plane)\n"
+msgstr ""
+"Неправильное выделение для ограничения 'на середине'.\n"
+"Ограничение может принимать в качестве выделения следующие примитивы:\n"
+"\n"
+" * точку и отрезок (точка на середине отрезка)\n"
+" * отрезок и рабочую плоскость (середина отрезка на плоскости)\n"
+
+#: constraint.cpp:426
+msgid ""
+"Bad selection for symmetric constraint. This constraint can apply to:\n"
+"\n"
+" * two points or a line segment (symmetric about workplane's coordinate "
+"axis)\n"
+" * line segment, and two points or a line segment (symmetric about line "
+"segment)\n"
+" * workplane, and two points or a line segment (symmetric about "
+"workplane)\n"
+msgstr ""
+"Неправильное выделение для ограничения 'симметрия'.\n"
+"Ограничение может принимать в качестве выделения следующие примитивы:\n"
+"\n"
+" * две точки / отрезок (симметричность точек по горизонтали/вертикали в "
+"зависимости от вида)\n"
+" * отрезок, две точки / отрезок (симметричность точек по оси отрезка)\n"
+" * рабочую плоскость и две точки / отрезок (симметричность относительно "
+"рабочей плоскости\n"
+
+#: constraint.cpp:440
+msgid ""
+"A workplane must be active when constraining symmetric without an explicit "
+"symmetry plane."
+msgstr ""
+"Рабочая плоскость должна быть активна для того, чтобы создать\n"
+"ограничение симметричности без явного указания плоскости симметрии."
+
+#: constraint.cpp:470
+msgid ""
+"Activate a workplane (with Sketch -> In Workplane) before applying a "
+"horizontal or vertical constraint."
+msgstr ""
+"Рабочая плоскость должна быть активирована (Эскиз -> В рабочей плоскости)\n"
+"перед тем, как накладывать ограничения горизонтальности / вертикальности."
+
+#: constraint.cpp:483
+msgid ""
+"Bad selection for horizontal / vertical constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points\n"
+" * a line segment\n"
+msgstr ""
+"Неправильное выделение для ограничения 'горизонтальность / вертикальность'.\n"
+"Ограничение может принимать в качестве выделения следующие примитивы:\n"
+"\n"
+" * две точки\n"
+" * отрезок\n"
+
+#: constraint.cpp:504
+msgid ""
+"Bad selection for same orientation constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two normals\n"
+msgstr ""
+"Неправильное выделение для ограничения \"идентичная ориентация\".\n"
+"Ограничение может принимать в качестве выделения следующие примитивы:\n"
+"\n"
+" * два координатных базиса('нормали')\n"
+
+#: constraint.cpp:554
+msgid "Must select an angle constraint."
+msgstr ""
+"Переключатся между смежными углами можно только выбрав ограничение угла."
+
+#: constraint.cpp:567
+msgid "Must select a constraint with associated label."
+msgstr ""
+"Переключать режим 'размера для справок' возможно только для ограничений, "
+"имеющих размерное значение."
+
+#: constraint.cpp:578
+msgid ""
+"Bad selection for angle constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+"Неправильное выделение для ограничения углового размера.\n"
+"Ограничение может принимать в качестве выделения следующие примитивы:\n"
+"\n"
+" * два отрезка\n"
+" * отрезок и координатный базис (нормаль)\n"
+" * два координатных базиса (нормали)\n"
+
+#: constraint.cpp:635
+msgid ""
+"The tangent arc and line segment must share an endpoint. Constrain them with "
+"Constrain -> On Point before constraining tangent."
+msgstr ""
+"Дуга и отрезок должны быть соединены. Соедините их крайние точки с помощью "
+"'Ограничения -> Точка на Примитиве' перед тем, как применять ограничение "
+"касательности."
+
+#: constraint.cpp:659
+msgid ""
+"The tangent cubic and line segment must share an endpoint. Constrain them "
+"with Constrain -> On Point before constraining tangent."
+msgstr ""
+"Сплайн и отрезок должны быть соединены. Соедините их крайние точки с помощью "
+"'Ограничения -> Точка на Примитиве' перед тем, как применять ограничение "
+"касательности."
+
+#: constraint.cpp:669
+msgid "Curve-curve tangency must apply in workplane."
+msgstr ""
+"Ограничение касательности может быть наложено только в рабочей плоскости."
+
+#: constraint.cpp:687
+msgid ""
+"The curves must share an endpoint. Constrain them with Constrain -> On Point "
+"before constraining tangent."
+msgstr ""
+"Кривые должны быть соединены. Соедините их крайние точки с помощью "
+"'Ограничения -> Точка на Примитиве' перед тем, как применять ограничение "
+"касательности."
+
+#: constraint.cpp:696
+msgid ""
+"Bad selection for parallel / tangent constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments (parallel)\n"
+" * a line segment and a normal (parallel)\n"
+" * two normals (parallel)\n"
+" * two line segments, arcs, or beziers, that share an endpoint (tangent)\n"
+msgstr ""
+"Неправильное выделение для ограничения параллельности / касательности.\n"
+"Ограничение может принимать в качестве выделения следующие примитивы:\n"
+"\n"
+" * два отрезка (параллельность)\n"
+" * отрезок и координатный базис (нормаль) (параллельность)\n"
+" * два координатных базиса (нормали) (параллельность)\n"
+" * два отрезка, две дуги или два сплайна, соединенных крайними точками "
+"(касательность)\n"
+
+#: constraint.cpp:714
+msgid ""
+"Bad selection for perpendicular constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+"Неправильное выделение для ограничения перпендикулярности.\n"
+"Ограничение может принимать в качестве выделения следующие примитивы:\n"
+"\n"
+" * два отрезка\n"
+" * отрезок и координатный базис (нормаль)\n"
+" * два координатных базиса (нормали)\n"
+
+#: constraint.cpp:729
+msgid ""
+"Bad selection for lock point where dragged constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * a point\n"
+msgstr ""
+"Неправильное выделение для ограничения 'Фиксация'.\n"
+"Ограничение может принимать в качестве выделения следующие примитивы:\n"
+"\n"
+" * точку\n"
+
+#: constraint.cpp:740
+msgid "click center of comment text"
+msgstr "кликните мышью там, где будет расположен текстовый комментарий"
+
+#: export.cpp:19
+msgid ""
+"No solid model present; draw one with extrudes and revolves, or use Export "
+"2d View to export bare lines and curves."
+msgstr ""
+
+#: export.cpp:61
+msgid ""
+"Bad selection for export section. Please select:\n"
+"\n"
+" * nothing, with an active workplane (workplane is section plane)\n"
+" * a face (section plane through face)\n"
+" * a point and two line segments (plane through point and parallel to "
+"lines)\n"
+msgstr ""
+
+#: export.cpp:822
+msgid "Active group mesh is empty; nothing to export."
+msgstr ""
+
+#: exportvector.cpp:337
+msgid "freehand lines were replaced with continuous lines"
+msgstr "Стили линии 'от руки' были заменены сплошными линиями"
+
+#: exportvector.cpp:339
+msgid "zigzag lines were replaced with continuous lines"
+msgstr "Стили линии 'зиг-заг' были заменены сплошными линиями"
+
+#: exportvector.cpp:593
+msgid ""
+"Some aspects of the drawing have no DXF equivalent and were not exported:\n"
+msgstr ""
+"Некоторые элементы чертежа не имеют аналогов в DXF-представлении и не были "
+"экспортированы:\n"
+
+#: exportvector.cpp:839
+msgid ""
+"PDF page size exceeds 200 by 200 inches; many viewers may reject this file."
+msgstr ""
+"Размер страницы PDF превышает 200x200 дюймов; некоторые программы просмотра "
+"не смогут прочитать такой файл."
+
+#: file.cpp:44 group.cpp:91
+msgctxt "group-name"
+msgid "sketch-in-plane"
+msgstr "эскиз-в-плоскости"
+
+#: file.cpp:62
+msgctxt "group-name"
+msgid "#references"
+msgstr "система-координат"
+
+#: file.cpp:549
+msgid ""
+"Unrecognized data in file. This file may be corrupt, or from a newer version "
+"of the program."
+msgstr ""
+"Некоторые данные из этого файла не распознаны. Возможно, файл поврежден или "
+"создан в более новой версии программы"
+
+#: file.cpp:859
+msgctxt "title"
+msgid "Missing File"
+msgstr "Файл Отсутствует"
+
+#: file.cpp:860
+#, c-format
+msgctxt "dialog"
+msgid "The linked file “%s” is not present."
+msgstr ""
+
+#: file.cpp:862
+msgctxt "dialog"
+msgid ""
+"Do you want to locate it manually?\n"
+"\n"
+"If you decline, any geometry that depends on the missing file will be "
+"permanently removed."
+msgstr ""
+"Хотите найти их вручную?\n"
+"Если вы ответите \"Нет\", то вся геометрия, которая зависит от "
+"отсутствующего файла будет удалена."
+
+#: file.cpp:865
+msgctxt "button"
+msgid "&Yes"
+msgstr "Да"
+
+#: file.cpp:867
+msgctxt "button"
+msgid "&No"
+msgstr "Нет"
+
+#: file.cpp:869
+msgctxt "button"
+msgid "&Cancel"
+msgstr "Отменить"
+
+#: graphicswin.cpp:41
+msgid "&File"
+msgstr "&Файл"
+
+#: graphicswin.cpp:42
+msgid "&New"
+msgstr "&Новый"
+
+#: graphicswin.cpp:43
+msgid "&Open..."
+msgstr "&Открыть..."
+
+#: graphicswin.cpp:44
+msgid "Open &Recent"
+msgstr "Открыть Н&едавний"
+
+#: graphicswin.cpp:45
+msgid "&Save"
+msgstr "&Сохранить"
+
+#: graphicswin.cpp:46
+msgid "Save &As..."
+msgstr "Сохранить &Как..."
+
+#: graphicswin.cpp:48
+msgid "Export &Image..."
+msgstr "Экспортировать И&зображение..."
+
+#: graphicswin.cpp:49
+msgid "Export 2d &View..."
+msgstr "Экспортировать &2d Вид..."
+
+#: graphicswin.cpp:50
+msgid "Export 2d &Section..."
+msgstr "Экспортировать 2d Се&чение..."
+
+#: graphicswin.cpp:51
+msgid "Export 3d &Wireframe..."
+msgstr "Экспортировать &3d Каркас..."
+
+#: graphicswin.cpp:52
+msgid "Export Triangle &Mesh..."
+msgstr "Экспортировать &Полигональную сетку..."
+
+#: graphicswin.cpp:53
+msgid "Export &Surfaces..."
+msgstr "Экспортировать Повер&хности..."
+
+#: graphicswin.cpp:54
+msgid "Im&port..."
+msgstr "&Импортировать..."
+
+#: graphicswin.cpp:57
+msgid "E&xit"
+msgstr "&Выход"
+
+#: graphicswin.cpp:60
+msgid "&Edit"
+msgstr "&Правка"
+
+#: graphicswin.cpp:61
+msgid "&Undo"
+msgstr "&Отменить"
+
+#: graphicswin.cpp:62
+msgid "&Redo"
+msgstr "&Вернуть"
+
+#: graphicswin.cpp:63
+msgid "Re&generate All"
+msgstr "&Перегенерировать Все"
+
+#: graphicswin.cpp:65
+msgid "Snap Selection to &Grid"
+msgstr "Привязать Выделение к &Сетке"
+
+#: graphicswin.cpp:66
+msgid "Rotate Imported &90°"
+msgstr "Повернуть Импортированное на &90°"
+
+#: graphicswin.cpp:68
+msgid "Cu&t"
+msgstr "Вы&резать"
+
+#: graphicswin.cpp:69
+msgid "&Copy"
+msgstr "&Копировать"
+
+#: graphicswin.cpp:70
+msgid "&Paste"
+msgstr "В&ставить"
+
+#: graphicswin.cpp:71
+msgid "Paste &Transformed..."
+msgstr "Вставить с &Трансформацией..."
+
+#: graphicswin.cpp:72
+msgid "&Delete"
+msgstr "&Удалить"
+
+#: graphicswin.cpp:74
+msgid "Select &Edge Chain"
+msgstr "Вы&делить Последовательность Ребер"
+
+#: graphicswin.cpp:75
+msgid "Select &All"
+msgstr "В&ыделить Все"
+
+#: graphicswin.cpp:76
+msgid "&Unselect All"
+msgstr "С&бросить Выделение"
+
+#: graphicswin.cpp:78
+msgid "&Line Styles..."
+msgstr ""
+
+#: graphicswin.cpp:79
+msgid "&View Projection..."
+msgstr "&View Прое&кция..."
+
+#: graphicswin.cpp:81
+msgid "Con&figuration..."
+msgstr ""
+
+#: graphicswin.cpp:84
+msgid "&View"
+msgstr "&Вид"
+
+#: graphicswin.cpp:85
+msgid "Zoom &In"
+msgstr "&Приблизить"
+
+#: graphicswin.cpp:86
+msgid "Zoom &Out"
+msgstr "От&далить"
+
+#: graphicswin.cpp:87
+msgid "Zoom To &Fit"
+msgstr "Показать &Все / Выделенное"
+
+#: graphicswin.cpp:89
+msgid "Align View to &Workplane"
+msgstr "Выровнять Вид на &Рабочую Плоскость"
+
+#: graphicswin.cpp:90
+msgid "Nearest &Ortho View"
+msgstr "Ближайший &Ортогональный Вид"
+
+#: graphicswin.cpp:91
+msgid "Nearest &Isometric View"
+msgstr "Ближайший &Изометрический Вид"
+
+#: graphicswin.cpp:92
+msgid "&Center View At Point"
+msgstr "&Центрировать Вид на Точке"
+
+#: graphicswin.cpp:94
+msgid "Show Snap &Grid"
+msgstr "Показать &Сетку"
+
+#: graphicswin.cpp:95
+msgid "Use &Perspective Projection"
+msgstr "Перспективная Прое&кция"
+
+#: graphicswin.cpp:96
+msgid "Dimension &Units"
+msgstr ""
+
+#: graphicswin.cpp:97
+msgid "Dimensions in &Millimeters"
+msgstr "Размеры в Ми&ллиметрах"
+
+#: graphicswin.cpp:98
+msgid "Dimensions in M&eters"
+msgstr ""
+
+#: graphicswin.cpp:99
+msgid "Dimensions in &Inches"
+msgstr "Размеры в Дю&ймах"
+
+#: graphicswin.cpp:101
+msgid "Show &Toolbar"
+msgstr "Показывать Па&нель Инструментов"
+
+#: graphicswin.cpp:102
+msgid "Show Property Bro&wser"
+msgstr "Показывать Брау&зер"
+
+#: graphicswin.cpp:104
+msgid "&Full Screen"
+msgstr "Полно&экранный Режим"
+
+#: graphicswin.cpp:106
+msgid "&New Group"
+msgstr "&Группа"
+
+#: graphicswin.cpp:107
+msgid "Sketch In &3d"
+msgstr "Создать Эскиз в &3d"
+
+#: graphicswin.cpp:108
+msgid "Sketch In New &Workplane"
+msgstr "Создать Эскиз в Новой &Рабочей Плоскости"
+
+#: graphicswin.cpp:110
+msgid "Step &Translating"
+msgstr "&Линейный Массив"
+
+#: graphicswin.cpp:111
+msgid "Step &Rotating"
+msgstr "&Круговой Массив"
+
+#: graphicswin.cpp:113
+msgid "E&xtrude"
+msgstr "Тело &Выдавливания"
+
+#: graphicswin.cpp:114
+msgid "&Helix"
+msgstr ""
+
+#: graphicswin.cpp:115
+msgid "&Lathe"
+msgstr "Тело В&ращения"
+
+#: graphicswin.cpp:116
+msgid "Re&volve"
+msgstr ""
+
+#: graphicswin.cpp:118
+msgid "Link / Assemble..."
+msgstr "&Импорт Детали / Сборка..."
+
+#: graphicswin.cpp:119
+msgid "Link Recent"
+msgstr "Последние &Детали"
+
+#: graphicswin.cpp:121
+msgid "&Sketch"
+msgstr "&Эскиз"
+
+#: graphicswin.cpp:122
+msgid "In &Workplane"
+msgstr "В &Рабочей Плоскости"
+
+#: graphicswin.cpp:123
+msgid "Anywhere In &3d"
+msgstr "Режим &3d"
+
+#: graphicswin.cpp:125
+msgid "Datum &Point"
+msgstr "Опорная &Точка"
+
+#: graphicswin.cpp:126
+msgid "&Workplane"
+msgstr "Рабочая &Плоскость"
+
+#: graphicswin.cpp:128
+msgid "Line &Segment"
+msgstr "&Отрезок"
+
+#: graphicswin.cpp:129
+msgid "C&onstruction Line Segment"
+msgstr "&Вспомогательный Отрезок"
+
+#: graphicswin.cpp:130
+msgid "&Rectangle"
+msgstr "Прямоу&гольник"
+
+#: graphicswin.cpp:131
+msgid "&Circle"
+msgstr "О&кружность"
+
+#: graphicswin.cpp:132
+msgid "&Arc of a Circle"
+msgstr "Д&уга Окружности"
+
+#: graphicswin.cpp:133
+msgid "&Bezier Cubic Spline"
+msgstr "Кубический &Сплайн Безье"
+
+#: graphicswin.cpp:135
+msgid "&Text in TrueType Font"
+msgstr "Т&екст TrueType"
+
+#: graphicswin.cpp:136
+msgid "&Image"
+msgstr "И&зображение"
+
+#: graphicswin.cpp:138
+msgid "To&ggle Construction"
+msgstr "Переключить Режим Вс&помогательных Построений"
+
+#: graphicswin.cpp:139
+msgid "Tangent &Arc at Point"
+msgstr "Кас&ательная в Точке"
+
+#: graphicswin.cpp:140
+msgid "Split Curves at &Intersection"
+msgstr "Ра&збить Кривые Пересечением"
+
+#: graphicswin.cpp:142
+msgid "&Constrain"
+msgstr "&Ограничения"
+
+#: graphicswin.cpp:143
+msgid "&Distance / Diameter"
+msgstr "&Расстояние / Диаметр"
+
+#: graphicswin.cpp:144
+msgid "Re&ference Dimension"
+msgstr "&Справочный Размер"
+
+#: graphicswin.cpp:145
+msgid "A&ngle"
+msgstr "&Угол"
+
+#: graphicswin.cpp:146
+msgid "Reference An&gle"
+msgstr "С&правочный Угол"
+
+#: graphicswin.cpp:147
+msgid "Other S&upplementary Angle"
+msgstr "Переключить Сме&жный Угол"
+
+#: graphicswin.cpp:148
+msgid "Toggle R&eference Dim"
+msgstr "Переключить Режим Размера Для Спра&вок"
+
+#: graphicswin.cpp:150
+msgid "&Horizontal"
+msgstr "&Горизонтальность"
+
+#: graphicswin.cpp:151
+msgid "&Vertical"
+msgstr "&Вертикальность"
+
+#: graphicswin.cpp:153
+msgid "&On Point / Curve / Plane"
+msgstr "&Точка на Примитиве"
+
+#: graphicswin.cpp:154
+msgid "E&qual Length / Radius / Angle"
+msgstr "&Равенство Длин / Радиусов / Углов"
+
+#: graphicswin.cpp:155
+msgid "Length Ra&tio"
+msgstr "Отно&шение Длин"
+
+#: graphicswin.cpp:156
+msgid "Length Diff&erence"
+msgstr "Ра&зница Длин"
+
+#: graphicswin.cpp:157
+msgid "At &Midpoint"
+msgstr "&На Середине"
+
+#: graphicswin.cpp:158
+msgid "S&ymmetric"
+msgstr "С&имметричность"
+
+#: graphicswin.cpp:159
+msgid "Para&llel / Tangent"
+msgstr "Пара&ллельность / Касательность"
+
+#: graphicswin.cpp:160
+msgid "&Perpendicular"
+msgstr "Перпендикул&ярность"
+
+#: graphicswin.cpp:161
+msgid "Same Orient&ation"
+msgstr "Идентичная &Ориентация"
+
+#: graphicswin.cpp:162
+msgid "Lock Point Where &Dragged"
+msgstr "За&фиксировать"
+
+#: graphicswin.cpp:164
+msgid "Comment"
+msgstr "Текстовый &Комментарий"
+
+#: graphicswin.cpp:166
+msgid "&Analyze"
+msgstr "&Анализ"
+
+#: graphicswin.cpp:167
+msgid "Measure &Volume"
+msgstr "Измерить &Объем"
+
+#: graphicswin.cpp:168
+msgid "Measure A&rea"
+msgstr "Измерить П&лощадь"
+
+#: graphicswin.cpp:169
+msgid "Measure &Perimeter"
+msgstr "Измерить П&ериметр"
+
+#: graphicswin.cpp:170
+msgid "Show &Interfering Parts"
+msgstr "Показать Пе&ресекающиеся Детали"
+
+#: graphicswin.cpp:171
+msgid "Show &Naked Edges"
+msgstr "Показать Про&блемные Ребра"
+
+#: graphicswin.cpp:172
+msgid "Show &Center of Mass"
+msgstr ""
+
+#: graphicswin.cpp:174
+msgid "Show &Underconstrained Points"
+msgstr ""
+
+#: graphicswin.cpp:176
+msgid "&Trace Point"
+msgstr "Включить &Трассировку Точки"
+
+#: graphicswin.cpp:177
+msgid "&Stop Tracing..."
+msgstr "Остановить Тра&ссировку..."
+
+#: graphicswin.cpp:178
+msgid "Step &Dimension..."
+msgstr "Плавное Из&менение Размера..."
+
+#: graphicswin.cpp:180
+msgid "&Help"
+msgstr "&Помощь"
+
+#: graphicswin.cpp:181
+msgid "&Language"
+msgstr "&Язык"
+
+#: graphicswin.cpp:182
+msgid "&Website / Manual"
+msgstr "Вебсайт / &Справка"
+
+#: graphicswin.cpp:184
+msgid "&About"
+msgstr "О &Программе"
+
+#: graphicswin.cpp:352
+msgid "(no recent files)"
+msgstr "(пусто)"
+
+#: graphicswin.cpp:360
+#, c-format
+msgid "File '%s' does not exist."
+msgstr ""
+
+#: graphicswin.cpp:721
+msgid "No workplane is active, so the grid will not appear."
+msgstr "Сетку не будет видно, пока рабочая плоскость не активирована."
+
+#: graphicswin.cpp:730
+msgid ""
+"The perspective factor is set to zero, so the view will always be a parallel "
+"projection.\n"
+"\n"
+"For a perspective projection, modify the perspective factor in the "
+"configuration screen. A value around 0.3 is typical."
+msgstr ""
+"Коэффициент перспективы установлен в ноль, что соответствует ортогональной "
+"проекции.\n"
+"Чтобы получить перспективную проекцию, отредактируйте значение коэффициента "
+"перспективы на конфигурационной странице браузера.\n"
+"Значение по умолчанию 0.3."
+
+#: graphicswin.cpp:809
+msgid ""
+"Select a point; this point will become the center of the view on screen."
+msgstr "Выделите точку. Вид будет отцентрован по этой точке."
+
+#: graphicswin.cpp:1103
+msgid "No additional entities share endpoints with the selected entities."
+msgstr "Нет дополнительных объектов, соединенных с выбранными примитивами."
+
+#: graphicswin.cpp:1121
+msgid ""
+"To use this command, select a point or other entity from an linked part, or "
+"make a link group the active group."
+msgstr ""
+"Чтобы использовать эту команду, выделите точку или другой примитив, "
+"принадлежащий импортированной детали или активируйте группу импортированной "
+"детали."
+
+#: graphicswin.cpp:1144
+msgid ""
+"No workplane is active. Activate a workplane (with Sketch -> In Workplane) "
+"to define the plane for the snap grid."
+msgstr ""
+"Рабочая плоскость не активна. Активируйте ее через Эскиз -> В Рабочей "
+"Плоскости чтобы определить плоскость для сетки."
+
+#: graphicswin.cpp:1151
+msgid ""
+"Can't snap these items to grid; select points, text comments, or constraints "
+"with a label. To snap a line, select its endpoints."
+msgstr ""
+"Невозможно привязать выбранные примитивы к сетке. Необходимо выбирать точки, "
+"текстовые комментарии или ограничения с размерными значениями. Чтобы "
+"привязать отрезок или другой примитив, выбирайте его точки."
+
+#: graphicswin.cpp:1239
+msgid "No workplane selected. Activating default workplane for this group."
+msgstr ""
+"Рабочая плоскость не активна. Активирована рабочая плоскость по умолчанию "
+"для данной группы."
+
+#: graphicswin.cpp:1242
+msgid ""
+"No workplane is selected, and the active group does not have a default "
+"workplane. Try selecting a workplane, or activating a sketch-in-new-"
+"workplane group."
+msgstr ""
+"Рабочая плоскость не выбрана и активная группа не содержит рабочей плоскости "
+"по умолчанию. Попробуйте выделить рабочую плоскость или создать новую с "
+"помощью Группа -> Создать Эскиз в Новой Рабочей Плоскости."
+
+#: graphicswin.cpp:1263
+msgid ""
+"Bad selection for tangent arc at point. Select a single point, or select "
+"nothing to set up arc parameters."
+msgstr ""
+"Неправильное выделение для создания скругления в точке. Выделите либо одну "
+"точку, либо запустите команду без выделения, чтобы перейти к окну настроек "
+"этой команды."
+
+#: graphicswin.cpp:1274
+msgid "click point on arc (draws anti-clockwise)"
+msgstr ""
+"кликните мышью там, где хотите создать дугу окружности (дуга будет "
+"нарисована против часовой стрелки)"
+
+#: graphicswin.cpp:1275
+msgid "click to place datum point"
+msgstr "кликните мышью там, где хотите создать опорную точку"
+
+#: graphicswin.cpp:1276
+msgid "click first point of line segment"
+msgstr "кликните мышью там, где хотите создать первую точку отрезка"
+
+#: graphicswin.cpp:1278
+msgid "click first point of construction line segment"
+msgstr ""
+"кликните мышью там, где хотите создать первую точку вспомогательного отрезка"
+
+#: graphicswin.cpp:1279
+msgid "click first point of cubic segment"
+msgstr ""
+"кликните мышью там, где хотите создать первую точку кубического сплайна Безье"
+
+#: graphicswin.cpp:1280
+msgid "click center of circle"
+msgstr "кликните мышью там, где будет находиться центр окружности"
+
+#: graphicswin.cpp:1281
+msgid "click origin of workplane"
+msgstr ""
+"кликните мышью там, где будет находиться точка, через которую будет "
+"построена рабочая плоскость"
+
+#: graphicswin.cpp:1282
+msgid "click one corner of rectangle"
+msgstr "кликните мышью там, где будет находиться один из углов прямоугольника"
+
+#: graphicswin.cpp:1283
+msgid "click top left of text"
+msgstr "кликните мышью там, где хотите создать текст"
+
+#: graphicswin.cpp:1289
+msgid "click top left of image"
+msgstr ""
+
+#: graphicswin.cpp:1301
+msgid ""
+"No entities are selected. Select entities before trying to toggle their "
+"construction state."
+msgstr ""
+"Не выбран ни один примитив. Выберите примитивы перед тем, как переключать их "
+"режим дополнительных построений."
+
+#: group.cpp:86
+msgctxt "group-name"
+msgid "sketch-in-3d"
+msgstr "эскиз-в-3d"
+
+#: group.cpp:142
+msgid ""
+"Bad selection for new sketch in workplane. This group can be created with:\n"
+"\n"
+" * a point (through the point, orthogonal to coordinate axes)\n"
+" * a point and two line segments (through the point, parallel to the "
+"lines)\n"
+" * a workplane (copy of the workplane)\n"
+msgstr ""
+"Неправильное выделение для создания эскиза.\n"
+"Группа может быть создана, используя в качестве выделения следующие "
+"примитивы:\n"
+"\n"
+" * точку (рабочая плоскость, ориентированная к ближайшему виду, "
+"проходящая через точку)\n"
+" * точку и два отрезка (рабочая плоскость, проходящая через точку и "
+"параллельная отрезкам)\n"
+" * рабочую плоскость (копия рабочей плоскости)\n"
+
+#: group.cpp:154
+msgid ""
+"Activate a workplane (Sketch -> In Workplane) before extruding. The sketch "
+"will be extruded normal to the workplane."
+msgstr ""
+"Выберите рабочую плоскость (Эскиз -> В Рабочей Плоскости) перед созданием "
+"группы выдавливания. Эскиз будет выдавлен по нормали к рабочей плоскости."
+
+#: group.cpp:163
+msgctxt "group-name"
+msgid "extrude"
+msgstr "тело-выдавливания"
+
+#: group.cpp:168
+msgid "Lathe operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:179
+msgid ""
+"Bad selection for new lathe group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+"Неправильное выделение для создания группы тела вращения. \n"
+"Группа может быть создана, используя в качестве выделения следующие "
+"примитивы:\n"
+"\n"
+" * точку и отрезок / координатных базис (нормаль) (тело вращения вокруг "
+"оси, проходящей через точку и параллельной отрезку / нормали)\n"
+" * отрезок (тело вращения вокруг оси, проходящей через отрезок)\n"
+"\n"
+
+#: group.cpp:189
+msgctxt "group-name"
+msgid "lathe"
+msgstr "тело-вращения"
+
+#: group.cpp:194
+msgid "Revolve operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:205
+msgid ""
+"Bad selection for new revolve group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:217
+msgctxt "group-name"
+msgid "revolve"
+msgstr ""
+
+#: group.cpp:222
+msgid "Helix operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:233
+msgid ""
+"Bad selection for new helix group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:245
+msgctxt "group-name"
+msgid "helix"
+msgstr ""
+
+#: group.cpp:258
+msgid ""
+"Bad selection for new rotation. This group can be created with:\n"
+"\n"
+" * a point, while locked in workplane (rotate in plane, about that "
+"point)\n"
+" * a point and a line or a normal (rotate about an axis through the "
+"point, and parallel to line / normal)\n"
+msgstr ""
+"Неправильное выделение для создания группы кругового массива. \n"
+"Группа может быть создана, используя в качестве выделения следующие "
+"примитивы:\n"
+"\n"
+" * точку при активной рабочей плоскости (вращение в плоскости вокруг "
+"выбранной точки)\n"
+" * точку и отрезок / координатных базис (нормаль) (вращение вокруг оси, "
+"проходящей через точку и параллельной отрезку / нормали)\n"
+"\n"
+
+#: group.cpp:271
+msgctxt "group-name"
+msgid "rotate"
+msgstr "круговой-массив"
+
+#: group.cpp:282
+msgctxt "group-name"
+msgid "translate"
+msgstr "линейный-массив"
+
+#: group.cpp:400
+msgid "(unnamed)"
+msgstr "(без имени)"
+
+#: groupmesh.cpp:708
+msgid "not closed contour, or not all same style!"
+msgstr "незамкнутый контур или несовпадение стилей!"
+
+#: groupmesh.cpp:721
+msgid "points not all coplanar!"
+msgstr "не все точки лежат в одной плоскости!"
+
+#: groupmesh.cpp:723
+msgid "contour is self-intersecting!"
+msgstr "контур имеет самопересечения!"
+
+#: groupmesh.cpp:725
+msgid "zero-length edge!"
+msgstr "вырожденный отрезок!"
+
+#: modify.cpp:254
+msgid "Must be sketching in workplane to create tangent arc."
+msgstr ""
+"Скругления эскиза можно создавать только когда рабочая плоскость "
+"активирована."
+
+#: modify.cpp:301
+msgid ""
+"To create a tangent arc, select a point where two non-construction lines or "
+"circles in this group and workplane join."
+msgstr ""
+"Чтобы создать скругление эскиза, выберите точку, где соединяются два "
+"примитива, не принадлежащих к вспомогательной геометрии."
+
+#: modify.cpp:388
+msgid ""
+"Couldn't round this corner. Try a smaller radius, or try creating the "
+"desired geometry by hand with tangency constraints."
+msgstr ""
+"Невозможно скруглить угол. Попробуйте радиус поменьше или создайте требуемую "
+"геометрию с помощью Ограничения -> Параллельность / Касательность."
+
+#: modify.cpp:597
+msgid "Couldn't split this entity; lines, circles, or cubics only."
+msgstr ""
+"Невозможно разделить такие примитивы. Выберите линии, окружности или "
+"кубические сплайны."
+
+#: modify.cpp:624
+msgid "Must be sketching in workplane to split."
+msgstr ""
+"Пересечение примитивов работает только когда рабочая плоскость активна."
+
+#: modify.cpp:631
+msgid ""
+"Select two entities that intersect each other (e.g. two lines/circles/arcs "
+"or a line/circle/arc and a point)."
+msgstr ""
+
+#: modify.cpp:736
+msgid "Can't split; no intersection found."
+msgstr "Невозможно разделить пересекаемые примитивы: пересечений нет."
+
+#: mouse.cpp:560
+msgid "Assign to Style"
+msgstr "Применить Стиль"
+
+#: mouse.cpp:576
+msgid "No Style"
+msgstr "Стиль по Умолчанию"
+
+#: mouse.cpp:579
+msgid "Newly Created Custom Style..."
+msgstr "Создать Новый Стиль..."
+
+#: mouse.cpp:586
+msgid "Group Info"
+msgstr "Настройки Группы"
+
+#: mouse.cpp:606
+msgid "Style Info"
+msgstr "Настройки Стиля"
+
+#: mouse.cpp:626
+msgid "Select Edge Chain"
+msgstr "Выделить Последовательность Примитивов"
+
+#: mouse.cpp:632
+msgid "Toggle Reference Dimension"
+msgstr "Переключить Режим Размера Для Справок"
+
+#: mouse.cpp:638
+msgid "Other Supplementary Angle"
+msgstr "Переключить Смежный Угол"
+
+#: mouse.cpp:643
+msgid "Snap to Grid"
+msgstr "Привязать к Сетке"
+
+#: mouse.cpp:652
+msgid "Remove Spline Point"
+msgstr "Удалить Точку Сплайна"
+
+#: mouse.cpp:687
+msgid "Add Spline Point"
+msgstr "Добавить Точку Сплайна"
+
+#: mouse.cpp:691
+msgid "Cannot add spline point: maximum number of points reached."
+msgstr ""
+"Невозможно добавить точку сплайна: достигнуто ограничение максимального "
+"количества точек для сплайна."
+
+#: mouse.cpp:716
+msgid "Toggle Construction"
+msgstr "Переключить Режим 'Дополнительные Построения'."
+
+#: mouse.cpp:731
+msgid "Delete Point-Coincident Constraint"
+msgstr "Удалить Ограничение Совпадения Точек"
+
+#: mouse.cpp:750
+msgid "Cut"
+msgstr "Вырезать"
+
+#: mouse.cpp:752
+msgid "Copy"
+msgstr "Копировать"
+
+#: mouse.cpp:756
+msgid "Select All"
+msgstr "Выделить Все"
+
+#: mouse.cpp:761
+msgid "Paste"
+msgstr "Вставить"
+
+#: mouse.cpp:763
+msgid "Paste Transformed..."
+msgstr "Вставить с Трансформацией..."
+
+#: mouse.cpp:768
+msgid "Delete"
+msgstr "Удалить"
+
+#: mouse.cpp:771
+msgid "Unselect All"
+msgstr "Сбросить Выделение"
+
+#: mouse.cpp:778
+msgid "Unselect Hovered"
+msgstr "Снять Выделение с Выбранного"
+
+#: mouse.cpp:787
+msgid "Zoom to Fit"
+msgstr "Показать Все"
+
+#: mouse.cpp:990 mouse.cpp:1277
+msgid "click next point of line, or press Esc"
+msgstr "кликните мышью там, где хотите расположить следующую точку"
+
+#: mouse.cpp:996
+msgid ""
+"Can't draw rectangle in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+"Невозможно начертить прямоугольник, когда рабочая плоскость не активна."
+
+#: mouse.cpp:1030
+msgid "click to place other corner of rectangle"
+msgstr "кликните мышью там, где хотите расположить другой угол прямоугольника"
+
+#: mouse.cpp:1050
+msgid "click to set radius"
+msgstr "кликните, чтобы задать радиус"
+
+#: mouse.cpp:1055
+msgid ""
+"Can't draw arc in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr "Невозможно создать дугу, когда нет активной рабочей плоскости."
+
+#: mouse.cpp:1074
+msgid "click to place point"
+msgstr "кликните мышью там, где хотите создать точку"
+
+#: mouse.cpp:1090
+msgid "click next point of cubic, or press Esc"
+msgstr ""
+"кликните мышью там, где хотите создать следующую точку сплайна или нажмите "
+"Esc для завершения операции."
+
+#: mouse.cpp:1095
+msgid ""
+"Sketching in a workplane already; sketch in 3d before creating new workplane."
+msgstr ""
+"Рабочая плоскость уже активна. Перейдите в режим 3d перед созданием новой "
+"рабочей плоскости."
+
+#: mouse.cpp:1111
+msgid ""
+"Can't draw text in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr "Невозможно создать текст, когда нет активной рабочей плоскости."
+
+#: mouse.cpp:1128
+msgid "click to place bottom right of text"
+msgstr ""
+
+#: mouse.cpp:1134
+msgid ""
+"Can't draw image in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+
+#: mouse.cpp:1161
+msgid "NEW COMMENT -- DOUBLE-CLICK TO EDIT"
+msgstr "КОММЕНТАРИЙ -- ДВОЙНОЙ ЩЕЛЧОК ДЛЯ РЕДАКТИРОВАНИЯ"
+
+#: platform/gui.cpp:85 platform/gui.cpp:89
+msgctxt "file-type"
+msgid "SolveSpace models"
+msgstr "проекты SolveSpace"
+
+#: platform/gui.cpp:90
+msgctxt "file-type"
+msgid "IDF circuit board"
+msgstr ""
+
+#: platform/gui.cpp:94
+msgctxt "file-type"
+msgid "PNG image"
+msgstr "PNG изображение"
+
+#: platform/gui.cpp:98
+msgctxt "file-type"
+msgid "STL mesh"
+msgstr "STL полигональная сетка"
+
+#: platform/gui.cpp:99
+msgctxt "file-type"
+msgid "Wavefront OBJ mesh"
+msgstr "Wavefront OBJ полигональная сетка"
+
+#: platform/gui.cpp:100
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, with viewer"
+msgstr "Three.js-совместимая полигональная сетка с просмторщиком"
+
+#: platform/gui.cpp:101
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, mesh only"
+msgstr "Three.js-совместимая полигональная сетка"
+
+#: platform/gui.cpp:102
+msgctxt "file-type"
+msgid "Q3D Object file"
+msgstr ""
+
+#: platform/gui.cpp:103
+msgctxt "file-type"
+msgid "VRML text file"
+msgstr ""
+
+#: platform/gui.cpp:107 platform/gui.cpp:114 platform/gui.cpp:121
+msgctxt "file-type"
+msgid "STEP file"
+msgstr "STEP файл"
+
+#: platform/gui.cpp:111
+msgctxt "file-type"
+msgid "PDF file"
+msgstr "PDF документ"
+
+#: platform/gui.cpp:112
+msgctxt "file-type"
+msgid "Encapsulated PostScript"
+msgstr "Encapsulated PostScript"
+
+#: platform/gui.cpp:113
+msgctxt "file-type"
+msgid "Scalable Vector Graphics"
+msgstr "SVG изображение"
+
+#: platform/gui.cpp:115 platform/gui.cpp:122
+msgctxt "file-type"
+msgid "DXF file (AutoCAD 2007)"
+msgstr "DXF файл (AutoCAD 2007)"
+
+#: platform/gui.cpp:116
+msgctxt "file-type"
+msgid "HPGL file"
+msgstr "HPGL файл"
+
+#: platform/gui.cpp:117
+msgctxt "file-type"
+msgid "G Code"
+msgstr "G Code"
+
+#: platform/gui.cpp:126
+msgctxt "file-type"
+msgid "AutoCAD DXF and DWG files"
+msgstr "AutoCAD DXF и DWG файлы"
+
+#: platform/gui.cpp:130
+msgctxt "file-type"
+msgid "Comma-separated values"
+msgstr "CSV файлы (значения, разделенные запятой)"
+
+#: platform/guigtk.cpp:1317 platform/guimac.mm:1360 platform/guiwin.cpp:1608
+msgid "untitled"
+msgstr "без имени"
+
+#: platform/guigtk.cpp:1328 platform/guigtk.cpp:1361 platform/guimac.mm:1318
+#: platform/guiwin.cpp:1555
+msgctxt "title"
+msgid "Save File"
+msgstr "Сохранить Файл"
+
+#: platform/guigtk.cpp:1329 platform/guigtk.cpp:1362 platform/guimac.mm:1301
+#: platform/guiwin.cpp:1557
+msgctxt "title"
+msgid "Open File"
+msgstr "Открыть Файл"
+
+#: platform/guigtk.cpp:1332 platform/guigtk.cpp:1368
+msgctxt "button"
+msgid "_Cancel"
+msgstr "Отменить"
+
+#: platform/guigtk.cpp:1333 platform/guigtk.cpp:1366
+msgctxt "button"
+msgid "_Save"
+msgstr "Сохранить"
+
+#: platform/guigtk.cpp:1334 platform/guigtk.cpp:1367
+msgctxt "button"
+msgid "_Open"
+msgstr ""
+
+#: style.cpp:166
+msgid ""
+"Can't assign style to an entity that's derived from another entity; try "
+"assigning a style to this entity's parent."
+msgstr ""
+"Невозможно применить стиль к примитиву, который произошел от другого "
+"примитива. Попробуйте применить стиль к исходному примитиву."
+
+#: style.cpp:665
+msgid "Style name cannot be empty"
+msgstr "Имя стиля не может быть пустым."
+
+#: textscreens.cpp:741
+msgid "Can't repeat fewer than 1 time."
+msgstr "Невозможно сделать повторение меньше, чем 1 раз."
+
+#: textscreens.cpp:745
+msgid "Can't repeat more than 999 times."
+msgstr "Невозможно сделать повтор больше, чем 999 раз."
+
+#: textscreens.cpp:770
+msgid "Group name cannot be empty"
+msgstr "Имя группы не может быть пустым."
+
+#: textscreens.cpp:813
+msgid "Opacity must be between zero and one."
+msgstr "Прозрачность должна быть числом от нуля до единицы."
+
+#: textscreens.cpp:848
+msgid "Radius cannot be zero or negative."
+msgstr "Радиус не может быть нулевым или отрицательным."
+
+#: toolbar.cpp:18
+msgid "Sketch line segment"
+msgstr "Начертить отрезок прямой"
+
+#: toolbar.cpp:20
+msgid "Sketch rectangle"
+msgstr "Начертить прямоугольник"
+
+#: toolbar.cpp:22
+msgid "Sketch circle"
+msgstr "Начертить окружность"
+
+#: toolbar.cpp:24
+msgid "Sketch arc of a circle"
+msgstr "Начертить дугу окружности"
+
+#: toolbar.cpp:26
+msgid "Sketch curves from text in a TrueType font"
+msgstr "Начертить текст TrueType"
+
+#: toolbar.cpp:28
+msgid "Sketch image from a file"
+msgstr ""
+
+#: toolbar.cpp:30
+msgid "Create tangent arc at selected point"
+msgstr "Создать скругление в выбранной точке"
+
+#: toolbar.cpp:32
+msgid "Sketch cubic Bezier spline"
+msgstr "Начертить кубический сплайн Безье"
+
+#: toolbar.cpp:34
+msgid "Sketch datum point"
+msgstr "Начертить опорную точку"
+
+#: toolbar.cpp:36
+msgid "Toggle construction"
+msgstr "Переключить вспомогательную геометрию"
+
+#: toolbar.cpp:38
+msgid "Split lines / curves where they intersect"
+msgstr "Разбить кривые по пересечению"
+
+#: toolbar.cpp:42
+msgid "Constrain distance / diameter / length"
+msgstr "Ограничение расстояния / диаметра / длины"
+
+#: toolbar.cpp:44
+msgid "Constrain angle"
+msgstr "Ограничение угла"
+
+#: toolbar.cpp:46
+msgid "Constrain to be horizontal"
+msgstr "Ограничение горизонтальности"
+
+#: toolbar.cpp:48
+msgid "Constrain to be vertical"
+msgstr "Ограничение вертикальности"
+
+#: toolbar.cpp:50
+msgid "Constrain to be parallel or tangent"
+msgstr "Ограничение параллельности / касательности"
+
+#: toolbar.cpp:52
+msgid "Constrain to be perpendicular"
+msgstr "Ограничение перпендикулярности"
+
+#: toolbar.cpp:54
+msgid "Constrain point on line / curve / plane / point"
+msgstr "Ограничение точка на точке / линии / окружности / дуге / плоскости"
+
+#: toolbar.cpp:56
+msgid "Constrain symmetric"
+msgstr "Симметричность"
+
+#: toolbar.cpp:58
+msgid "Constrain equal length / radius / angle"
+msgstr "Ограничение равенства длин / радиусов / углов"
+
+#: toolbar.cpp:60
+msgid "Constrain normals in same orientation"
+msgstr "Ограничение идентичности ориентации координатных базисов"
+
+#: toolbar.cpp:62
+msgid "Other supplementary angle"
+msgstr "Переключить смежный угол"
+
+#: toolbar.cpp:64
+msgid "Toggle reference dimension"
+msgstr "Переключить режим размера для справок"
+
+#: toolbar.cpp:68
+msgid "New group extruding active sketch"
+msgstr "Создать группу выдавливания текущего эскиза"
+
+#: toolbar.cpp:70
+msgid "New group rotating active sketch"
+msgstr "Создать группу вращения текущего эскиза"
+
+#: toolbar.cpp:72
+msgid "New group step and repeat rotating"
+msgstr "Создать группу кругового массива"
+
+#: toolbar.cpp:74
+msgid "New group step and repeat translating"
+msgstr "Создать группу линейного массива"
+
+#: toolbar.cpp:76
+msgid "New group in new workplane (thru given entities)"
+msgstr "Создать группу в новой рабочей плоскости (через выбранные примитивы)"
+
+#: toolbar.cpp:78
+msgid "New group in 3d"
+msgstr "Новая группа в 3d"
+
+#: toolbar.cpp:80
+msgid "New group linking / assembling file"
+msgstr "Новая группа импорта детали / сборки"
+
+#: toolbar.cpp:84
+msgid "Nearest isometric view"
+msgstr "Ближайший изометрический вид"
+
+#: toolbar.cpp:86
+msgid "Align view to active workplane"
+msgstr "Выровнять вид на рабочую плоскость"
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Error"
+msgstr "Ошибка"
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Message"
+msgstr "Сообщение"
+
+#: util.cpp:170
+msgctxt "button"
+msgid "&OK"
+msgstr "ХОРОШО"
+
+#: view.cpp:78
+msgid "Scale cannot be zero or negative."
+msgstr "Масштабный коэффициент не может быть нулевым или отрицательным."
+
+#: view.cpp:90 view.cpp:99
+msgid "Bad format: specify x, y, z"
+msgstr "Неверный формат: введите данные как x, y, z"
+
+#~ msgctxt "title"
+#~ msgid "(new sketch)"
+#~ msgstr "(новый проект)"
+
+#~ msgctxt "title"
+#~ msgid "Property Browser"
+#~ msgstr "Браузер"
+
+#~ msgid "Specify between 0 and 8 digits after the decimal."
+#~ msgstr "Введите число от 0 до 8."
+
+#~ msgid "Show Degrees of &Freedom"
+#~ msgstr "Показать Степени С&вободы"
+
+#~ msgid "click to place bottom left of text"
+#~ msgstr "кликните мышью, чтобы расположить текст"
+
+#~ msgid "Do you want to save the changes you made to the new sketch?"
+#~ msgstr "Хотите сохранить ваши изменения?"
+
+#~ msgid "Your changes will be lost if you don't save them."
+#~ msgstr "Ваши изменения будут утеряны, если вы их не сохраните."
+
+#~ msgctxt "button"
+#~ msgid "Save"
+#~ msgstr "Сохранить"
+
+#~ msgctxt "button"
+#~ msgid "Cancel"
+#~ msgstr "Отменить"
+
+#~ msgctxt "button"
+#~ msgid "Don't Save"
+#~ msgstr "Не Сохранять"
+
+#~ msgid "An autosave file is available for this project."
+#~ msgstr "Файлы автосохранения доступны для данного проекта."
+
+#~ msgid "Do you want to load the autosave file instead?"
+#~ msgstr "Хотите загрузить их вместо открываемого файла?"
+
+#~ msgctxt "button"
+#~ msgid "Load"
+#~ msgstr "Загрузить Автосохранение"
+
+#~ msgctxt "button"
+#~ msgid "Don't Load"
+#~ msgstr "Просто Открыть Файл"
+
+#~ msgid ""
+#~ "Do you want to locate it manually?\n"
+#~ "If you select “No”, any geometry that depends on the missing file will be "
+#~ "removed."
+#~ msgstr ""
+#~ "Хотите найти их вручную?\n"
+#~ "Если вы ответите \"Нет\", то вся геометрия, которая зависит от "
+#~ "отсутствующего файла будет удалена."
+
+#~ msgctxt "button"
+#~ msgid "Yes"
+#~ msgstr "Да"
+
+#~ msgctxt "button"
+#~ msgid "No"
+#~ msgstr "Нет"
+
+#~ msgctxt "button"
+#~ msgid "OK"
+#~ msgstr "ХОРОШО"
+
+#~ msgid "_Cancel"
+#~ msgstr "Отменить"
+
+#~ msgid "_Open"
+#~ msgstr "Открыть"
+
+#~ msgid ""
+#~ "The file has changed since it was last saved.\n"
+#~ "\n"
+#~ "Do you want to save the changes?"
+#~ msgstr ""
+#~ "Файл имеет несохраненные изменения.\n"
+#~ "\n"
+#~ "Хотите сохранить их?"
+
+#~ msgctxt "title"
+#~ msgid "Modified File"
+#~ msgstr "Измененный Файл"
+
+#~ msgctxt "button"
+#~ msgid "Do_n't Save"
+#~ msgstr "Не Сохранять"
+
+#~ msgid ""
+#~ "An autosave file is available for this project.\n"
+#~ "\n"
+#~ "Do you want to load the autosave file instead?"
+#~ msgstr ""
+#~ "Файлы автосохранения доступны для данного проекта.\n"
+#~ "\n"
+#~ "Хотите загрузить их вместо открываемого файла?"
+
+#~ msgctxt "title"
+#~ msgid "Autosave Available"
+#~ msgstr "Автосохранение Доступно"
+
+#~ msgctxt "button"
+#~ msgid "_Load autosave"
+#~ msgstr "Загрузить Автосохранение"
+
+#~ msgctxt "button"
+#~ msgid "Do_n't Load"
+#~ msgstr "Не Загружать"
+
+#~ msgctxt "button"
+#~ msgid "_Yes"
+#~ msgstr "Да"
+
+#~ msgctxt "button"
+#~ msgid "_No"
+#~ msgstr "Нет"
+
+#~ msgid ""
+#~ "Select two entities that intersect each other (e.g. two lines or two "
+#~ "circles or a circle and a line)."
+#~ msgstr ""
+#~ "Выберите два пересекающихся примитива. Например, две линии или две "
+#~ "окружности или линию и окружность."
+
+#~ msgid "Show Menu &Bar"
+#~ msgstr "Показывать Мен&ю"
+
+#~ msgctxt "group-name"
+#~ msgid "link"
+#~ msgstr "ссылка-на-деталь"
+
+#~ msgid "Scale must not be zero or negative!"
+#~ msgstr ""
+#~ "Коэффициент масштабирования не может быть нулевым или отрицательным!"
--- /dev/null
+# Ukrainian translations for SolveSpace package.
+# Copyright (C) 2017 the SolveSpace authors
+# This file is distributed under the same license as the SolveSpace package.
+# AppSoft4 <appsoft@ua.fm>, 2017.
+msgid ""
+msgstr ""
+"Project-Id-Version: SolveSpace 3.0\n"
+"Report-Msgid-Bugs-To: whitequark@whitequark.org\n"
+"POT-Creation-Date: 2020-11-17 20:50-0500\n"
+"PO-Revision-Date: 2017-01-05 10:30+0000\n"
+"Last-Translator: appsoft@ua.fm\n"
+"Language-Team: OpenOrienteeringUkraine\n"
+"Language: uk_UA\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: clipboard.cpp:274
+msgid ""
+"Cut, paste, and copy work only in a workplane.\n"
+"\n"
+"Activate one with Sketch -> In Workplane."
+msgstr ""
+
+#: clipboard.cpp:291
+msgid "Clipboard is empty; nothing to paste."
+msgstr ""
+
+#: clipboard.cpp:338
+msgid "Number of copies to paste must be at least one."
+msgstr ""
+
+#: clipboard.cpp:354 textscreens.cpp:783
+msgid "Scale cannot be zero."
+msgstr ""
+
+#: clipboard.cpp:396
+msgid "Select one point to define origin of rotation."
+msgstr ""
+
+#: clipboard.cpp:408
+msgid "Select two points to define translation vector."
+msgstr ""
+
+#: clipboard.cpp:418
+msgid ""
+"Transformation is identity. So all copies will be exactly on top of each "
+"other."
+msgstr ""
+
+#: clipboard.cpp:422
+msgid "Too many items to paste; split this into smaller pastes."
+msgstr ""
+
+#: clipboard.cpp:427
+msgid "No workplane active."
+msgstr ""
+
+#: confscreen.cpp:410
+msgid "Bad format: specify coordinates as x, y, z"
+msgstr ""
+
+#: confscreen.cpp:420 style.cpp:659 textscreens.cpp:805
+msgid "Bad format: specify color as r, g, b"
+msgstr ""
+
+#: confscreen.cpp:446
+msgid ""
+"The perspective factor will have no effect until you enable View -> Use "
+"Perspective Projection."
+msgstr ""
+
+#: confscreen.cpp:459 confscreen.cpp:469
+#, c-format
+msgid "Specify between 0 and %d digits after the decimal."
+msgstr ""
+
+#: confscreen.cpp:481
+msgid "Export scale must not be zero!"
+msgstr ""
+
+#: confscreen.cpp:493
+msgid "Cutter radius offset must not be negative!"
+msgstr ""
+
+#: confscreen.cpp:547
+msgid "Bad value: autosave interval should be positive"
+msgstr ""
+
+#: confscreen.cpp:550
+msgid "Bad format: specify interval in integral minutes"
+msgstr ""
+
+#: constraint.cpp:12
+msgctxt "constr-name"
+msgid "pts-coincident"
+msgstr ""
+
+#: constraint.cpp:13
+msgctxt "constr-name"
+msgid "pt-pt-distance"
+msgstr ""
+
+#: constraint.cpp:14
+msgctxt "constr-name"
+msgid "pt-line-distance"
+msgstr ""
+
+#: constraint.cpp:15
+msgctxt "constr-name"
+msgid "pt-plane-distance"
+msgstr ""
+
+#: constraint.cpp:16
+msgctxt "constr-name"
+msgid "pt-face-distance"
+msgstr ""
+
+#: constraint.cpp:17
+msgctxt "constr-name"
+msgid "proj-pt-pt-distance"
+msgstr ""
+
+#: constraint.cpp:18
+msgctxt "constr-name"
+msgid "pt-in-plane"
+msgstr ""
+
+#: constraint.cpp:19
+msgctxt "constr-name"
+msgid "pt-on-line"
+msgstr ""
+
+#: constraint.cpp:20
+msgctxt "constr-name"
+msgid "pt-on-face"
+msgstr ""
+
+#: constraint.cpp:21
+msgctxt "constr-name"
+msgid "eq-length"
+msgstr ""
+
+#: constraint.cpp:22
+msgctxt "constr-name"
+msgid "eq-length-and-pt-ln-dist"
+msgstr ""
+
+#: constraint.cpp:23
+msgctxt "constr-name"
+msgid "eq-pt-line-distances"
+msgstr ""
+
+#: constraint.cpp:24
+msgctxt "constr-name"
+msgid "length-ratio"
+msgstr ""
+
+#: constraint.cpp:25
+msgctxt "constr-name"
+msgid "length-difference"
+msgstr ""
+
+#: constraint.cpp:26
+msgctxt "constr-name"
+msgid "symmetric"
+msgstr ""
+
+#: constraint.cpp:27
+msgctxt "constr-name"
+msgid "symmetric-h"
+msgstr ""
+
+#: constraint.cpp:28
+msgctxt "constr-name"
+msgid "symmetric-v"
+msgstr ""
+
+#: constraint.cpp:29
+msgctxt "constr-name"
+msgid "symmetric-line"
+msgstr ""
+
+#: constraint.cpp:30
+msgctxt "constr-name"
+msgid "at-midpoint"
+msgstr ""
+
+#: constraint.cpp:31
+msgctxt "constr-name"
+msgid "horizontal"
+msgstr ""
+
+#: constraint.cpp:32
+msgctxt "constr-name"
+msgid "vertical"
+msgstr ""
+
+#: constraint.cpp:33
+msgctxt "constr-name"
+msgid "diameter"
+msgstr ""
+
+#: constraint.cpp:34
+msgctxt "constr-name"
+msgid "pt-on-circle"
+msgstr ""
+
+#: constraint.cpp:35
+msgctxt "constr-name"
+msgid "same-orientation"
+msgstr ""
+
+#: constraint.cpp:36
+msgctxt "constr-name"
+msgid "angle"
+msgstr ""
+
+#: constraint.cpp:37
+msgctxt "constr-name"
+msgid "parallel"
+msgstr ""
+
+#: constraint.cpp:38
+msgctxt "constr-name"
+msgid "arc-line-tangent"
+msgstr ""
+
+#: constraint.cpp:39
+msgctxt "constr-name"
+msgid "cubic-line-tangent"
+msgstr ""
+
+#: constraint.cpp:40
+msgctxt "constr-name"
+msgid "curve-curve-tangent"
+msgstr ""
+
+#: constraint.cpp:41
+msgctxt "constr-name"
+msgid "perpendicular"
+msgstr ""
+
+#: constraint.cpp:42
+msgctxt "constr-name"
+msgid "eq-radius"
+msgstr ""
+
+#: constraint.cpp:43
+msgctxt "constr-name"
+msgid "eq-angle"
+msgstr ""
+
+#: constraint.cpp:44
+msgctxt "constr-name"
+msgid "eq-line-len-arc-len"
+msgstr ""
+
+#: constraint.cpp:45
+msgctxt "constr-name"
+msgid "lock-where-dragged"
+msgstr ""
+
+#: constraint.cpp:46
+msgctxt "constr-name"
+msgid "comment"
+msgstr ""
+
+#: constraint.cpp:171
+msgid ""
+"Bad selection for distance / diameter constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two points (distance between points)\n"
+" * a line segment (length)\n"
+" * two points and a line segment or normal (projected distance)\n"
+" * a workplane and a point (minimum distance)\n"
+" * a line segment and a point (minimum distance)\n"
+" * a plane face and a point (minimum distance)\n"
+" * a circle or an arc (diameter)\n"
+msgstr ""
+
+#: constraint.cpp:224
+msgid ""
+"Bad selection for on point / curve / plane constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points (points coincident)\n"
+" * a point and a workplane (point in plane)\n"
+" * a point and a line segment (point on line)\n"
+" * a point and a circle or arc (point on curve)\n"
+" * a point and a plane face (point on face)\n"
+msgstr ""
+
+#: constraint.cpp:286
+msgid ""
+"Bad selection for equal length / radius constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two line segments (equal length)\n"
+" * two line segments and two points (equal point-line distances)\n"
+" * a line segment and two points (equal point-line distances)\n"
+" * a line segment, and a point and line segment (point-line distance "
+"equals length)\n"
+" * four line segments or normals (equal angle between A,B and C,D)\n"
+" * three line segments or normals (equal angle between A,B and B,C)\n"
+" * two circles or arcs (equal radius)\n"
+" * a line segment and an arc (line segment length equals arc length)\n"
+msgstr ""
+
+#: constraint.cpp:325
+msgid ""
+"Bad selection for length ratio constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+
+#: constraint.cpp:342
+msgid ""
+"Bad selection for length difference constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+
+#: constraint.cpp:368
+msgid ""
+"Bad selection for at midpoint constraint. This constraint can apply to:\n"
+"\n"
+" * a line segment and a point (point at midpoint)\n"
+" * a line segment and a workplane (line's midpoint on plane)\n"
+msgstr ""
+
+#: constraint.cpp:426
+msgid ""
+"Bad selection for symmetric constraint. This constraint can apply to:\n"
+"\n"
+" * two points or a line segment (symmetric about workplane's coordinate "
+"axis)\n"
+" * line segment, and two points or a line segment (symmetric about line "
+"segment)\n"
+" * workplane, and two points or a line segment (symmetric about "
+"workplane)\n"
+msgstr ""
+
+#: constraint.cpp:440
+msgid ""
+"A workplane must be active when constraining symmetric without an explicit "
+"symmetry plane."
+msgstr ""
+
+#: constraint.cpp:470
+msgid ""
+"Activate a workplane (with Sketch -> In Workplane) before applying a "
+"horizontal or vertical constraint."
+msgstr ""
+
+#: constraint.cpp:483
+msgid ""
+"Bad selection for horizontal / vertical constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points\n"
+" * a line segment\n"
+msgstr ""
+
+#: constraint.cpp:504
+msgid ""
+"Bad selection for same orientation constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two normals\n"
+msgstr ""
+
+#: constraint.cpp:554
+msgid "Must select an angle constraint."
+msgstr ""
+
+#: constraint.cpp:567
+msgid "Must select a constraint with associated label."
+msgstr ""
+
+#: constraint.cpp:578
+msgid ""
+"Bad selection for angle constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+
+#: constraint.cpp:635
+msgid ""
+"The tangent arc and line segment must share an endpoint. Constrain them with "
+"Constrain -> On Point before constraining tangent."
+msgstr ""
+
+#: constraint.cpp:659
+msgid ""
+"The tangent cubic and line segment must share an endpoint. Constrain them "
+"with Constrain -> On Point before constraining tangent."
+msgstr ""
+
+#: constraint.cpp:669
+msgid "Curve-curve tangency must apply in workplane."
+msgstr ""
+
+#: constraint.cpp:687
+msgid ""
+"The curves must share an endpoint. Constrain them with Constrain -> On Point "
+"before constraining tangent."
+msgstr ""
+
+#: constraint.cpp:696
+msgid ""
+"Bad selection for parallel / tangent constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments (parallel)\n"
+" * a line segment and a normal (parallel)\n"
+" * two normals (parallel)\n"
+" * two line segments, arcs, or beziers, that share an endpoint (tangent)\n"
+msgstr ""
+
+#: constraint.cpp:714
+msgid ""
+"Bad selection for perpendicular constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+
+#: constraint.cpp:729
+msgid ""
+"Bad selection for lock point where dragged constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * a point\n"
+msgstr ""
+
+#: constraint.cpp:740
+msgid "click center of comment text"
+msgstr ""
+
+#: export.cpp:19
+msgid ""
+"No solid model present; draw one with extrudes and revolves, or use Export "
+"2d View to export bare lines and curves."
+msgstr ""
+
+#: export.cpp:61
+msgid ""
+"Bad selection for export section. Please select:\n"
+"\n"
+" * nothing, with an active workplane (workplane is section plane)\n"
+" * a face (section plane through face)\n"
+" * a point and two line segments (plane through point and parallel to "
+"lines)\n"
+msgstr ""
+
+#: export.cpp:822
+msgid "Active group mesh is empty; nothing to export."
+msgstr ""
+
+#: exportvector.cpp:337
+msgid "freehand lines were replaced with continuous lines"
+msgstr ""
+
+#: exportvector.cpp:339
+msgid "zigzag lines were replaced with continuous lines"
+msgstr ""
+
+#: exportvector.cpp:593
+msgid ""
+"Some aspects of the drawing have no DXF equivalent and were not exported:\n"
+msgstr ""
+
+#: exportvector.cpp:839
+msgid ""
+"PDF page size exceeds 200 by 200 inches; many viewers may reject this file."
+msgstr ""
+
+#: file.cpp:44 group.cpp:91
+msgctxt "group-name"
+msgid "sketch-in-plane"
+msgstr ""
+
+#: file.cpp:62
+msgctxt "group-name"
+msgid "#references"
+msgstr ""
+
+#: file.cpp:549
+msgid ""
+"Unrecognized data in file. This file may be corrupt, or from a newer version "
+"of the program."
+msgstr ""
+
+#: file.cpp:859
+msgctxt "title"
+msgid "Missing File"
+msgstr ""
+
+#: file.cpp:860
+#, c-format
+msgctxt "dialog"
+msgid "The linked file “%s” is not present."
+msgstr ""
+
+#: file.cpp:862
+msgctxt "dialog"
+msgid ""
+"Do you want to locate it manually?\n"
+"\n"
+"If you decline, any geometry that depends on the missing file will be "
+"permanently removed."
+msgstr ""
+
+#: file.cpp:865
+msgctxt "button"
+msgid "&Yes"
+msgstr ""
+
+#: file.cpp:867
+msgctxt "button"
+msgid "&No"
+msgstr ""
+
+#: file.cpp:869
+msgctxt "button"
+msgid "&Cancel"
+msgstr ""
+
+#: graphicswin.cpp:41
+msgid "&File"
+msgstr "&Файл"
+
+#: graphicswin.cpp:42
+msgid "&New"
+msgstr "&Новий"
+
+#: graphicswin.cpp:43
+msgid "&Open..."
+msgstr "&Відкрити..."
+
+#: graphicswin.cpp:44
+msgid "Open &Recent"
+msgstr "Відкрити &Недавні"
+
+#: graphicswin.cpp:45
+msgid "&Save"
+msgstr "&Зберегти"
+
+#: graphicswin.cpp:46
+msgid "Save &As..."
+msgstr "Зберегти &Як..."
+
+#: graphicswin.cpp:48
+msgid "Export &Image..."
+msgstr "Експортувати &Зображення..."
+
+#: graphicswin.cpp:49
+msgid "Export 2d &View..."
+msgstr "Експортувати 2d &Вигляд..."
+
+#: graphicswin.cpp:50
+msgid "Export 2d &Section..."
+msgstr "Експортувати 2d &Секцію..."
+
+#: graphicswin.cpp:51
+msgid "Export 3d &Wireframe..."
+msgstr "Експортувати 3d &Скелет..."
+
+#: graphicswin.cpp:52
+msgid "Export Triangle &Mesh..."
+msgstr "Експортувати Тригранний &Каркас..."
+
+#: graphicswin.cpp:53
+msgid "Export &Surfaces..."
+msgstr "Експортувати &Поверхні..."
+
+#: graphicswin.cpp:54
+msgid "Im&port..."
+msgstr "Ім&портувати..."
+
+#: graphicswin.cpp:57
+msgid "E&xit"
+msgstr "В&ихід"
+
+#: graphicswin.cpp:60
+msgid "&Edit"
+msgstr "&Редагувати"
+
+#: graphicswin.cpp:61
+msgid "&Undo"
+msgstr "&Відмінити"
+
+#: graphicswin.cpp:62
+msgid "&Redo"
+msgstr "&Повторити"
+
+#: graphicswin.cpp:63
+msgid "Re&generate All"
+msgstr "Ре&генерувати Все"
+
+#: graphicswin.cpp:65
+msgid "Snap Selection to &Grid"
+msgstr "Прикріпити Виділене до &Сітки"
+
+#: graphicswin.cpp:66
+msgid "Rotate Imported &90°"
+msgstr "Обернути Імпортоване на &90°"
+
+#: graphicswin.cpp:68
+msgid "Cu&t"
+msgstr "Ви&різати"
+
+#: graphicswin.cpp:69
+msgid "&Copy"
+msgstr "&Копіювати"
+
+#: graphicswin.cpp:70
+msgid "&Paste"
+msgstr "&Вставити"
+
+#: graphicswin.cpp:71
+msgid "Paste &Transformed..."
+msgstr "Вставити &Трансфмованим..."
+
+#: graphicswin.cpp:72
+msgid "&Delete"
+msgstr "&Delete"
+
+#: graphicswin.cpp:74
+msgid "Select &Edge Chain"
+msgstr "Виділити Ланцуг &Ребер"
+
+#: graphicswin.cpp:75
+msgid "Select &All"
+msgstr "Виділити &Усе"
+
+#: graphicswin.cpp:76
+msgid "&Unselect All"
+msgstr "&Зняти Виділення з Усього"
+
+#: graphicswin.cpp:78
+msgid "&Line Styles..."
+msgstr ""
+
+#: graphicswin.cpp:79
+msgid "&View Projection..."
+msgstr ""
+
+#: graphicswin.cpp:81
+msgid "Con&figuration..."
+msgstr ""
+
+#: graphicswin.cpp:84
+msgid "&View"
+msgstr "&View"
+
+#: graphicswin.cpp:85
+msgid "Zoom &In"
+msgstr "На&близити"
+
+#: graphicswin.cpp:86
+msgid "Zoom &Out"
+msgstr "Від&далити"
+
+#: graphicswin.cpp:87
+msgid "Zoom To &Fit"
+msgstr "Умістити на &Екрані"
+
+#: graphicswin.cpp:89
+msgid "Align View to &Workplane"
+msgstr "Вирівняти Вигляд до Робочої &Площини"
+
+#: graphicswin.cpp:90
+msgid "Nearest &Ortho View"
+msgstr "Найближчий &Ортогональний Вигляд"
+
+#: graphicswin.cpp:91
+msgid "Nearest &Isometric View"
+msgstr "Найближчий &Ізометричний Вигляд"
+
+#: graphicswin.cpp:92
+msgid "&Center View At Point"
+msgstr "&Центрувати Вигляд На Точці"
+
+#: graphicswin.cpp:94
+msgid "Show Snap &Grid"
+msgstr "Показати &Сітку Прикріплення"
+
+#: graphicswin.cpp:95
+msgid "Use &Perspective Projection"
+msgstr "Використовувати &Перспективну Проекцію"
+
+#: graphicswin.cpp:96
+msgid "Dimension &Units"
+msgstr ""
+
+#: graphicswin.cpp:97
+msgid "Dimensions in &Millimeters"
+msgstr "Розміри в &Міліметрах"
+
+#: graphicswin.cpp:98
+msgid "Dimensions in M&eters"
+msgstr ""
+
+#: graphicswin.cpp:99
+msgid "Dimensions in &Inches"
+msgstr "Розміри в &Дюймах"
+
+#: graphicswin.cpp:101
+msgid "Show &Toolbar"
+msgstr "Показати Панель &Інструментів"
+
+#: graphicswin.cpp:102
+msgid "Show Property Bro&wser"
+msgstr "Показати Вікно Власти&востей"
+
+#: graphicswin.cpp:104
+msgid "&Full Screen"
+msgstr "&Повний Екран"
+
+#: graphicswin.cpp:106
+msgid "&New Group"
+msgstr "&Нова Група"
+
+#: graphicswin.cpp:107
+msgid "Sketch In &3d"
+msgstr "Креслення В &3d"
+
+#: graphicswin.cpp:108
+msgid "Sketch In New &Workplane"
+msgstr "Креслення В Новій Робочій &Площині"
+
+#: graphicswin.cpp:110
+msgid "Step &Translating"
+msgstr "Покрокове &Переміщення"
+
+#: graphicswin.cpp:111
+msgid "Step &Rotating"
+msgstr "Покрокове &Обертання"
+
+#: graphicswin.cpp:113
+msgid "E&xtrude"
+msgstr "Ви&давити"
+
+#: graphicswin.cpp:114
+msgid "&Helix"
+msgstr ""
+
+#: graphicswin.cpp:115
+msgid "&Lathe"
+msgstr "&Виточити"
+
+#: graphicswin.cpp:116
+msgid "Re&volve"
+msgstr ""
+
+#: graphicswin.cpp:118
+msgid "Link / Assemble..."
+msgstr "Приєднати / Монтувати..."
+
+#: graphicswin.cpp:119
+msgid "Link Recent"
+msgstr "Приєднати Недавні"
+
+#: graphicswin.cpp:121
+msgid "&Sketch"
+msgstr "&Креслення"
+
+#: graphicswin.cpp:122
+msgid "In &Workplane"
+msgstr "В Робочій &Площині"
+
+#: graphicswin.cpp:123
+msgid "Anywhere In &3d"
+msgstr "Будь-де В &3d"
+
+#: graphicswin.cpp:125
+msgid "Datum &Point"
+msgstr "Опорна &Точка"
+
+#: graphicswin.cpp:126
+msgid "&Workplane"
+msgstr "Робоча &Площина"
+
+#: graphicswin.cpp:128
+msgid "Line &Segment"
+msgstr "&Відрізок Прямої"
+
+#: graphicswin.cpp:129
+msgid "C&onstruction Line Segment"
+msgstr "Контсрук&ційний Відрізок Прямої"
+
+#: graphicswin.cpp:130
+msgid "&Rectangle"
+msgstr "&Прямокутник"
+
+#: graphicswin.cpp:131
+msgid "&Circle"
+msgstr "&Коло"
+
+#: graphicswin.cpp:132
+msgid "&Arc of a Circle"
+msgstr "&Дуга Кола"
+
+#: graphicswin.cpp:133
+msgid "&Bezier Cubic Spline"
+msgstr "Кубічний Сплайн &Без'є"
+
+#: graphicswin.cpp:135
+msgid "&Text in TrueType Font"
+msgstr "&Текст з TrueType Шрифтом"
+
+#: graphicswin.cpp:136
+msgid "&Image"
+msgstr ""
+
+#: graphicswin.cpp:138
+msgid "To&ggle Construction"
+msgstr "Пере&мкнути Конструктивність"
+
+#: graphicswin.cpp:139
+msgid "Tangent &Arc at Point"
+msgstr "Дотична &Дуга на Точці"
+
+#: graphicswin.cpp:140
+msgid "Split Curves at &Intersection"
+msgstr "Розрізати Криві на &Перетині"
+
+#: graphicswin.cpp:142
+msgid "&Constrain"
+msgstr "&Обмежити"
+
+#: graphicswin.cpp:143
+msgid "&Distance / Diameter"
+msgstr "&Відстань / Діаметр"
+
+#: graphicswin.cpp:144
+msgid "Re&ference Dimension"
+msgstr "Від&носний Розмір"
+
+#: graphicswin.cpp:145
+msgid "A&ngle"
+msgstr "К&ут"
+
+#: graphicswin.cpp:146
+msgid "Reference An&gle"
+msgstr "Відносний К&ут"
+
+#: graphicswin.cpp:147
+msgid "Other S&upplementary Angle"
+msgstr "Інший Су&міжний Кут"
+
+#: graphicswin.cpp:148
+msgid "Toggle R&eference Dim"
+msgstr "Перемкнути Від&носність Розмірів"
+
+#: graphicswin.cpp:150
+msgid "&Horizontal"
+msgstr "&Горизонтально"
+
+#: graphicswin.cpp:151
+msgid "&Vertical"
+msgstr "&Вертикально"
+
+#: graphicswin.cpp:153
+msgid "&On Point / Curve / Plane"
+msgstr "&На точці / Кривій / Площині"
+
+#: graphicswin.cpp:154
+msgid "E&qual Length / Radius / Angle"
+msgstr "Рі&вні Довжина / Радіус / Кут"
+
+#: graphicswin.cpp:155
+msgid "Length Ra&tio"
+msgstr "Про&порція Довжин"
+
+#: graphicswin.cpp:156
+msgid "Length Diff&erence"
+msgstr "Рі&зниця Довжин"
+
+#: graphicswin.cpp:157
+msgid "At &Midpoint"
+msgstr "До &Середини"
+
+#: graphicswin.cpp:158
+msgid "S&ymmetric"
+msgstr "Си&метрично"
+
+#: graphicswin.cpp:159
+msgid "Para&llel / Tangent"
+msgstr "Пара&лельно / Дотична"
+
+#: graphicswin.cpp:160
+msgid "&Perpendicular"
+msgstr "&Препендикулярно"
+
+#: graphicswin.cpp:161
+msgid "Same Orient&ation"
+msgstr "Однакова Орієн&тація"
+
+#: graphicswin.cpp:162
+msgid "Lock Point Where &Dragged"
+msgstr "Фіксувати Точку Після &Переміщення"
+
+#: graphicswin.cpp:164
+msgid "Comment"
+msgstr "Коментар"
+
+#: graphicswin.cpp:166
+msgid "&Analyze"
+msgstr "&Аналізувати"
+
+#: graphicswin.cpp:167
+msgid "Measure &Volume"
+msgstr "Обрахувати &Об'єм"
+
+#: graphicswin.cpp:168
+msgid "Measure A&rea"
+msgstr "Обрахувати Пл&ощу"
+
+#: graphicswin.cpp:169
+msgid "Measure &Perimeter"
+msgstr "Обрахувати &Периметр"
+
+#: graphicswin.cpp:170
+msgid "Show &Interfering Parts"
+msgstr "Показати &Дотичні Частини"
+
+#: graphicswin.cpp:171
+msgid "Show &Naked Edges"
+msgstr "Показати &Приховані Ребра"
+
+#: graphicswin.cpp:172
+msgid "Show &Center of Mass"
+msgstr ""
+
+#: graphicswin.cpp:174
+msgid "Show &Underconstrained Points"
+msgstr ""
+
+#: graphicswin.cpp:176
+msgid "&Trace Point"
+msgstr "&Трасувати Точку"
+
+#: graphicswin.cpp:177
+msgid "&Stop Tracing..."
+msgstr "&Зупити Трасування..."
+
+#: graphicswin.cpp:178
+msgid "Step &Dimension..."
+msgstr "Прорахувати &Розмір..."
+
+#: graphicswin.cpp:180
+msgid "&Help"
+msgstr "&Довідка"
+
+#: graphicswin.cpp:181
+msgid "&Language"
+msgstr "&Мова"
+
+#: graphicswin.cpp:182
+msgid "&Website / Manual"
+msgstr "&Вебсайт / Посібник"
+
+#: graphicswin.cpp:184
+msgid "&About"
+msgstr "&Про програму"
+
+#: graphicswin.cpp:352
+msgid "(no recent files)"
+msgstr ""
+
+#: graphicswin.cpp:360
+#, c-format
+msgid "File '%s' does not exist."
+msgstr ""
+
+#: graphicswin.cpp:721
+msgid "No workplane is active, so the grid will not appear."
+msgstr ""
+
+#: graphicswin.cpp:730
+msgid ""
+"The perspective factor is set to zero, so the view will always be a parallel "
+"projection.\n"
+"\n"
+"For a perspective projection, modify the perspective factor in the "
+"configuration screen. A value around 0.3 is typical."
+msgstr ""
+
+#: graphicswin.cpp:809
+msgid ""
+"Select a point; this point will become the center of the view on screen."
+msgstr ""
+
+#: graphicswin.cpp:1103
+msgid "No additional entities share endpoints with the selected entities."
+msgstr ""
+
+#: graphicswin.cpp:1121
+msgid ""
+"To use this command, select a point or other entity from an linked part, or "
+"make a link group the active group."
+msgstr ""
+
+#: graphicswin.cpp:1144
+msgid ""
+"No workplane is active. Activate a workplane (with Sketch -> In Workplane) "
+"to define the plane for the snap grid."
+msgstr ""
+
+#: graphicswin.cpp:1151
+msgid ""
+"Can't snap these items to grid; select points, text comments, or constraints "
+"with a label. To snap a line, select its endpoints."
+msgstr ""
+
+#: graphicswin.cpp:1239
+msgid "No workplane selected. Activating default workplane for this group."
+msgstr ""
+
+#: graphicswin.cpp:1242
+msgid ""
+"No workplane is selected, and the active group does not have a default "
+"workplane. Try selecting a workplane, or activating a sketch-in-new-"
+"workplane group."
+msgstr ""
+
+#: graphicswin.cpp:1263
+msgid ""
+"Bad selection for tangent arc at point. Select a single point, or select "
+"nothing to set up arc parameters."
+msgstr ""
+
+#: graphicswin.cpp:1274
+msgid "click point on arc (draws anti-clockwise)"
+msgstr ""
+
+#: graphicswin.cpp:1275
+msgid "click to place datum point"
+msgstr ""
+
+#: graphicswin.cpp:1276
+msgid "click first point of line segment"
+msgstr ""
+
+#: graphicswin.cpp:1278
+msgid "click first point of construction line segment"
+msgstr ""
+
+#: graphicswin.cpp:1279
+msgid "click first point of cubic segment"
+msgstr ""
+
+#: graphicswin.cpp:1280
+msgid "click center of circle"
+msgstr ""
+
+#: graphicswin.cpp:1281
+msgid "click origin of workplane"
+msgstr ""
+
+#: graphicswin.cpp:1282
+msgid "click one corner of rectangle"
+msgstr ""
+
+#: graphicswin.cpp:1283
+msgid "click top left of text"
+msgstr ""
+
+#: graphicswin.cpp:1289
+msgid "click top left of image"
+msgstr ""
+
+#: graphicswin.cpp:1301
+msgid ""
+"No entities are selected. Select entities before trying to toggle their "
+"construction state."
+msgstr ""
+
+#: group.cpp:86
+msgctxt "group-name"
+msgid "sketch-in-3d"
+msgstr ""
+
+#: group.cpp:142
+msgid ""
+"Bad selection for new sketch in workplane. This group can be created with:\n"
+"\n"
+" * a point (through the point, orthogonal to coordinate axes)\n"
+" * a point and two line segments (through the point, parallel to the "
+"lines)\n"
+" * a workplane (copy of the workplane)\n"
+msgstr ""
+
+#: group.cpp:154
+msgid ""
+"Activate a workplane (Sketch -> In Workplane) before extruding. The sketch "
+"will be extruded normal to the workplane."
+msgstr ""
+
+#: group.cpp:163
+msgctxt "group-name"
+msgid "extrude"
+msgstr ""
+
+#: group.cpp:168
+msgid "Lathe operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:179
+msgid ""
+"Bad selection for new lathe group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:189
+msgctxt "group-name"
+msgid "lathe"
+msgstr ""
+
+#: group.cpp:194
+msgid "Revolve operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:205
+msgid ""
+"Bad selection for new revolve group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:217
+msgctxt "group-name"
+msgid "revolve"
+msgstr ""
+
+#: group.cpp:222
+msgid "Helix operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:233
+msgid ""
+"Bad selection for new helix group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:245
+msgctxt "group-name"
+msgid "helix"
+msgstr ""
+
+#: group.cpp:258
+msgid ""
+"Bad selection for new rotation. This group can be created with:\n"
+"\n"
+" * a point, while locked in workplane (rotate in plane, about that "
+"point)\n"
+" * a point and a line or a normal (rotate about an axis through the "
+"point, and parallel to line / normal)\n"
+msgstr ""
+
+#: group.cpp:271
+msgctxt "group-name"
+msgid "rotate"
+msgstr ""
+
+#: group.cpp:282
+msgctxt "group-name"
+msgid "translate"
+msgstr ""
+
+#: group.cpp:400
+msgid "(unnamed)"
+msgstr ""
+
+#: groupmesh.cpp:708
+msgid "not closed contour, or not all same style!"
+msgstr ""
+
+#: groupmesh.cpp:721
+msgid "points not all coplanar!"
+msgstr ""
+
+#: groupmesh.cpp:723
+msgid "contour is self-intersecting!"
+msgstr ""
+
+#: groupmesh.cpp:725
+msgid "zero-length edge!"
+msgstr ""
+
+#: modify.cpp:254
+msgid "Must be sketching in workplane to create tangent arc."
+msgstr ""
+
+#: modify.cpp:301
+msgid ""
+"To create a tangent arc, select a point where two non-construction lines or "
+"circles in this group and workplane join."
+msgstr ""
+
+#: modify.cpp:388
+msgid ""
+"Couldn't round this corner. Try a smaller radius, or try creating the "
+"desired geometry by hand with tangency constraints."
+msgstr ""
+
+#: modify.cpp:597
+msgid "Couldn't split this entity; lines, circles, or cubics only."
+msgstr ""
+
+#: modify.cpp:624
+msgid "Must be sketching in workplane to split."
+msgstr ""
+
+#: modify.cpp:631
+msgid ""
+"Select two entities that intersect each other (e.g. two lines/circles/arcs "
+"or a line/circle/arc and a point)."
+msgstr ""
+
+#: modify.cpp:736
+msgid "Can't split; no intersection found."
+msgstr ""
+
+#: mouse.cpp:560
+msgid "Assign to Style"
+msgstr "Встановити Стиль"
+
+#: mouse.cpp:576
+msgid "No Style"
+msgstr "Без Стилю"
+
+#: mouse.cpp:579
+msgid "Newly Created Custom Style..."
+msgstr "Створити Користувацький Стиль..."
+
+#: mouse.cpp:586
+msgid "Group Info"
+msgstr "Параметри Групи"
+
+#: mouse.cpp:606
+msgid "Style Info"
+msgstr "Параметри Стилю"
+
+#: mouse.cpp:626
+msgid "Select Edge Chain"
+msgstr "Виділити Ланцюг Ребер"
+
+#: mouse.cpp:632
+msgid "Toggle Reference Dimension"
+msgstr "Перемкнути Відносність Розміру"
+
+#: mouse.cpp:638
+msgid "Other Supplementary Angle"
+msgstr "Інший Суміжний Кут"
+
+#: mouse.cpp:643
+msgid "Snap to Grid"
+msgstr "Прикріпити до Сітки"
+
+#: mouse.cpp:652
+msgid "Remove Spline Point"
+msgstr "Видалити Точку Сплайну"
+
+#: mouse.cpp:687
+msgid "Add Spline Point"
+msgstr "Додати Точку Сплайну"
+
+#: mouse.cpp:691
+msgid "Cannot add spline point: maximum number of points reached."
+msgstr ""
+
+#: mouse.cpp:716
+msgid "Toggle Construction"
+msgstr "Пермкнути Конструктивність"
+
+#: mouse.cpp:731
+msgid "Delete Point-Coincident Constraint"
+msgstr "Роз'єднати З'єднання Точок"
+
+#: mouse.cpp:750
+msgid "Cut"
+msgstr "Вирізати"
+
+#: mouse.cpp:752
+msgid "Copy"
+msgstr "Копіювати"
+
+#: mouse.cpp:756
+msgid "Select All"
+msgstr "Виділити Усе"
+
+#: mouse.cpp:761
+msgid "Paste"
+msgstr "Вставити"
+
+#: mouse.cpp:763
+msgid "Paste Transformed..."
+msgstr "Вставити Трансформованим..."
+
+#: mouse.cpp:768
+msgid "Delete"
+msgstr "Видалити"
+
+#: mouse.cpp:771
+msgid "Unselect All"
+msgstr "Зняти Виділення з Усього"
+
+#: mouse.cpp:778
+msgid "Unselect Hovered"
+msgstr "Зняти Виділення з Наведеного"
+
+#: mouse.cpp:787
+msgid "Zoom to Fit"
+msgstr "Умістити на Екрані"
+
+#: mouse.cpp:990 mouse.cpp:1277
+msgid "click next point of line, or press Esc"
+msgstr ""
+
+#: mouse.cpp:996
+msgid ""
+"Can't draw rectangle in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+
+#: mouse.cpp:1030
+msgid "click to place other corner of rectangle"
+msgstr ""
+
+#: mouse.cpp:1050
+msgid "click to set radius"
+msgstr ""
+
+#: mouse.cpp:1055
+msgid ""
+"Can't draw arc in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+
+#: mouse.cpp:1074
+msgid "click to place point"
+msgstr ""
+
+#: mouse.cpp:1090
+msgid "click next point of cubic, or press Esc"
+msgstr ""
+
+#: mouse.cpp:1095
+msgid ""
+"Sketching in a workplane already; sketch in 3d before creating new workplane."
+msgstr ""
+
+#: mouse.cpp:1111
+msgid ""
+"Can't draw text in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+
+#: mouse.cpp:1128
+msgid "click to place bottom right of text"
+msgstr ""
+
+#: mouse.cpp:1134
+msgid ""
+"Can't draw image in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr ""
+
+#: mouse.cpp:1161
+msgid "NEW COMMENT -- DOUBLE-CLICK TO EDIT"
+msgstr ""
+
+#: platform/gui.cpp:85 platform/gui.cpp:89
+msgctxt "file-type"
+msgid "SolveSpace models"
+msgstr ""
+
+#: platform/gui.cpp:90
+msgctxt "file-type"
+msgid "IDF circuit board"
+msgstr ""
+
+#: platform/gui.cpp:94
+msgctxt "file-type"
+msgid "PNG image"
+msgstr ""
+
+#: platform/gui.cpp:98
+msgctxt "file-type"
+msgid "STL mesh"
+msgstr ""
+
+#: platform/gui.cpp:99
+msgctxt "file-type"
+msgid "Wavefront OBJ mesh"
+msgstr ""
+
+#: platform/gui.cpp:100
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, with viewer"
+msgstr ""
+
+#: platform/gui.cpp:101
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, mesh only"
+msgstr ""
+
+#: platform/gui.cpp:102
+msgctxt "file-type"
+msgid "Q3D Object file"
+msgstr ""
+
+#: platform/gui.cpp:103
+msgctxt "file-type"
+msgid "VRML text file"
+msgstr ""
+
+#: platform/gui.cpp:107 platform/gui.cpp:114 platform/gui.cpp:121
+msgctxt "file-type"
+msgid "STEP file"
+msgstr ""
+
+#: platform/gui.cpp:111
+msgctxt "file-type"
+msgid "PDF file"
+msgstr ""
+
+#: platform/gui.cpp:112
+msgctxt "file-type"
+msgid "Encapsulated PostScript"
+msgstr ""
+
+#: platform/gui.cpp:113
+msgctxt "file-type"
+msgid "Scalable Vector Graphics"
+msgstr ""
+
+#: platform/gui.cpp:115 platform/gui.cpp:122
+msgctxt "file-type"
+msgid "DXF file (AutoCAD 2007)"
+msgstr ""
+
+#: platform/gui.cpp:116
+msgctxt "file-type"
+msgid "HPGL file"
+msgstr ""
+
+#: platform/gui.cpp:117
+msgctxt "file-type"
+msgid "G Code"
+msgstr ""
+
+#: platform/gui.cpp:126
+msgctxt "file-type"
+msgid "AutoCAD DXF and DWG files"
+msgstr ""
+
+#: platform/gui.cpp:130
+msgctxt "file-type"
+msgid "Comma-separated values"
+msgstr ""
+
+#: platform/guigtk.cpp:1317 platform/guimac.mm:1360 platform/guiwin.cpp:1608
+msgid "untitled"
+msgstr ""
+
+#: platform/guigtk.cpp:1328 platform/guigtk.cpp:1361 platform/guimac.mm:1318
+#: platform/guiwin.cpp:1555
+msgctxt "title"
+msgid "Save File"
+msgstr ""
+
+#: platform/guigtk.cpp:1329 platform/guigtk.cpp:1362 platform/guimac.mm:1301
+#: platform/guiwin.cpp:1557
+msgctxt "title"
+msgid "Open File"
+msgstr ""
+
+#: platform/guigtk.cpp:1332 platform/guigtk.cpp:1368
+msgctxt "button"
+msgid "_Cancel"
+msgstr ""
+
+#: platform/guigtk.cpp:1333 platform/guigtk.cpp:1366
+msgctxt "button"
+msgid "_Save"
+msgstr ""
+
+#: platform/guigtk.cpp:1334 platform/guigtk.cpp:1367
+msgctxt "button"
+msgid "_Open"
+msgstr ""
+
+#: style.cpp:166
+msgid ""
+"Can't assign style to an entity that's derived from another entity; try "
+"assigning a style to this entity's parent."
+msgstr ""
+
+#: style.cpp:665
+msgid "Style name cannot be empty"
+msgstr ""
+
+#: textscreens.cpp:741
+msgid "Can't repeat fewer than 1 time."
+msgstr ""
+
+#: textscreens.cpp:745
+msgid "Can't repeat more than 999 times."
+msgstr ""
+
+#: textscreens.cpp:770
+msgid "Group name cannot be empty"
+msgstr ""
+
+#: textscreens.cpp:813
+msgid "Opacity must be between zero and one."
+msgstr ""
+
+#: textscreens.cpp:848
+msgid "Radius cannot be zero or negative."
+msgstr ""
+
+#: toolbar.cpp:18
+msgid "Sketch line segment"
+msgstr "Накреслети відрізок прямої"
+
+#: toolbar.cpp:20
+msgid "Sketch rectangle"
+msgstr "Накреслити прямокутник"
+
+#: toolbar.cpp:22
+msgid "Sketch circle"
+msgstr "Накреслити коло"
+
+#: toolbar.cpp:24
+msgid "Sketch arc of a circle"
+msgstr "Накреслити дугу кола"
+
+#: toolbar.cpp:26
+msgid "Sketch curves from text in a TrueType font"
+msgstr "Накреслити криві з тексту на TrueType шрифті"
+
+#: toolbar.cpp:28
+msgid "Sketch image from a file"
+msgstr ""
+
+#: toolbar.cpp:30
+msgid "Create tangent arc at selected point"
+msgstr "Створити дотичну дугу у вибраній точці"
+
+#: toolbar.cpp:32
+msgid "Sketch cubic Bezier spline"
+msgstr "Накреслити кубічний сплайн Без'є"
+
+#: toolbar.cpp:34
+msgid "Sketch datum point"
+msgstr "Накреслити опорну точку"
+
+#: toolbar.cpp:36
+msgid "Toggle construction"
+msgstr "Перемкнути конструктивність"
+
+#: toolbar.cpp:38
+msgid "Split lines / curves where they intersect"
+msgstr "Розрізати лініх / криві в місцях перетину"
+
+#: toolbar.cpp:42
+msgid "Constrain distance / diameter / length"
+msgstr "Визначити відстань / діаметр / довжину"
+
+#: toolbar.cpp:44
+msgid "Constrain angle"
+msgstr "Визначити кут"
+
+#: toolbar.cpp:46
+msgid "Constrain to be horizontal"
+msgstr "Встановити горизонтально"
+
+#: toolbar.cpp:48
+msgid "Constrain to be vertical"
+msgstr "Встановити перпендикулярно"
+
+#: toolbar.cpp:50
+msgid "Constrain to be parallel or tangent"
+msgstr "Встановити паралельно або дотично"
+
+#: toolbar.cpp:52
+msgid "Constrain to be perpendicular"
+msgstr "Встановити перпендикулярно"
+
+#: toolbar.cpp:54
+msgid "Constrain point on line / curve / plane / point"
+msgstr "Прив'язати точку до лінії / кривої / площини / точки"
+
+#: toolbar.cpp:56
+msgid "Constrain symmetric"
+msgstr "Встановити симетрично"
+
+#: toolbar.cpp:58
+msgid "Constrain equal length / radius / angle"
+msgstr "Встановити однаковими відстань / радіус / кут"
+
+#: toolbar.cpp:60
+msgid "Constrain normals in same orientation"
+msgstr "Спрямувати нормалі в одному напрямку"
+
+#: toolbar.cpp:62
+msgid "Other supplementary angle"
+msgstr "Інший суміжний кут"
+
+#: toolbar.cpp:64
+msgid "Toggle reference dimension"
+msgstr "Перемкнути відносність розміру"
+
+#: toolbar.cpp:68
+msgid "New group extruding active sketch"
+msgstr "Нова група екструдування активного креслення"
+
+#: toolbar.cpp:70
+msgid "New group rotating active sketch"
+msgstr "Нова група обертання актиного креслення"
+
+#: toolbar.cpp:72
+msgid "New group step and repeat rotating"
+msgstr "Нова група крокування і повторення обертання"
+
+#: toolbar.cpp:74
+msgid "New group step and repeat translating"
+msgstr "Нова група крокування і повторення зміщення"
+
+#: toolbar.cpp:76
+msgid "New group in new workplane (thru given entities)"
+msgstr "Нова група в новій площині (через обрані об'екти)"
+
+#: toolbar.cpp:78
+msgid "New group in 3d"
+msgstr "Нова група в 3d"
+
+#: toolbar.cpp:80
+msgid "New group linking / assembling file"
+msgstr "Нова група приєднання / монтування файлу"
+
+#: toolbar.cpp:84
+msgid "Nearest isometric view"
+msgstr "Найближчий ізометричний вигляд"
+
+#: toolbar.cpp:86
+msgid "Align view to active workplane"
+msgstr "Вирівняти вигляд до активної робочої площини"
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Error"
+msgstr ""
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Message"
+msgstr ""
+
+#: util.cpp:170
+msgctxt "button"
+msgid "&OK"
+msgstr ""
+
+#: view.cpp:78
+msgid "Scale cannot be zero or negative."
+msgstr ""
+
+#: view.cpp:90 view.cpp:99
+msgid "Bad format: specify x, y, z"
+msgstr ""
+
+#~ msgid "Show Degrees of &Freedom"
+#~ msgstr "Показати Степені &Свободи"
+
+#~ msgid "Show Menu &Bar"
+#~ msgstr "Показати Панель &Меню"
--- /dev/null
+# Chinese translations for SolveSpace package.
+# Copyright (C) 2020 the PACKAGE authors
+# This file is distributed under the same license as the SolveSpace package.
+# <lomatus@163.com>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: SolveSpace 3.0\n"
+"Report-Msgid-Bugs-To: whitequark@whitequark.org\n"
+"POT-Creation-Date: 2020-11-17 20:50-0500\n"
+"PO-Revision-Date: 2020-09-28 12:42+0800\n"
+"Last-Translator: lomatus@163.com\n"
+"Language-Team: none\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.4.1\n"
+
+#: clipboard.cpp:274
+msgid ""
+"Cut, paste, and copy work only in a workplane.\n"
+"\n"
+"Activate one with Sketch -> In Workplane."
+msgstr ""
+"仅在工作平面中剪切、粘贴和复制工作。\n"
+"\n"
+"使用\"工作平面中的草图 -+\"激活一个。"
+
+#: clipboard.cpp:291
+msgid "Clipboard is empty; nothing to paste."
+msgstr "剪贴板为空;没有要粘贴的内容。"
+
+#: clipboard.cpp:338
+msgid "Number of copies to paste must be at least one."
+msgstr "要粘贴的副本数必须至少为 1 个。"
+
+#: clipboard.cpp:354 textscreens.cpp:783
+msgid "Scale cannot be zero."
+msgstr "缩放不能为零。"
+
+#: clipboard.cpp:396
+msgid "Select one point to define origin of rotation."
+msgstr "选择一个点以定义旋转原点。"
+
+#: clipboard.cpp:408
+msgid "Select two points to define translation vector."
+msgstr "选择两个点来定义转换向量。"
+
+#: clipboard.cpp:418
+msgid ""
+"Transformation is identity. So all copies will be exactly on top of each "
+"other."
+msgstr "转换就是标识,因此所有的复制在彼此之上。"
+
+#: clipboard.cpp:422
+msgid "Too many items to paste; split this into smaller pastes."
+msgstr "要粘贴的项目太多; 请把他们拆分。"
+
+#: clipboard.cpp:427
+msgid "No workplane active."
+msgstr "没有工作平面处于活动状态。"
+
+#: confscreen.cpp:410
+msgid "Bad format: specify coordinates as x, y, z"
+msgstr "格式错误:将坐标指定为 x、y、z"
+
+#: confscreen.cpp:420 style.cpp:659 textscreens.cpp:805
+msgid "Bad format: specify color as r, g, b"
+msgstr "格式错误:将颜色指定为 r、g、b"
+
+#: confscreen.cpp:446
+msgid ""
+"The perspective factor will have no effect until you enable View -> Use "
+"Perspective Projection."
+msgstr "在启用\"视图 -= 使用透视投影\"之前,透视因子将不起作用。"
+
+#: confscreen.cpp:459 confscreen.cpp:469
+#, c-format
+msgid "Specify between 0 and %d digits after the decimal."
+msgstr "在十进制之后指定 0 和 %d 数字之间。"
+
+#: confscreen.cpp:481
+msgid "Export scale must not be zero!"
+msgstr "输出比例不能为零!"
+
+#: confscreen.cpp:493
+msgid "Cutter radius offset must not be negative!"
+msgstr "刀具半径偏移不能为负数!"
+
+#: confscreen.cpp:547
+msgid "Bad value: autosave interval should be positive"
+msgstr "坏值:自动保存间隔应为正"
+
+#: confscreen.cpp:550
+msgid "Bad format: specify interval in integral minutes"
+msgstr "格式错误:以整数分钟为单位指定间隔"
+
+#: constraint.cpp:12
+msgctxt "constr-name"
+msgid "pts-coincident"
+msgstr "点约束"
+
+#: constraint.cpp:13
+msgctxt "constr-name"
+msgid "pt-pt-distance"
+msgstr "点点间距"
+
+#: constraint.cpp:14
+msgctxt "constr-name"
+msgid "pt-line-distance"
+msgstr "线长"
+
+#: constraint.cpp:15
+msgctxt "constr-name"
+msgid "pt-plane-distance"
+msgstr "平面具体"
+
+#: constraint.cpp:16
+msgctxt "constr-name"
+msgid "pt-face-distance"
+msgstr "面间距"
+
+#: constraint.cpp:17
+msgctxt "constr-name"
+msgid "proj-pt-pt-distance"
+msgstr "点点距离"
+
+#: constraint.cpp:18
+msgctxt "constr-name"
+msgid "pt-in-plane"
+msgstr "点在面"
+
+#: constraint.cpp:19
+msgctxt "constr-name"
+msgid "pt-on-line"
+msgstr "点在线"
+
+#: constraint.cpp:20
+msgctxt "constr-name"
+msgid "pt-on-face"
+msgstr "点在面"
+
+#: constraint.cpp:21
+msgctxt "constr-name"
+msgid "eq-length"
+msgstr "长度相同"
+
+#: constraint.cpp:22
+msgctxt "constr-name"
+msgid "eq-length-and-pt-ln-dist"
+msgstr "长度相等且点在距离"
+
+#: constraint.cpp:23
+msgctxt "constr-name"
+msgid "eq-pt-line-distances"
+msgstr "点线距离相等"
+
+#: constraint.cpp:24
+msgctxt "constr-name"
+msgid "length-ratio"
+msgstr "长度比率"
+
+#: constraint.cpp:25
+msgctxt "constr-name"
+msgid "length-difference"
+msgstr "长度不同"
+
+#: constraint.cpp:26
+msgctxt "constr-name"
+msgid "symmetric"
+msgstr "对称的"
+
+#: constraint.cpp:27
+msgctxt "constr-name"
+msgid "symmetric-h"
+msgstr "水平对称"
+
+#: constraint.cpp:28
+msgctxt "constr-name"
+msgid "symmetric-v"
+msgstr "纵向对称"
+
+#: constraint.cpp:29
+msgctxt "constr-name"
+msgid "symmetric-line"
+msgstr "线对称"
+
+#: constraint.cpp:30
+msgctxt "constr-name"
+msgid "at-midpoint"
+msgstr "在中点"
+
+#: constraint.cpp:31
+msgctxt "constr-name"
+msgid "horizontal"
+msgstr "水平约束"
+
+#: constraint.cpp:32
+msgctxt "constr-name"
+msgid "vertical"
+msgstr "垂直约束"
+
+#: constraint.cpp:33
+msgctxt "constr-name"
+msgid "diameter"
+msgstr "直径约束"
+
+#: constraint.cpp:34
+msgctxt "constr-name"
+msgid "pt-on-circle"
+msgstr "圆点约束"
+
+#: constraint.cpp:35
+msgctxt "constr-name"
+msgid "same-orientation"
+msgstr "相同原点"
+
+#: constraint.cpp:36
+msgctxt "constr-name"
+msgid "angle"
+msgstr "角度约束"
+
+#: constraint.cpp:37
+msgctxt "constr-name"
+msgid "parallel"
+msgstr "平行约束"
+
+#: constraint.cpp:38
+msgctxt "constr-name"
+msgid "arc-line-tangent"
+msgstr "弧切线"
+
+#: constraint.cpp:39
+msgctxt "constr-name"
+msgid "cubic-line-tangent"
+msgstr "立方体切线"
+
+#: constraint.cpp:40
+msgctxt "constr-name"
+msgid "curve-curve-tangent"
+msgstr "曲线间切线"
+
+#: constraint.cpp:41
+msgctxt "constr-name"
+msgid "perpendicular"
+msgstr "垂直约束"
+
+#: constraint.cpp:42
+msgctxt "constr-name"
+msgid "eq-radius"
+msgstr "等于半径"
+
+#: constraint.cpp:43
+msgctxt "constr-name"
+msgid "eq-angle"
+msgstr "等于角度"
+
+#: constraint.cpp:44
+msgctxt "constr-name"
+msgid "eq-line-len-arc-len"
+msgstr "等于线长或弧长"
+
+#: constraint.cpp:45
+msgctxt "constr-name"
+msgid "lock-where-dragged"
+msgstr "锁定位置"
+
+#: constraint.cpp:46
+msgctxt "constr-name"
+msgid "comment"
+msgstr "备注"
+
+#: constraint.cpp:171
+msgid ""
+"Bad selection for distance / diameter constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two points (distance between points)\n"
+" * a line segment (length)\n"
+" * two points and a line segment or normal (projected distance)\n"
+" * a workplane and a point (minimum distance)\n"
+" * a line segment and a point (minimum distance)\n"
+" * a plane face and a point (minimum distance)\n"
+" * a circle or an arc (diameter)\n"
+msgstr ""
+"距离/直径约束的选择错误。此约束可应用于:\n"
+"\n"
+"* 两点(点之间的距离)\n"
+" * 线段(长度)\n"
+" * 两个点和线段或法线(投影距离)\n"
+" * 工作平面和点(最小距离)\n"
+" * 线段和点(最小距离)\n"
+" * 平面面和点(最小距离)\n"
+" * 圆或弧(直径)\n"
+
+#: constraint.cpp:224
+msgid ""
+"Bad selection for on point / curve / plane constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points (points coincident)\n"
+" * a point and a workplane (point in plane)\n"
+" * a point and a line segment (point on line)\n"
+" * a point and a circle or arc (point on curve)\n"
+" * a point and a plane face (point on face)\n"
+msgstr ""
+"点 / 曲线 / 平面约束的选定方法错误。此约束可应用于:\n"
+"\n"
+"* 两点(点重合)\n"
+" * 一个点和一个工作平面(平面中点)\n"
+" * 点和线段(点在线)\n"
+" * 一个点和一个圆或圆(曲线上的点)\n"
+" * 点和平面面(点在脸上)\n"
+
+#: constraint.cpp:286
+msgid ""
+"Bad selection for equal length / radius constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two line segments (equal length)\n"
+" * two line segments and two points (equal point-line distances)\n"
+" * a line segment and two points (equal point-line distances)\n"
+" * a line segment, and a point and line segment (point-line distance "
+"equals length)\n"
+" * four line segments or normals (equal angle between A,B and C,D)\n"
+" * three line segments or normals (equal angle between A,B and B,C)\n"
+" * two circles or arcs (equal radius)\n"
+" * a line segment and an arc (line segment length equals arc length)\n"
+msgstr ""
+"等长度/半径约束的选定方法错误。此约束可应用于:\n"
+"\n"
+"* 两个线段(相等长度)\n"
+" * 两个线段和两个点(相等的点线距离)\n"
+" * 线段和两个点(相等的点线距离)\n"
+" * 线段和点段和线段(点线距离等于长度)\n"
+" * 四条线段或法线(A、B 和 C、D 之间的等角)\n"
+" * 三条线段或法线(A、B 和 B、C 之间的等角)\n"
+" * 两个圆或圆(相等半径)\n"
+" * 线段和圆弧(线段长度等于弧长)\n"
+
+#: constraint.cpp:325
+msgid ""
+"Bad selection for length ratio constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+"长度比率约束的选择错误。此约束可应用于:\n"
+"\n"
+"* 两个线段\n"
+
+#: constraint.cpp:342
+msgid ""
+"Bad selection for length difference constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+"长度差异约束的选择错误。此约束可应用于:\n"
+"\n"
+"* 两个线段\n"
+
+#: constraint.cpp:368
+msgid ""
+"Bad selection for at midpoint constraint. This constraint can apply to:\n"
+"\n"
+" * a line segment and a point (point at midpoint)\n"
+" * a line segment and a workplane (line's midpoint on plane)\n"
+msgstr ""
+"中点约束的选定方法错误。此约束可应用于:\n"
+"\n"
+"* 线段和点(点在中点)\n"
+" * 线段和工作平面(平面上的线中点)\n"
+
+#: constraint.cpp:426
+msgid ""
+"Bad selection for symmetric constraint. This constraint can apply to:\n"
+"\n"
+" * two points or a line segment (symmetric about workplane's coordinate "
+"axis)\n"
+" * line segment, and two points or a line segment (symmetric about line "
+"segment)\n"
+" * workplane, and two points or a line segment (symmetric about "
+"workplane)\n"
+msgstr ""
+"对称约束的选择错误。此约束可应用于:\n"
+"\n"
+"* 两个点或线段(与工作平面的坐标轴对称)\n"
+" * 线段,和两个点或线段(对称的线段)\n"
+" * 工作平面和两个点或线段(工作平面对称)\n"
+
+#: constraint.cpp:440
+msgid ""
+"A workplane must be active when constraining symmetric without an explicit "
+"symmetry plane."
+msgstr "在没有显式对称平面约束对称时,工作平面必须处于活动状态。"
+
+#: constraint.cpp:470
+msgid ""
+"Activate a workplane (with Sketch -> In Workplane) before applying a "
+"horizontal or vertical constraint."
+msgstr "在应用水平或垂直约束之前,激活工作平面(使用草图 -= 在工作平面中)。"
+
+#: constraint.cpp:483
+msgid ""
+"Bad selection for horizontal / vertical constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * two points\n"
+" * a line segment\n"
+msgstr ""
+"水平/垂直约束的选择错误。此约束可应用于:\n"
+"\n"
+"• 两点\n"
+" • 线段\n"
+
+#: constraint.cpp:504
+msgid ""
+"Bad selection for same orientation constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two normals\n"
+msgstr ""
+"同一方向约束的选择错误。此约束可应用于:\n"
+"\n"
+"• 两个法线\n"
+
+#: constraint.cpp:554
+msgid "Must select an angle constraint."
+msgstr "必须选择角度约束。"
+
+#: constraint.cpp:567
+msgid "Must select a constraint with associated label."
+msgstr "必须选择具有关联标签的约束。"
+
+#: constraint.cpp:578
+msgid ""
+"Bad selection for angle constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+"角度约束的选择错误。此约束可应用于:\n"
+"\n"
+"* 两个线段\n"
+" * 线段和法线\n"
+" • 两个法线\n"
+
+#: constraint.cpp:635
+msgid ""
+"The tangent arc and line segment must share an endpoint. Constrain them with "
+"Constrain -> On Point before constraining tangent."
+msgstr "切线弧和线段必须共享一个端点。在约束切线之前,使用约束 -= 点约束它们。"
+
+#: constraint.cpp:659
+msgid ""
+"The tangent cubic and line segment must share an endpoint. Constrain them "
+"with Constrain -> On Point before constraining tangent."
+msgstr ""
+"切线立方段和线段必须共享终结点。在约束切线之前,使用约束 -= 点约束它们。"
+
+#: constraint.cpp:669
+msgid "Curve-curve tangency must apply in workplane."
+msgstr "曲线曲线切线必须应用于工作平面。"
+
+#: constraint.cpp:687
+msgid ""
+"The curves must share an endpoint. Constrain them with Constrain -> On Point "
+"before constraining tangent."
+msgstr "曲线必须共享一个终结点。在约束切线之前,使用约束 -= 点约束它们。"
+
+#: constraint.cpp:696
+msgid ""
+"Bad selection for parallel / tangent constraint. This constraint can apply "
+"to:\n"
+"\n"
+" * two line segments (parallel)\n"
+" * a line segment and a normal (parallel)\n"
+" * two normals (parallel)\n"
+" * two line segments, arcs, or beziers, that share an endpoint (tangent)\n"
+msgstr ""
+"平行/切线约束的选择错误。此约束可应用于:\n"
+"\n"
+"* 两条线段(平行)\n"
+" * 线段和法线(平行)\n"
+" * 两个法线(平行)\n"
+" * 共享端点的两条线段、弧线或贝塞尔(切线)\n"
+
+#: constraint.cpp:714
+msgid ""
+"Bad selection for perpendicular constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+"垂直约束的选择错误。此约束可应用于:\n"
+"\n"
+"* 两个线段\n"
+" * 线段和法线\n"
+" • 两个法线\n"
+
+#: constraint.cpp:729
+msgid ""
+"Bad selection for lock point where dragged constraint. This constraint can "
+"apply to:\n"
+"\n"
+" * a point\n"
+msgstr ""
+"拖动约束的锁点选择错误。此约束可应用于:\n"
+"\n"
+"• 一点\n"
+
+#: constraint.cpp:740
+msgid "click center of comment text"
+msgstr "单击注释文本的中心"
+
+#: export.cpp:19
+msgid ""
+"No solid model present; draw one with extrudes and revolves, or use Export "
+"2d View to export bare lines and curves."
+msgstr ""
+"不存在实体模型;使用拉伸和旋转绘制一个,或使用导出 2d 视图导出裸线和曲线。"
+
+#: export.cpp:61
+msgid ""
+"Bad selection for export section. Please select:\n"
+"\n"
+" * nothing, with an active workplane (workplane is section plane)\n"
+" * a face (section plane through face)\n"
+" * a point and two line segments (plane through point and parallel to "
+"lines)\n"
+msgstr ""
+"导出部分的选择错误。请选择:\n"
+"\n"
+"* 无,带活动工作平面(工作平面为剖面平面)\n"
+" * 脸(通过面的剖面)\n"
+" * 一个点和两个线段(平面穿过点和平行线)\n"
+
+#: export.cpp:822
+msgid "Active group mesh is empty; nothing to export."
+msgstr "活动组网格为空;没有要导出的。"
+
+#: exportvector.cpp:337
+msgid "freehand lines were replaced with continuous lines"
+msgstr "徒手线替换为连续线"
+
+#: exportvector.cpp:339
+msgid "zigzag lines were replaced with continuous lines"
+msgstr "锯齿线替换为连续线"
+
+#: exportvector.cpp:593
+msgid ""
+"Some aspects of the drawing have no DXF equivalent and were not exported:\n"
+msgstr "绘图的某些方面没有 DXF 等效项,并且未导出:\n"
+
+#: exportvector.cpp:839
+msgid ""
+"PDF page size exceeds 200 by 200 inches; many viewers may reject this file."
+msgstr "PDF 页面大小超过 200 英寸或 200 英寸;许多查看器可能会拒绝此文件。"
+
+#: file.cpp:44 group.cpp:91
+msgctxt "group-name"
+msgid "sketch-in-plane"
+msgstr "平面草图"
+
+#: file.cpp:62
+msgctxt "group-name"
+msgid "#references"
+msgstr "#参考"
+
+#: file.cpp:549
+msgid ""
+"Unrecognized data in file. This file may be corrupt, or from a newer version "
+"of the program."
+msgstr "未识别文件内数据。该文件可能损坏,或使用新版本应用程序尝试打开。"
+
+#: file.cpp:859
+msgctxt "title"
+msgid "Missing File"
+msgstr "文件丢失"
+
+#: file.cpp:860
+#, c-format
+msgctxt "dialog"
+msgid "The linked file “%s” is not present."
+msgstr "连接的文件不存在:\"%s\"。"
+
+#: file.cpp:862
+msgctxt "dialog"
+msgid ""
+"Do you want to locate it manually?\n"
+"\n"
+"If you decline, any geometry that depends on the missing file will be "
+"permanently removed."
+msgstr "您是否想手工查找?如果是,所有基于该丢失文件的几何对象将会被全部删除."
+
+#: file.cpp:865
+msgctxt "button"
+msgid "&Yes"
+msgstr "是(&Y)"
+
+#: file.cpp:867
+msgctxt "button"
+msgid "&No"
+msgstr "否(&N)"
+
+#: file.cpp:869
+msgctxt "button"
+msgid "&Cancel"
+msgstr "取消(&C)"
+
+#: graphicswin.cpp:41
+msgid "&File"
+msgstr "文件(&F)"
+
+#: graphicswin.cpp:42
+msgid "&New"
+msgstr "新建(&N)"
+
+#: graphicswin.cpp:43
+msgid "&Open..."
+msgstr "打开(&O)"
+
+#: graphicswin.cpp:44
+msgid "Open &Recent"
+msgstr "打开最近使用(&R)"
+
+#: graphicswin.cpp:45
+msgid "&Save"
+msgstr "保存(&S)"
+
+#: graphicswin.cpp:46
+msgid "Save &As..."
+msgstr "另存为(&A)"
+
+#: graphicswin.cpp:48
+msgid "Export &Image..."
+msgstr "导出图片(&I)"
+
+#: graphicswin.cpp:49
+msgid "Export 2d &View..."
+msgstr "导出2D视图(&V)"
+
+#: graphicswin.cpp:50
+msgid "Export 2d &Section..."
+msgstr "导出2D截面(&S)"
+
+#: graphicswin.cpp:51
+msgid "Export 3d &Wireframe..."
+msgstr "导出3D线框模型(&W)"
+
+#: graphicswin.cpp:52
+msgid "Export Triangle &Mesh..."
+msgstr "导出三角面模型(&M)"
+
+#: graphicswin.cpp:53
+msgid "Export &Surfaces..."
+msgstr "导出表面模型(&S)"
+
+#: graphicswin.cpp:54
+msgid "Im&port..."
+msgstr "导入(&P)"
+
+#: graphicswin.cpp:57
+msgid "E&xit"
+msgstr "退出(&E)"
+
+#: graphicswin.cpp:60
+msgid "&Edit"
+msgstr "编辑(&E)"
+
+#: graphicswin.cpp:61
+msgid "&Undo"
+msgstr "回退(&U)"
+
+#: graphicswin.cpp:62
+msgid "&Redo"
+msgstr "重做(&R)"
+
+#: graphicswin.cpp:63
+msgid "Re&generate All"
+msgstr "重新生成所有(&G)"
+
+#: graphicswin.cpp:65
+msgid "Snap Selection to &Grid"
+msgstr "选择到轴线(&G)"
+
+#: graphicswin.cpp:66
+msgid "Rotate Imported &90°"
+msgstr "旋转导入模型90° (&9)"
+
+#: graphicswin.cpp:68
+msgid "Cu&t"
+msgstr "剪切 (&T)"
+
+#: graphicswin.cpp:69
+msgid "&Copy"
+msgstr "复制 (&C)"
+
+#: graphicswin.cpp:70
+msgid "&Paste"
+msgstr "粘贴 (&P)"
+
+#: graphicswin.cpp:71
+msgid "Paste &Transformed..."
+msgstr "粘贴移动(&T)"
+
+#: graphicswin.cpp:72
+msgid "&Delete"
+msgstr "删除(&D)"
+
+#: graphicswin.cpp:74
+msgid "Select &Edge Chain"
+msgstr "选择边缘约束(&E)"
+
+#: graphicswin.cpp:75
+msgid "Select &All"
+msgstr "选择所有(&A)"
+
+#: graphicswin.cpp:76
+msgid "&Unselect All"
+msgstr "取消全选(&U)"
+
+#: graphicswin.cpp:78
+msgid "&Line Styles..."
+msgstr "线型(&L)"
+
+#: graphicswin.cpp:79
+msgid "&View Projection..."
+msgstr "查看投影...(&V)"
+
+#: graphicswin.cpp:81
+msgid "Con&figuration..."
+msgstr "配置 (&F)"
+
+#: graphicswin.cpp:84
+msgid "&View"
+msgstr "查看 (&V)"
+
+#: graphicswin.cpp:85
+msgid "Zoom &In"
+msgstr "放大(&I)"
+
+#: graphicswin.cpp:86
+msgid "Zoom &Out"
+msgstr "缩小(&O)"
+
+#: graphicswin.cpp:87
+msgid "Zoom To &Fit"
+msgstr "自动缩放(&F)"
+
+#: graphicswin.cpp:89
+msgid "Align View to &Workplane"
+msgstr "切换视图至平面(&W)"
+
+#: graphicswin.cpp:90
+msgid "Nearest &Ortho View"
+msgstr "Ortho视图 (&O)"
+
+#: graphicswin.cpp:91
+msgid "Nearest &Isometric View"
+msgstr "ISO视图 (&I)"
+
+#: graphicswin.cpp:92
+msgid "&Center View At Point"
+msgstr "以点为中心视图 (&C)"
+
+#: graphicswin.cpp:94
+msgid "Show Snap &Grid"
+msgstr "显示捕捉轴线 (&G)"
+
+#: graphicswin.cpp:95
+msgid "Use &Perspective Projection"
+msgstr "使用远景透视(&P)"
+
+#: graphicswin.cpp:96
+msgid "Dimension &Units"
+msgstr "标注单位(&U)"
+
+#: graphicswin.cpp:97
+msgid "Dimensions in &Millimeters"
+msgstr "标注单位 mm (&M)"
+
+#: graphicswin.cpp:98
+msgid "Dimensions in M&eters"
+msgstr "标注单位m (&E)"
+
+#: graphicswin.cpp:99
+msgid "Dimensions in &Inches"
+msgstr "标准单位英寸 (&I)"
+
+#: graphicswin.cpp:101
+msgid "Show &Toolbar"
+msgstr "显示工具条(&T)"
+
+#: graphicswin.cpp:102
+msgid "Show Property Bro&wser"
+msgstr "显示属性浏览器(&W)"
+
+#: graphicswin.cpp:104
+msgid "&Full Screen"
+msgstr "全屏(&F)"
+
+#: graphicswin.cpp:106
+msgid "&New Group"
+msgstr "新组合(&N)"
+
+#: graphicswin.cpp:107
+msgid "Sketch In &3d"
+msgstr "在三维内绘制(&3)"
+
+#: graphicswin.cpp:108
+msgid "Sketch In New &Workplane"
+msgstr "在新工作面绘制(&W)"
+
+#: graphicswin.cpp:110
+msgid "Step &Translating"
+msgstr "移动(&T)"
+
+#: graphicswin.cpp:111
+msgid "Step &Rotating"
+msgstr "旋转(&R)"
+
+#: graphicswin.cpp:113
+msgid "E&xtrude"
+msgstr "挤出(&E)"
+
+#: graphicswin.cpp:114
+msgid "&Helix"
+msgstr "螺旋(&H)"
+
+#: graphicswin.cpp:115
+msgid "&Lathe"
+msgstr "扫略(&L)"
+
+#: graphicswin.cpp:116
+msgid "Re&volve"
+msgstr "旋转(&V)"
+
+#: graphicswin.cpp:118
+msgid "Link / Assemble..."
+msgstr "链接/装配..."
+
+#: graphicswin.cpp:119
+msgid "Link Recent"
+msgstr "连接最近文件"
+
+#: graphicswin.cpp:121
+msgid "&Sketch"
+msgstr "绘图(&S)"
+
+#: graphicswin.cpp:122
+msgid "In &Workplane"
+msgstr "在工作平面(&W)"
+
+#: graphicswin.cpp:123
+msgid "Anywhere In &3d"
+msgstr "在3D的任何位置(&3)"
+
+#: graphicswin.cpp:125
+msgid "Datum &Point"
+msgstr "基准点(&P)"
+
+#: graphicswin.cpp:126
+msgid "&Workplane"
+msgstr "工作面(&W)"
+
+#: graphicswin.cpp:128
+msgid "Line &Segment"
+msgstr "线段(&S)"
+
+#: graphicswin.cpp:129
+msgid "C&onstruction Line Segment"
+msgstr "构造线段(&C)"
+
+#: graphicswin.cpp:130
+msgid "&Rectangle"
+msgstr "矩形(&R)"
+
+#: graphicswin.cpp:131
+msgid "&Circle"
+msgstr "圆线(&C)"
+
+#: graphicswin.cpp:132
+msgid "&Arc of a Circle"
+msgstr "圆弧(&A)"
+
+#: graphicswin.cpp:133
+msgid "&Bezier Cubic Spline"
+msgstr "立方体线的贝塞尔曲线(&B)"
+
+#: graphicswin.cpp:135
+msgid "&Text in TrueType Font"
+msgstr "TrueTyoe字体文字(&T)"
+
+#: graphicswin.cpp:136
+msgid "&Image"
+msgstr "图片(&I)"
+
+#: graphicswin.cpp:138
+msgid "To&ggle Construction"
+msgstr "切换构造(&G)"
+
+#: graphicswin.cpp:139
+msgid "Tangent &Arc at Point"
+msgstr "弧线切线点(&A)"
+
+#: graphicswin.cpp:140
+msgid "Split Curves at &Intersection"
+msgstr "在交叉处拆分曲线(&I)"
+
+#: graphicswin.cpp:142
+msgid "&Constrain"
+msgstr "约束(&C)"
+
+#: graphicswin.cpp:143
+msgid "&Distance / Diameter"
+msgstr "距离/直径(&D)"
+
+#: graphicswin.cpp:144
+msgid "Re&ference Dimension"
+msgstr "参考标注(&F)"
+
+#: graphicswin.cpp:145
+msgid "A&ngle"
+msgstr "角度(&A)"
+
+#: graphicswin.cpp:146
+msgid "Reference An&gle"
+msgstr "参考角度(&G)"
+
+#: graphicswin.cpp:147
+msgid "Other S&upplementary Angle"
+msgstr "其它增补角度(&U)"
+
+#: graphicswin.cpp:148
+msgid "Toggle R&eference Dim"
+msgstr "切换参考标注(&E)"
+
+#: graphicswin.cpp:150
+msgid "&Horizontal"
+msgstr "水平约束(&H)"
+
+#: graphicswin.cpp:151
+msgid "&Vertical"
+msgstr "垂直约束(&V)"
+
+#: graphicswin.cpp:153
+msgid "&On Point / Curve / Plane"
+msgstr "在点线面(&O)"
+
+#: graphicswin.cpp:154
+msgid "E&qual Length / Radius / Angle"
+msgstr "等于/长度/半径/角度(&Q)"
+
+#: graphicswin.cpp:155
+msgid "Length Ra&tio"
+msgstr "长度比例(&T)"
+
+#: graphicswin.cpp:156
+msgid "Length Diff&erence"
+msgstr "长度偏差(&E)"
+
+#: graphicswin.cpp:157
+msgid "At &Midpoint"
+msgstr "在中点(&M)"
+
+#: graphicswin.cpp:158
+msgid "S&ymmetric"
+msgstr "对称(&Y)"
+
+#: graphicswin.cpp:159
+msgid "Para&llel / Tangent"
+msgstr "水平/切线(&L)"
+
+#: graphicswin.cpp:160
+msgid "&Perpendicular"
+msgstr "垂直的(&P)"
+
+#: graphicswin.cpp:161
+msgid "Same Orient&ation"
+msgstr "相同方向(&A)"
+
+#: graphicswin.cpp:162
+msgid "Lock Point Where &Dragged"
+msgstr "锁定点位置(&D)"
+
+#: graphicswin.cpp:164
+msgid "Comment"
+msgstr "备注"
+
+#: graphicswin.cpp:166
+msgid "&Analyze"
+msgstr "分析(&A)"
+
+#: graphicswin.cpp:167
+msgid "Measure &Volume"
+msgstr "测量体积(&V)"
+
+#: graphicswin.cpp:168
+msgid "Measure A&rea"
+msgstr "测量面积(&R)"
+
+#: graphicswin.cpp:169
+msgid "Measure &Perimeter"
+msgstr "测量周长(&P)"
+
+#: graphicswin.cpp:170
+msgid "Show &Interfering Parts"
+msgstr "显示干涉零件(&I)"
+
+#: graphicswin.cpp:171
+msgid "Show &Naked Edges"
+msgstr "显示孤立边(&N)"
+
+#: graphicswin.cpp:172
+msgid "Show &Center of Mass"
+msgstr "显示中心(&C)"
+
+#: graphicswin.cpp:174
+msgid "Show &Underconstrained Points"
+msgstr "显示无效约束点(&U)"
+
+#: graphicswin.cpp:176
+msgid "&Trace Point"
+msgstr "跟踪点(&T)"
+
+#: graphicswin.cpp:177
+msgid "&Stop Tracing..."
+msgstr "停止跟踪(&S)"
+
+#: graphicswin.cpp:178
+msgid "Step &Dimension..."
+msgstr "逐步标注(&D)"
+
+#: graphicswin.cpp:180
+msgid "&Help"
+msgstr "帮助(&H)"
+
+#: graphicswin.cpp:181
+msgid "&Language"
+msgstr "语言(&L)"
+
+#: graphicswin.cpp:182
+msgid "&Website / Manual"
+msgstr "网页/手册(&W)"
+
+#: graphicswin.cpp:184
+msgid "&About"
+msgstr "关于(&A)"
+
+#: graphicswin.cpp:352
+msgid "(no recent files)"
+msgstr "(无文件)"
+
+#: graphicswin.cpp:360
+#, c-format
+msgid "File '%s' does not exist."
+msgstr "文件不存在: \"%s\"。"
+
+#: graphicswin.cpp:721
+msgid "No workplane is active, so the grid will not appear."
+msgstr "没有激活的工作面,因此无法显示轴网。"
+
+#: graphicswin.cpp:730
+msgid ""
+"The perspective factor is set to zero, so the view will always be a parallel "
+"projection.\n"
+"\n"
+"For a perspective projection, modify the perspective factor in the "
+"configuration screen. A value around 0.3 is typical."
+msgstr ""
+
+#: graphicswin.cpp:809
+msgid ""
+"Select a point; this point will become the center of the view on screen."
+msgstr ""
+
+#: graphicswin.cpp:1103
+msgid "No additional entities share endpoints with the selected entities."
+msgstr ""
+
+#: graphicswin.cpp:1121
+msgid ""
+"To use this command, select a point or other entity from an linked part, or "
+"make a link group the active group."
+msgstr ""
+
+#: graphicswin.cpp:1144
+msgid ""
+"No workplane is active. Activate a workplane (with Sketch -> In Workplane) "
+"to define the plane for the snap grid."
+msgstr ""
+
+#: graphicswin.cpp:1151
+msgid ""
+"Can't snap these items to grid; select points, text comments, or constraints "
+"with a label. To snap a line, select its endpoints."
+msgstr ""
+
+#: graphicswin.cpp:1239
+msgid "No workplane selected. Activating default workplane for this group."
+msgstr ""
+
+#: graphicswin.cpp:1242
+msgid ""
+"No workplane is selected, and the active group does not have a default "
+"workplane. Try selecting a workplane, or activating a sketch-in-new-"
+"workplane group."
+msgstr ""
+
+#: graphicswin.cpp:1263
+msgid ""
+"Bad selection for tangent arc at point. Select a single point, or select "
+"nothing to set up arc parameters."
+msgstr ""
+
+#: graphicswin.cpp:1274
+msgid "click point on arc (draws anti-clockwise)"
+msgstr "点击弧线的点(逆时针方向绘制)"
+
+#: graphicswin.cpp:1275
+msgid "click to place datum point"
+msgstr "点击放置基准点"
+
+#: graphicswin.cpp:1276
+msgid "click first point of line segment"
+msgstr "点击线条的起点"
+
+#: graphicswin.cpp:1278
+msgid "click first point of construction line segment"
+msgstr "点击构造线的起点"
+
+#: graphicswin.cpp:1279
+msgid "click first point of cubic segment"
+msgstr "点击立方体的起点"
+
+#: graphicswin.cpp:1280
+msgid "click center of circle"
+msgstr "点击圆弧的中心"
+
+#: graphicswin.cpp:1281
+msgid "click origin of workplane"
+msgstr "点击工作面的原点"
+
+#: graphicswin.cpp:1282
+msgid "click one corner of rectangle"
+msgstr "点击一个矩形倒角"
+
+#: graphicswin.cpp:1283
+msgid "click top left of text"
+msgstr "点击文字左上角"
+
+#: graphicswin.cpp:1289
+msgid "click top left of image"
+msgstr "点击图片左上角"
+
+#: graphicswin.cpp:1301
+msgid ""
+"No entities are selected. Select entities before trying to toggle their "
+"construction state."
+msgstr "为选中实体,切换构造状态前请先选中实体对象。"
+
+#: group.cpp:86
+msgctxt "group-name"
+msgid "sketch-in-3d"
+msgstr "3D草图"
+
+#: group.cpp:142
+msgid ""
+"Bad selection for new sketch in workplane. This group can be created with:\n"
+"\n"
+" * a point (through the point, orthogonal to coordinate axes)\n"
+" * a point and two line segments (through the point, parallel to the "
+"lines)\n"
+" * a workplane (copy of the workplane)\n"
+msgstr ""
+"在新工作面内绘图选择失败,该组可以使用:\n"
+"\n"
+" * 一个点(通过该点,正交至坐标轴)\n"
+" * 一个点和二个线段(通过点,绘制平行线至线段)\n"
+" * 一个工作面(复制工作面)\n"
+
+#: group.cpp:154
+msgid ""
+"Activate a workplane (Sketch -> In Workplane) before extruding. The sketch "
+"will be extruded normal to the workplane."
+msgstr "挤出前先激活工作面(草图->在工作面),该草图将由工作面的法线方向挤出。"
+
+#: group.cpp:163
+msgctxt "group-name"
+msgid "extrude"
+msgstr "挤出"
+
+#: group.cpp:168
+msgid "Lathe operation can only be applied to planar sketches."
+msgstr "扫略操作仅可用于二维草图。"
+
+#: group.cpp:179
+msgid ""
+"Bad selection for new lathe group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+"创建扫略组失败,该组可由:\n"
+"\n"
+" * 一个点和一个线段或法线(围绕坐标轴至线或法线的平行线,通过点)\n"
+" * 一个线段(围绕线段)\n"
+
+#: group.cpp:189
+msgctxt "group-name"
+msgid "lathe"
+msgstr "扫略"
+
+#: group.cpp:194
+msgid "Revolve operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:205
+msgid ""
+"Bad selection for new revolve group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:217
+msgctxt "group-name"
+msgid "revolve"
+msgstr "旋转"
+
+#: group.cpp:222
+msgid "Helix operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:233
+msgid ""
+"Bad selection for new helix group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel "
+"to line / normal, through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:245
+msgctxt "group-name"
+msgid "helix"
+msgstr "螺旋"
+
+#: group.cpp:258
+msgid ""
+"Bad selection for new rotation. This group can be created with:\n"
+"\n"
+" * a point, while locked in workplane (rotate in plane, about that "
+"point)\n"
+" * a point and a line or a normal (rotate about an axis through the "
+"point, and parallel to line / normal)\n"
+msgstr ""
+
+#: group.cpp:271
+msgctxt "group-name"
+msgid "rotate"
+msgstr "旋转"
+
+#: group.cpp:282
+msgctxt "group-name"
+msgid "translate"
+msgstr "移动"
+
+#: group.cpp:400
+msgid "(unnamed)"
+msgstr "(未命名)"
+
+#: groupmesh.cpp:708
+msgid "not closed contour, or not all same style!"
+msgstr "未闭合轮廓,或样式不一致!"
+
+#: groupmesh.cpp:721
+msgid "points not all coplanar!"
+msgstr "点不在相同平面!"
+
+#: groupmesh.cpp:723
+msgid "contour is self-intersecting!"
+msgstr "轮廓自相交!"
+
+#: groupmesh.cpp:725
+msgid "zero-length edge!"
+msgstr "边缘长度为零!"
+
+#: modify.cpp:254
+msgid "Must be sketching in workplane to create tangent arc."
+msgstr ""
+
+#: modify.cpp:301
+msgid ""
+"To create a tangent arc, select a point where two non-construction lines or "
+"circles in this group and workplane join."
+msgstr ""
+
+#: modify.cpp:388
+msgid ""
+"Couldn't round this corner. Try a smaller radius, or try creating the "
+"desired geometry by hand with tangency constraints."
+msgstr ""
+
+#: modify.cpp:597
+msgid "Couldn't split this entity; lines, circles, or cubics only."
+msgstr ""
+
+#: modify.cpp:624
+msgid "Must be sketching in workplane to split."
+msgstr ""
+
+#: modify.cpp:631
+msgid ""
+"Select two entities that intersect each other (e.g. two lines/circles/arcs "
+"or a line/circle/arc and a point)."
+msgstr ""
+
+#: modify.cpp:736
+msgid "Can't split; no intersection found."
+msgstr "无法拆分;未发现较差点。"
+
+#: mouse.cpp:560
+msgid "Assign to Style"
+msgstr "指定样式"
+
+#: mouse.cpp:576
+msgid "No Style"
+msgstr "无样式"
+
+#: mouse.cpp:579
+msgid "Newly Created Custom Style..."
+msgstr "新组样式。"
+
+#: mouse.cpp:586
+msgid "Group Info"
+msgstr "组信息"
+
+#: mouse.cpp:606
+msgid "Style Info"
+msgstr "样式信息"
+
+#: mouse.cpp:626
+msgid "Select Edge Chain"
+msgstr "选择边缘链"
+
+#: mouse.cpp:632
+msgid "Toggle Reference Dimension"
+msgstr "切换参考标注"
+
+#: mouse.cpp:638
+msgid "Other Supplementary Angle"
+msgstr "其它补充角度"
+
+#: mouse.cpp:643
+msgid "Snap to Grid"
+msgstr "捕捉至轴网"
+
+#: mouse.cpp:652
+msgid "Remove Spline Point"
+msgstr "删除样条线的点"
+
+#: mouse.cpp:687
+msgid "Add Spline Point"
+msgstr "增加样条线的点"
+
+#: mouse.cpp:691
+msgid "Cannot add spline point: maximum number of points reached."
+msgstr "无法增加样条线点:超过最大限制。"
+
+#: mouse.cpp:716
+msgid "Toggle Construction"
+msgstr "切换构造"
+
+#: mouse.cpp:731
+msgid "Delete Point-Coincident Constraint"
+msgstr "删除点一致约束"
+
+#: mouse.cpp:750
+msgid "Cut"
+msgstr "剪切"
+
+#: mouse.cpp:752
+msgid "Copy"
+msgstr "复制"
+
+#: mouse.cpp:756
+msgid "Select All"
+msgstr "全选"
+
+#: mouse.cpp:761
+msgid "Paste"
+msgstr "粘贴"
+
+#: mouse.cpp:763
+msgid "Paste Transformed..."
+msgstr "粘贴移动的..."
+
+#: mouse.cpp:768
+msgid "Delete"
+msgstr "删除"
+
+#: mouse.cpp:771
+msgid "Unselect All"
+msgstr "取消全选"
+
+#: mouse.cpp:778
+msgid "Unselect Hovered"
+msgstr "取消覆盖区域的全选"
+
+#: mouse.cpp:787
+msgid "Zoom to Fit"
+msgstr "自动缩放"
+
+#: mouse.cpp:990 mouse.cpp:1277
+msgid "click next point of line, or press Esc"
+msgstr "点击下一个点或取消(ESC)"
+
+#: mouse.cpp:996
+msgid ""
+"Can't draw rectangle in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr "无法在3D内绘制矩形; 首先,激活工作面,草图->在工作面。"
+
+#: mouse.cpp:1030
+msgid "click to place other corner of rectangle"
+msgstr "点击放置其它矩形倒角"
+
+#: mouse.cpp:1050
+msgid "click to set radius"
+msgstr "点击设置半径"
+
+#: mouse.cpp:1055
+msgid ""
+"Can't draw arc in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr "无法在3D空间内绘制弧线,可使用 草图->在工作面 激活工作面。"
+
+#: mouse.cpp:1074
+msgid "click to place point"
+msgstr "点击放置点"
+
+#: mouse.cpp:1090
+msgid "click next point of cubic, or press Esc"
+msgstr "点击下一个点或取消(ESC)"
+
+#: mouse.cpp:1095
+msgid ""
+"Sketching in a workplane already; sketch in 3d before creating new workplane."
+msgstr "已经在工作面绘制;在新建工作面前在三维空间绘制。"
+
+#: mouse.cpp:1111
+msgid ""
+"Can't draw text in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr "无法在三维空间内绘制文字,可使用 草图->在工作面 激活工作面。"
+
+#: mouse.cpp:1128
+msgid "click to place bottom right of text"
+msgstr "点击文字的右下角放置"
+
+#: mouse.cpp:1134
+msgid ""
+"Can't draw image in 3d; first, activate a workplane with Sketch -> In "
+"Workplane."
+msgstr "无法在三维空间内绘制图片,可使用 草图->在工作面 激活工作面。"
+
+#: mouse.cpp:1161
+msgid "NEW COMMENT -- DOUBLE-CLICK TO EDIT"
+msgstr "新备注 - 双击编辑"
+
+#: platform/gui.cpp:85 platform/gui.cpp:89
+msgctxt "file-type"
+msgid "SolveSpace models"
+msgstr "SolveSpace模型"
+
+#: platform/gui.cpp:90
+msgctxt "file-type"
+msgid "IDF circuit board"
+msgstr ""
+
+#: platform/gui.cpp:94
+msgctxt "file-type"
+msgid "PNG image"
+msgstr "PNG图片"
+
+#: platform/gui.cpp:98
+msgctxt "file-type"
+msgid "STL mesh"
+msgstr "STL网格"
+
+#: platform/gui.cpp:99
+msgctxt "file-type"
+msgid "Wavefront OBJ mesh"
+msgstr "Wavefront OBJ网格"
+
+#: platform/gui.cpp:100
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, with viewer"
+msgstr "Three.js-网格及查看视图"
+
+#: platform/gui.cpp:101
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, mesh only"
+msgstr "Three.js-仅网格"
+
+#: platform/gui.cpp:102
+msgctxt "file-type"
+msgid "Q3D Object file"
+msgstr "Q3D对象文件"
+
+#: platform/gui.cpp:103
+msgctxt "file-type"
+msgid "VRML text file"
+msgstr "VRML文本文件"
+
+#: platform/gui.cpp:107 platform/gui.cpp:114 platform/gui.cpp:121
+msgctxt "file-type"
+msgid "STEP file"
+msgstr "STEP文件"
+
+#: platform/gui.cpp:111
+msgctxt "file-type"
+msgid "PDF file"
+msgstr "PDF文件"
+
+#: platform/gui.cpp:112
+msgctxt "file-type"
+msgid "Encapsulated PostScript"
+msgstr "封装好的PostScript"
+
+#: platform/gui.cpp:113
+msgctxt "file-type"
+msgid "Scalable Vector Graphics"
+msgstr "SVG矢量图"
+
+#: platform/gui.cpp:115 platform/gui.cpp:122
+msgctxt "file-type"
+msgid "DXF file (AutoCAD 2007)"
+msgstr "DXF文件(AutoCAD 2007)"
+
+#: platform/gui.cpp:116
+msgctxt "file-type"
+msgid "HPGL file"
+msgstr "HPGL文件"
+
+#: platform/gui.cpp:117
+msgctxt "file-type"
+msgid "G Code"
+msgstr "G Code"
+
+#: platform/gui.cpp:126
+msgctxt "file-type"
+msgid "AutoCAD DXF and DWG files"
+msgstr "AutoCAD DXF/DWG文件"
+
+#: platform/gui.cpp:130
+msgctxt "file-type"
+msgid "Comma-separated values"
+msgstr "逗号分隔数据"
+
+#: platform/guigtk.cpp:1317 platform/guimac.mm:1360 platform/guiwin.cpp:1608
+msgid "untitled"
+msgstr "未命名"
+
+#: platform/guigtk.cpp:1328 platform/guigtk.cpp:1361 platform/guimac.mm:1318
+#: platform/guiwin.cpp:1555
+msgctxt "title"
+msgid "Save File"
+msgstr "保存文件"
+
+#: platform/guigtk.cpp:1329 platform/guigtk.cpp:1362 platform/guimac.mm:1301
+#: platform/guiwin.cpp:1557
+msgctxt "title"
+msgid "Open File"
+msgstr "打开文件"
+
+#: platform/guigtk.cpp:1332 platform/guigtk.cpp:1368
+msgctxt "button"
+msgid "_Cancel"
+msgstr "取消_C"
+
+#: platform/guigtk.cpp:1333 platform/guigtk.cpp:1366
+msgctxt "button"
+msgid "_Save"
+msgstr "保存_S"
+
+#: platform/guigtk.cpp:1334 platform/guigtk.cpp:1367
+msgctxt "button"
+msgid "_Open"
+msgstr "打开_O"
+
+#: style.cpp:166
+msgid ""
+"Can't assign style to an entity that's derived from another entity; try "
+"assigning a style to this entity's parent."
+msgstr "无法将样式分配给派生自其他实体的实体;尝试将样式分配给此实体的父级。"
+
+#: style.cpp:665
+msgid "Style name cannot be empty"
+msgstr "样式名称不能为空"
+
+#: textscreens.cpp:741
+msgid "Can't repeat fewer than 1 time."
+msgstr "不能重复少于 1 次。"
+
+#: textscreens.cpp:745
+msgid "Can't repeat more than 999 times."
+msgstr "重复不超过 999 次。"
+
+#: textscreens.cpp:770
+msgid "Group name cannot be empty"
+msgstr "组名称不能为空"
+
+#: textscreens.cpp:813
+msgid "Opacity must be between zero and one."
+msgstr "不透明度必须在零和 1 之间。"
+
+#: textscreens.cpp:848
+msgid "Radius cannot be zero or negative."
+msgstr "半径偏移不能为负数。"
+
+#: toolbar.cpp:18
+msgid "Sketch line segment"
+msgstr "草图线段"
+
+#: toolbar.cpp:20
+msgid "Sketch rectangle"
+msgstr "草图矩形"
+
+#: toolbar.cpp:22
+msgid "Sketch circle"
+msgstr "草图圆"
+
+#: toolbar.cpp:24
+msgid "Sketch arc of a circle"
+msgstr "圆的草图弧线"
+
+#: toolbar.cpp:26
+msgid "Sketch curves from text in a TrueType font"
+msgstr "从 TrueType 字体中的文本中绘制草图曲线"
+
+#: toolbar.cpp:28
+msgid "Sketch image from a file"
+msgstr "从文件中绘制图像"
+
+#: toolbar.cpp:30
+msgid "Create tangent arc at selected point"
+msgstr "在选定点创建切线弧"
+
+#: toolbar.cpp:32
+msgid "Sketch cubic Bezier spline"
+msgstr "草图立方贝塞尔样条"
+
+#: toolbar.cpp:34
+msgid "Sketch datum point"
+msgstr "草图基准点"
+
+#: toolbar.cpp:36
+msgid "Toggle construction"
+msgstr "切换结构"
+
+#: toolbar.cpp:38
+msgid "Split lines / curves where they intersect"
+msgstr "相交的分割线/曲线"
+
+#: toolbar.cpp:42
+msgid "Constrain distance / diameter / length"
+msgstr "约束距离/直径/长度"
+
+#: toolbar.cpp:44
+msgid "Constrain angle"
+msgstr "约束角度"
+
+#: toolbar.cpp:46
+msgid "Constrain to be horizontal"
+msgstr "约束为水平"
+
+#: toolbar.cpp:48
+msgid "Constrain to be vertical"
+msgstr "约束为垂直"
+
+#: toolbar.cpp:50
+msgid "Constrain to be parallel or tangent"
+msgstr "约束为平行或切线"
+
+#: toolbar.cpp:52
+msgid "Constrain to be perpendicular"
+msgstr "约束至垂直"
+
+#: toolbar.cpp:54
+msgid "Constrain point on line / curve / plane / point"
+msgstr "约束点至线/曲线/平面/点"
+
+#: toolbar.cpp:56
+msgid "Constrain symmetric"
+msgstr "对称约束"
+
+#: toolbar.cpp:58
+msgid "Constrain equal length / radius / angle"
+msgstr "约束长/半径/角度相等"
+
+#: toolbar.cpp:60
+msgid "Constrain normals in same orientation"
+msgstr "约束法线在同原点"
+
+#: toolbar.cpp:62
+msgid "Other supplementary angle"
+msgstr "其它补充角度"
+
+#: toolbar.cpp:64
+msgid "Toggle reference dimension"
+msgstr "切换参考标注"
+
+#: toolbar.cpp:68
+msgid "New group extruding active sketch"
+msgstr "新组中挤出当前草图"
+
+#: toolbar.cpp:70
+msgid "New group rotating active sketch"
+msgstr "新组中旋转体当前草图"
+
+#: toolbar.cpp:72
+msgid "New group step and repeat rotating"
+msgstr "新组中逐步重复旋转体"
+
+#: toolbar.cpp:74
+msgid "New group step and repeat translating"
+msgstr "新组中逐步重复移动体"
+
+#: toolbar.cpp:76
+msgid "New group in new workplane (thru given entities)"
+msgstr "在新工作平面创建组(通过指定对象)"
+
+#: toolbar.cpp:78
+msgid "New group in 3d"
+msgstr "在3D中新建组"
+
+#: toolbar.cpp:80
+msgid "New group linking / assembling file"
+msgstr "新组 连接/装配文件"
+
+#: toolbar.cpp:84
+msgid "Nearest isometric view"
+msgstr "ISO视图"
+
+#: toolbar.cpp:86
+msgid "Align view to active workplane"
+msgstr "切换视图至当前工作面"
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Error"
+msgstr "错误"
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Message"
+msgstr "消息"
+
+#: util.cpp:170
+msgctxt "button"
+msgid "&OK"
+msgstr "&OK"
+
+#: view.cpp:78
+msgid "Scale cannot be zero or negative."
+msgstr "缩放不能为零。"
+
+#: view.cpp:90 view.cpp:99
+msgid "Bad format: specify x, y, z"
+msgstr "格式错误: 需指定 x, y, z"
--- /dev/null
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR the PACKAGE authors
+# This file is distributed under the same license as the SolveSpace package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: SolveSpace 3.0\n"
+"Report-Msgid-Bugs-To: whitequark@whitequark.org\n"
+"POT-Creation-Date: 2020-11-17 20:50-0500\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: clipboard.cpp:274
+msgid ""
+"Cut, paste, and copy work only in a workplane.\n"
+"\n"
+"Activate one with Sketch -> In Workplane."
+msgstr ""
+
+#: clipboard.cpp:291
+msgid "Clipboard is empty; nothing to paste."
+msgstr ""
+
+#: clipboard.cpp:338
+msgid "Number of copies to paste must be at least one."
+msgstr ""
+
+#: clipboard.cpp:354 textscreens.cpp:783
+msgid "Scale cannot be zero."
+msgstr ""
+
+#: clipboard.cpp:396
+msgid "Select one point to define origin of rotation."
+msgstr ""
+
+#: clipboard.cpp:408
+msgid "Select two points to define translation vector."
+msgstr ""
+
+#: clipboard.cpp:418
+msgid "Transformation is identity. So all copies will be exactly on top of each other."
+msgstr ""
+
+#: clipboard.cpp:422
+msgid "Too many items to paste; split this into smaller pastes."
+msgstr ""
+
+#: clipboard.cpp:427
+msgid "No workplane active."
+msgstr ""
+
+#: confscreen.cpp:410
+msgid "Bad format: specify coordinates as x, y, z"
+msgstr ""
+
+#: confscreen.cpp:420 style.cpp:659 textscreens.cpp:805
+msgid "Bad format: specify color as r, g, b"
+msgstr ""
+
+#: confscreen.cpp:446
+msgid ""
+"The perspective factor will have no effect until you enable View -> Use Perspective Projection."
+msgstr ""
+
+#: confscreen.cpp:459 confscreen.cpp:469
+#, c-format
+msgid "Specify between 0 and %d digits after the decimal."
+msgstr ""
+
+#: confscreen.cpp:481
+msgid "Export scale must not be zero!"
+msgstr ""
+
+#: confscreen.cpp:493
+msgid "Cutter radius offset must not be negative!"
+msgstr ""
+
+#: confscreen.cpp:547
+msgid "Bad value: autosave interval should be positive"
+msgstr ""
+
+#: confscreen.cpp:550
+msgid "Bad format: specify interval in integral minutes"
+msgstr ""
+
+#: constraint.cpp:12
+msgctxt "constr-name"
+msgid "pts-coincident"
+msgstr ""
+
+#: constraint.cpp:13
+msgctxt "constr-name"
+msgid "pt-pt-distance"
+msgstr ""
+
+#: constraint.cpp:14
+msgctxt "constr-name"
+msgid "pt-line-distance"
+msgstr ""
+
+#: constraint.cpp:15
+msgctxt "constr-name"
+msgid "pt-plane-distance"
+msgstr ""
+
+#: constraint.cpp:16
+msgctxt "constr-name"
+msgid "pt-face-distance"
+msgstr ""
+
+#: constraint.cpp:17
+msgctxt "constr-name"
+msgid "proj-pt-pt-distance"
+msgstr ""
+
+#: constraint.cpp:18
+msgctxt "constr-name"
+msgid "pt-in-plane"
+msgstr ""
+
+#: constraint.cpp:19
+msgctxt "constr-name"
+msgid "pt-on-line"
+msgstr ""
+
+#: constraint.cpp:20
+msgctxt "constr-name"
+msgid "pt-on-face"
+msgstr ""
+
+#: constraint.cpp:21
+msgctxt "constr-name"
+msgid "eq-length"
+msgstr ""
+
+#: constraint.cpp:22
+msgctxt "constr-name"
+msgid "eq-length-and-pt-ln-dist"
+msgstr ""
+
+#: constraint.cpp:23
+msgctxt "constr-name"
+msgid "eq-pt-line-distances"
+msgstr ""
+
+#: constraint.cpp:24
+msgctxt "constr-name"
+msgid "length-ratio"
+msgstr ""
+
+#: constraint.cpp:25
+msgctxt "constr-name"
+msgid "length-difference"
+msgstr ""
+
+#: constraint.cpp:26
+msgctxt "constr-name"
+msgid "symmetric"
+msgstr ""
+
+#: constraint.cpp:27
+msgctxt "constr-name"
+msgid "symmetric-h"
+msgstr ""
+
+#: constraint.cpp:28
+msgctxt "constr-name"
+msgid "symmetric-v"
+msgstr ""
+
+#: constraint.cpp:29
+msgctxt "constr-name"
+msgid "symmetric-line"
+msgstr ""
+
+#: constraint.cpp:30
+msgctxt "constr-name"
+msgid "at-midpoint"
+msgstr ""
+
+#: constraint.cpp:31
+msgctxt "constr-name"
+msgid "horizontal"
+msgstr ""
+
+#: constraint.cpp:32
+msgctxt "constr-name"
+msgid "vertical"
+msgstr ""
+
+#: constraint.cpp:33
+msgctxt "constr-name"
+msgid "diameter"
+msgstr ""
+
+#: constraint.cpp:34
+msgctxt "constr-name"
+msgid "pt-on-circle"
+msgstr ""
+
+#: constraint.cpp:35
+msgctxt "constr-name"
+msgid "same-orientation"
+msgstr ""
+
+#: constraint.cpp:36
+msgctxt "constr-name"
+msgid "angle"
+msgstr ""
+
+#: constraint.cpp:37
+msgctxt "constr-name"
+msgid "parallel"
+msgstr ""
+
+#: constraint.cpp:38
+msgctxt "constr-name"
+msgid "arc-line-tangent"
+msgstr ""
+
+#: constraint.cpp:39
+msgctxt "constr-name"
+msgid "cubic-line-tangent"
+msgstr ""
+
+#: constraint.cpp:40
+msgctxt "constr-name"
+msgid "curve-curve-tangent"
+msgstr ""
+
+#: constraint.cpp:41
+msgctxt "constr-name"
+msgid "perpendicular"
+msgstr ""
+
+#: constraint.cpp:42
+msgctxt "constr-name"
+msgid "eq-radius"
+msgstr ""
+
+#: constraint.cpp:43
+msgctxt "constr-name"
+msgid "eq-angle"
+msgstr ""
+
+#: constraint.cpp:44
+msgctxt "constr-name"
+msgid "eq-line-len-arc-len"
+msgstr ""
+
+#: constraint.cpp:45
+msgctxt "constr-name"
+msgid "lock-where-dragged"
+msgstr ""
+
+#: constraint.cpp:46
+msgctxt "constr-name"
+msgid "comment"
+msgstr ""
+
+#: constraint.cpp:171
+msgid ""
+"Bad selection for distance / diameter constraint. This constraint can apply to:\n"
+"\n"
+" * two points (distance between points)\n"
+" * a line segment (length)\n"
+" * two points and a line segment or normal (projected distance)\n"
+" * a workplane and a point (minimum distance)\n"
+" * a line segment and a point (minimum distance)\n"
+" * a plane face and a point (minimum distance)\n"
+" * a circle or an arc (diameter)\n"
+msgstr ""
+
+#: constraint.cpp:224
+msgid ""
+"Bad selection for on point / curve / plane constraint. This constraint can apply to:\n"
+"\n"
+" * two points (points coincident)\n"
+" * a point and a workplane (point in plane)\n"
+" * a point and a line segment (point on line)\n"
+" * a point and a circle or arc (point on curve)\n"
+" * a point and a plane face (point on face)\n"
+msgstr ""
+
+#: constraint.cpp:286
+msgid ""
+"Bad selection for equal length / radius constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments (equal length)\n"
+" * two line segments and two points (equal point-line distances)\n"
+" * a line segment and two points (equal point-line distances)\n"
+" * a line segment, and a point and line segment (point-line distance equals length)\n"
+" * four line segments or normals (equal angle between A,B and C,D)\n"
+" * three line segments or normals (equal angle between A,B and B,C)\n"
+" * two circles or arcs (equal radius)\n"
+" * a line segment and an arc (line segment length equals arc length)\n"
+msgstr ""
+
+#: constraint.cpp:325
+msgid ""
+"Bad selection for length ratio constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+
+#: constraint.cpp:342
+msgid ""
+"Bad selection for length difference constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+msgstr ""
+
+#: constraint.cpp:368
+msgid ""
+"Bad selection for at midpoint constraint. This constraint can apply to:\n"
+"\n"
+" * a line segment and a point (point at midpoint)\n"
+" * a line segment and a workplane (line's midpoint on plane)\n"
+msgstr ""
+
+#: constraint.cpp:426
+msgid ""
+"Bad selection for symmetric constraint. This constraint can apply to:\n"
+"\n"
+" * two points or a line segment (symmetric about workplane's coordinate axis)\n"
+" * line segment, and two points or a line segment (symmetric about line segment)\n"
+" * workplane, and two points or a line segment (symmetric about workplane)\n"
+msgstr ""
+
+#: constraint.cpp:440
+msgid "A workplane must be active when constraining symmetric without an explicit symmetry plane."
+msgstr ""
+
+#: constraint.cpp:470
+msgid ""
+"Activate a workplane (with Sketch -> In Workplane) before applying a horizontal or vertical "
+"constraint."
+msgstr ""
+
+#: constraint.cpp:483
+msgid ""
+"Bad selection for horizontal / vertical constraint. This constraint can apply to:\n"
+"\n"
+" * two points\n"
+" * a line segment\n"
+msgstr ""
+
+#: constraint.cpp:504
+msgid ""
+"Bad selection for same orientation constraint. This constraint can apply to:\n"
+"\n"
+" * two normals\n"
+msgstr ""
+
+#: constraint.cpp:554
+msgid "Must select an angle constraint."
+msgstr ""
+
+#: constraint.cpp:567
+msgid "Must select a constraint with associated label."
+msgstr ""
+
+#: constraint.cpp:578
+msgid ""
+"Bad selection for angle constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+
+#: constraint.cpp:635
+msgid ""
+"The tangent arc and line segment must share an endpoint. Constrain them with Constrain -> On "
+"Point before constraining tangent."
+msgstr ""
+
+#: constraint.cpp:659
+msgid ""
+"The tangent cubic and line segment must share an endpoint. Constrain them with Constrain -> On "
+"Point before constraining tangent."
+msgstr ""
+
+#: constraint.cpp:669
+msgid "Curve-curve tangency must apply in workplane."
+msgstr ""
+
+#: constraint.cpp:687
+msgid ""
+"The curves must share an endpoint. Constrain them with Constrain -> On Point before constraining "
+"tangent."
+msgstr ""
+
+#: constraint.cpp:696
+msgid ""
+"Bad selection for parallel / tangent constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments (parallel)\n"
+" * a line segment and a normal (parallel)\n"
+" * two normals (parallel)\n"
+" * two line segments, arcs, or beziers, that share an endpoint (tangent)\n"
+msgstr ""
+
+#: constraint.cpp:714
+msgid ""
+"Bad selection for perpendicular constraint. This constraint can apply to:\n"
+"\n"
+" * two line segments\n"
+" * a line segment and a normal\n"
+" * two normals\n"
+msgstr ""
+
+#: constraint.cpp:729
+msgid ""
+"Bad selection for lock point where dragged constraint. This constraint can apply to:\n"
+"\n"
+" * a point\n"
+msgstr ""
+
+#: constraint.cpp:740
+msgid "click center of comment text"
+msgstr ""
+
+#: export.cpp:19
+msgid ""
+"No solid model present; draw one with extrudes and revolves, or use Export 2d View to export bare "
+"lines and curves."
+msgstr ""
+
+#: export.cpp:61
+msgid ""
+"Bad selection for export section. Please select:\n"
+"\n"
+" * nothing, with an active workplane (workplane is section plane)\n"
+" * a face (section plane through face)\n"
+" * a point and two line segments (plane through point and parallel to lines)\n"
+msgstr ""
+
+#: export.cpp:822
+msgid "Active group mesh is empty; nothing to export."
+msgstr ""
+
+#: exportvector.cpp:337
+msgid "freehand lines were replaced with continuous lines"
+msgstr ""
+
+#: exportvector.cpp:339
+msgid "zigzag lines were replaced with continuous lines"
+msgstr ""
+
+#: exportvector.cpp:593
+msgid "Some aspects of the drawing have no DXF equivalent and were not exported:\n"
+msgstr ""
+
+#: exportvector.cpp:839
+msgid "PDF page size exceeds 200 by 200 inches; many viewers may reject this file."
+msgstr ""
+
+#: file.cpp:44 group.cpp:91
+msgctxt "group-name"
+msgid "sketch-in-plane"
+msgstr ""
+
+#: file.cpp:62
+msgctxt "group-name"
+msgid "#references"
+msgstr ""
+
+#: file.cpp:549
+msgid "Unrecognized data in file. This file may be corrupt, or from a newer version of the program."
+msgstr ""
+
+#: file.cpp:859
+msgctxt "title"
+msgid "Missing File"
+msgstr ""
+
+#: file.cpp:860
+#, c-format
+msgctxt "dialog"
+msgid "The linked file “%s” is not present."
+msgstr ""
+
+#: file.cpp:862
+msgctxt "dialog"
+msgid ""
+"Do you want to locate it manually?\n"
+"\n"
+"If you decline, any geometry that depends on the missing file will be permanently removed."
+msgstr ""
+
+#: file.cpp:865
+msgctxt "button"
+msgid "&Yes"
+msgstr ""
+
+#: file.cpp:867
+msgctxt "button"
+msgid "&No"
+msgstr ""
+
+#: file.cpp:869
+msgctxt "button"
+msgid "&Cancel"
+msgstr ""
+
+#: graphicswin.cpp:41
+msgid "&File"
+msgstr ""
+
+#: graphicswin.cpp:42
+msgid "&New"
+msgstr ""
+
+#: graphicswin.cpp:43
+msgid "&Open..."
+msgstr ""
+
+#: graphicswin.cpp:44
+msgid "Open &Recent"
+msgstr ""
+
+#: graphicswin.cpp:45
+msgid "&Save"
+msgstr ""
+
+#: graphicswin.cpp:46
+msgid "Save &As..."
+msgstr ""
+
+#: graphicswin.cpp:48
+msgid "Export &Image..."
+msgstr ""
+
+#: graphicswin.cpp:49
+msgid "Export 2d &View..."
+msgstr ""
+
+#: graphicswin.cpp:50
+msgid "Export 2d &Section..."
+msgstr ""
+
+#: graphicswin.cpp:51
+msgid "Export 3d &Wireframe..."
+msgstr ""
+
+#: graphicswin.cpp:52
+msgid "Export Triangle &Mesh..."
+msgstr ""
+
+#: graphicswin.cpp:53
+msgid "Export &Surfaces..."
+msgstr ""
+
+#: graphicswin.cpp:54
+msgid "Im&port..."
+msgstr ""
+
+#: graphicswin.cpp:57
+msgid "E&xit"
+msgstr ""
+
+#: graphicswin.cpp:60
+msgid "&Edit"
+msgstr ""
+
+#: graphicswin.cpp:61
+msgid "&Undo"
+msgstr ""
+
+#: graphicswin.cpp:62
+msgid "&Redo"
+msgstr ""
+
+#: graphicswin.cpp:63
+msgid "Re&generate All"
+msgstr ""
+
+#: graphicswin.cpp:65
+msgid "Snap Selection to &Grid"
+msgstr ""
+
+#: graphicswin.cpp:66
+msgid "Rotate Imported &90°"
+msgstr ""
+
+#: graphicswin.cpp:68
+msgid "Cu&t"
+msgstr ""
+
+#: graphicswin.cpp:69
+msgid "&Copy"
+msgstr ""
+
+#: graphicswin.cpp:70
+msgid "&Paste"
+msgstr ""
+
+#: graphicswin.cpp:71
+msgid "Paste &Transformed..."
+msgstr ""
+
+#: graphicswin.cpp:72
+msgid "&Delete"
+msgstr ""
+
+#: graphicswin.cpp:74
+msgid "Select &Edge Chain"
+msgstr ""
+
+#: graphicswin.cpp:75
+msgid "Select &All"
+msgstr ""
+
+#: graphicswin.cpp:76
+msgid "&Unselect All"
+msgstr ""
+
+#: graphicswin.cpp:78
+msgid "&Line Styles..."
+msgstr ""
+
+#: graphicswin.cpp:79
+msgid "&View Projection..."
+msgstr ""
+
+#: graphicswin.cpp:81
+msgid "Con&figuration..."
+msgstr ""
+
+#: graphicswin.cpp:84
+msgid "&View"
+msgstr ""
+
+#: graphicswin.cpp:85
+msgid "Zoom &In"
+msgstr ""
+
+#: graphicswin.cpp:86
+msgid "Zoom &Out"
+msgstr ""
+
+#: graphicswin.cpp:87
+msgid "Zoom To &Fit"
+msgstr ""
+
+#: graphicswin.cpp:89
+msgid "Align View to &Workplane"
+msgstr ""
+
+#: graphicswin.cpp:90
+msgid "Nearest &Ortho View"
+msgstr ""
+
+#: graphicswin.cpp:91
+msgid "Nearest &Isometric View"
+msgstr ""
+
+#: graphicswin.cpp:92
+msgid "&Center View At Point"
+msgstr ""
+
+#: graphicswin.cpp:94
+msgid "Show Snap &Grid"
+msgstr ""
+
+#: graphicswin.cpp:95
+msgid "Use &Perspective Projection"
+msgstr ""
+
+#: graphicswin.cpp:96
+msgid "Dimension &Units"
+msgstr ""
+
+#: graphicswin.cpp:97
+msgid "Dimensions in &Millimeters"
+msgstr ""
+
+#: graphicswin.cpp:98
+msgid "Dimensions in M&eters"
+msgstr ""
+
+#: graphicswin.cpp:99
+msgid "Dimensions in &Inches"
+msgstr ""
+
+#: graphicswin.cpp:101
+msgid "Show &Toolbar"
+msgstr ""
+
+#: graphicswin.cpp:102
+msgid "Show Property Bro&wser"
+msgstr ""
+
+#: graphicswin.cpp:104
+msgid "&Full Screen"
+msgstr ""
+
+#: graphicswin.cpp:106
+msgid "&New Group"
+msgstr ""
+
+#: graphicswin.cpp:107
+msgid "Sketch In &3d"
+msgstr ""
+
+#: graphicswin.cpp:108
+msgid "Sketch In New &Workplane"
+msgstr ""
+
+#: graphicswin.cpp:110
+msgid "Step &Translating"
+msgstr ""
+
+#: graphicswin.cpp:111
+msgid "Step &Rotating"
+msgstr ""
+
+#: graphicswin.cpp:113
+msgid "E&xtrude"
+msgstr ""
+
+#: graphicswin.cpp:114
+msgid "&Helix"
+msgstr ""
+
+#: graphicswin.cpp:115
+msgid "&Lathe"
+msgstr ""
+
+#: graphicswin.cpp:116
+msgid "Re&volve"
+msgstr ""
+
+#: graphicswin.cpp:118
+msgid "Link / Assemble..."
+msgstr ""
+
+#: graphicswin.cpp:119
+msgid "Link Recent"
+msgstr ""
+
+#: graphicswin.cpp:121
+msgid "&Sketch"
+msgstr ""
+
+#: graphicswin.cpp:122
+msgid "In &Workplane"
+msgstr ""
+
+#: graphicswin.cpp:123
+msgid "Anywhere In &3d"
+msgstr ""
+
+#: graphicswin.cpp:125
+msgid "Datum &Point"
+msgstr ""
+
+#: graphicswin.cpp:126
+msgid "&Workplane"
+msgstr ""
+
+#: graphicswin.cpp:128
+msgid "Line &Segment"
+msgstr ""
+
+#: graphicswin.cpp:129
+msgid "C&onstruction Line Segment"
+msgstr ""
+
+#: graphicswin.cpp:130
+msgid "&Rectangle"
+msgstr ""
+
+#: graphicswin.cpp:131
+msgid "&Circle"
+msgstr ""
+
+#: graphicswin.cpp:132
+msgid "&Arc of a Circle"
+msgstr ""
+
+#: graphicswin.cpp:133
+msgid "&Bezier Cubic Spline"
+msgstr ""
+
+#: graphicswin.cpp:135
+msgid "&Text in TrueType Font"
+msgstr ""
+
+#: graphicswin.cpp:136
+msgid "&Image"
+msgstr ""
+
+#: graphicswin.cpp:138
+msgid "To&ggle Construction"
+msgstr ""
+
+#: graphicswin.cpp:139
+msgid "Tangent &Arc at Point"
+msgstr ""
+
+#: graphicswin.cpp:140
+msgid "Split Curves at &Intersection"
+msgstr ""
+
+#: graphicswin.cpp:142
+msgid "&Constrain"
+msgstr ""
+
+#: graphicswin.cpp:143
+msgid "&Distance / Diameter"
+msgstr ""
+
+#: graphicswin.cpp:144
+msgid "Re&ference Dimension"
+msgstr ""
+
+#: graphicswin.cpp:145
+msgid "A&ngle"
+msgstr ""
+
+#: graphicswin.cpp:146
+msgid "Reference An&gle"
+msgstr ""
+
+#: graphicswin.cpp:147
+msgid "Other S&upplementary Angle"
+msgstr ""
+
+#: graphicswin.cpp:148
+msgid "Toggle R&eference Dim"
+msgstr ""
+
+#: graphicswin.cpp:150
+msgid "&Horizontal"
+msgstr ""
+
+#: graphicswin.cpp:151
+msgid "&Vertical"
+msgstr ""
+
+#: graphicswin.cpp:153
+msgid "&On Point / Curve / Plane"
+msgstr ""
+
+#: graphicswin.cpp:154
+msgid "E&qual Length / Radius / Angle"
+msgstr ""
+
+#: graphicswin.cpp:155
+msgid "Length Ra&tio"
+msgstr ""
+
+#: graphicswin.cpp:156
+msgid "Length Diff&erence"
+msgstr ""
+
+#: graphicswin.cpp:157
+msgid "At &Midpoint"
+msgstr ""
+
+#: graphicswin.cpp:158
+msgid "S&ymmetric"
+msgstr ""
+
+#: graphicswin.cpp:159
+msgid "Para&llel / Tangent"
+msgstr ""
+
+#: graphicswin.cpp:160
+msgid "&Perpendicular"
+msgstr ""
+
+#: graphicswin.cpp:161
+msgid "Same Orient&ation"
+msgstr ""
+
+#: graphicswin.cpp:162
+msgid "Lock Point Where &Dragged"
+msgstr ""
+
+#: graphicswin.cpp:164
+msgid "Comment"
+msgstr ""
+
+#: graphicswin.cpp:166
+msgid "&Analyze"
+msgstr ""
+
+#: graphicswin.cpp:167
+msgid "Measure &Volume"
+msgstr ""
+
+#: graphicswin.cpp:168
+msgid "Measure A&rea"
+msgstr ""
+
+#: graphicswin.cpp:169
+msgid "Measure &Perimeter"
+msgstr ""
+
+#: graphicswin.cpp:170
+msgid "Show &Interfering Parts"
+msgstr ""
+
+#: graphicswin.cpp:171
+msgid "Show &Naked Edges"
+msgstr ""
+
+#: graphicswin.cpp:172
+msgid "Show &Center of Mass"
+msgstr ""
+
+#: graphicswin.cpp:174
+msgid "Show &Underconstrained Points"
+msgstr ""
+
+#: graphicswin.cpp:176
+msgid "&Trace Point"
+msgstr ""
+
+#: graphicswin.cpp:177
+msgid "&Stop Tracing..."
+msgstr ""
+
+#: graphicswin.cpp:178
+msgid "Step &Dimension..."
+msgstr ""
+
+#: graphicswin.cpp:180
+msgid "&Help"
+msgstr ""
+
+#: graphicswin.cpp:181
+msgid "&Language"
+msgstr ""
+
+#: graphicswin.cpp:182
+msgid "&Website / Manual"
+msgstr ""
+
+#: graphicswin.cpp:184
+msgid "&About"
+msgstr ""
+
+#: graphicswin.cpp:352
+msgid "(no recent files)"
+msgstr ""
+
+#: graphicswin.cpp:360
+#, c-format
+msgid "File '%s' does not exist."
+msgstr ""
+
+#: graphicswin.cpp:721
+msgid "No workplane is active, so the grid will not appear."
+msgstr ""
+
+#: graphicswin.cpp:730
+msgid ""
+"The perspective factor is set to zero, so the view will always be a parallel projection.\n"
+"\n"
+"For a perspective projection, modify the perspective factor in the configuration screen. A value "
+"around 0.3 is typical."
+msgstr ""
+
+#: graphicswin.cpp:809
+msgid "Select a point; this point will become the center of the view on screen."
+msgstr ""
+
+#: graphicswin.cpp:1103
+msgid "No additional entities share endpoints with the selected entities."
+msgstr ""
+
+#: graphicswin.cpp:1121
+msgid ""
+"To use this command, select a point or other entity from an linked part, or make a link group the "
+"active group."
+msgstr ""
+
+#: graphicswin.cpp:1144
+msgid ""
+"No workplane is active. Activate a workplane (with Sketch -> In Workplane) to define the plane "
+"for the snap grid."
+msgstr ""
+
+#: graphicswin.cpp:1151
+msgid ""
+"Can't snap these items to grid; select points, text comments, or constraints with a label. To "
+"snap a line, select its endpoints."
+msgstr ""
+
+#: graphicswin.cpp:1239
+msgid "No workplane selected. Activating default workplane for this group."
+msgstr ""
+
+#: graphicswin.cpp:1242
+msgid ""
+"No workplane is selected, and the active group does not have a default workplane. Try selecting a "
+"workplane, or activating a sketch-in-new-workplane group."
+msgstr ""
+
+#: graphicswin.cpp:1263
+msgid ""
+"Bad selection for tangent arc at point. Select a single point, or select nothing to set up arc "
+"parameters."
+msgstr ""
+
+#: graphicswin.cpp:1274
+msgid "click point on arc (draws anti-clockwise)"
+msgstr ""
+
+#: graphicswin.cpp:1275
+msgid "click to place datum point"
+msgstr ""
+
+#: graphicswin.cpp:1276
+msgid "click first point of line segment"
+msgstr ""
+
+#: graphicswin.cpp:1278
+msgid "click first point of construction line segment"
+msgstr ""
+
+#: graphicswin.cpp:1279
+msgid "click first point of cubic segment"
+msgstr ""
+
+#: graphicswin.cpp:1280
+msgid "click center of circle"
+msgstr ""
+
+#: graphicswin.cpp:1281
+msgid "click origin of workplane"
+msgstr ""
+
+#: graphicswin.cpp:1282
+msgid "click one corner of rectangle"
+msgstr ""
+
+#: graphicswin.cpp:1283
+msgid "click top left of text"
+msgstr ""
+
+#: graphicswin.cpp:1289
+msgid "click top left of image"
+msgstr ""
+
+#: graphicswin.cpp:1301
+msgid "No entities are selected. Select entities before trying to toggle their construction state."
+msgstr ""
+
+#: group.cpp:86
+msgctxt "group-name"
+msgid "sketch-in-3d"
+msgstr ""
+
+#: group.cpp:142
+msgid ""
+"Bad selection for new sketch in workplane. This group can be created with:\n"
+"\n"
+" * a point (through the point, orthogonal to coordinate axes)\n"
+" * a point and two line segments (through the point, parallel to the lines)\n"
+" * a workplane (copy of the workplane)\n"
+msgstr ""
+
+#: group.cpp:154
+msgid ""
+"Activate a workplane (Sketch -> In Workplane) before extruding. The sketch will be extruded "
+"normal to the workplane."
+msgstr ""
+
+#: group.cpp:163
+msgctxt "group-name"
+msgid "extrude"
+msgstr ""
+
+#: group.cpp:168
+msgid "Lathe operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:179
+msgid ""
+"Bad selection for new lathe group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel to line / normal, "
+"through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:189
+msgctxt "group-name"
+msgid "lathe"
+msgstr ""
+
+#: group.cpp:194
+msgid "Revolve operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:205
+msgid ""
+"Bad selection for new revolve group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel to line / normal, "
+"through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:217
+msgctxt "group-name"
+msgid "revolve"
+msgstr ""
+
+#: group.cpp:222
+msgid "Helix operation can only be applied to planar sketches."
+msgstr ""
+
+#: group.cpp:233
+msgid ""
+"Bad selection for new helix group. This group can be created with:\n"
+"\n"
+" * a point and a line segment or normal (revolved about an axis parallel to line / normal, "
+"through point)\n"
+" * a line segment (revolved about line segment)\n"
+msgstr ""
+
+#: group.cpp:245
+msgctxt "group-name"
+msgid "helix"
+msgstr ""
+
+#: group.cpp:258
+msgid ""
+"Bad selection for new rotation. This group can be created with:\n"
+"\n"
+" * a point, while locked in workplane (rotate in plane, about that point)\n"
+" * a point and a line or a normal (rotate about an axis through the point, and parallel to "
+"line / normal)\n"
+msgstr ""
+
+#: group.cpp:271
+msgctxt "group-name"
+msgid "rotate"
+msgstr ""
+
+#: group.cpp:282
+msgctxt "group-name"
+msgid "translate"
+msgstr ""
+
+#: group.cpp:400
+msgid "(unnamed)"
+msgstr ""
+
+#: groupmesh.cpp:708
+msgid "not closed contour, or not all same style!"
+msgstr ""
+
+#: groupmesh.cpp:721
+msgid "points not all coplanar!"
+msgstr ""
+
+#: groupmesh.cpp:723
+msgid "contour is self-intersecting!"
+msgstr ""
+
+#: groupmesh.cpp:725
+msgid "zero-length edge!"
+msgstr ""
+
+#: modify.cpp:254
+msgid "Must be sketching in workplane to create tangent arc."
+msgstr ""
+
+#: modify.cpp:301
+msgid ""
+"To create a tangent arc, select a point where two non-construction lines or circles in this group "
+"and workplane join."
+msgstr ""
+
+#: modify.cpp:388
+msgid ""
+"Couldn't round this corner. Try a smaller radius, or try creating the desired geometry by hand "
+"with tangency constraints."
+msgstr ""
+
+#: modify.cpp:597
+msgid "Couldn't split this entity; lines, circles, or cubics only."
+msgstr ""
+
+#: modify.cpp:624
+msgid "Must be sketching in workplane to split."
+msgstr ""
+
+#: modify.cpp:631
+msgid ""
+"Select two entities that intersect each other (e.g. two lines/circles/arcs or a line/circle/arc "
+"and a point)."
+msgstr ""
+
+#: modify.cpp:736
+msgid "Can't split; no intersection found."
+msgstr ""
+
+#: mouse.cpp:560
+msgid "Assign to Style"
+msgstr ""
+
+#: mouse.cpp:576
+msgid "No Style"
+msgstr ""
+
+#: mouse.cpp:579
+msgid "Newly Created Custom Style..."
+msgstr ""
+
+#: mouse.cpp:586
+msgid "Group Info"
+msgstr ""
+
+#: mouse.cpp:606
+msgid "Style Info"
+msgstr ""
+
+#: mouse.cpp:626
+msgid "Select Edge Chain"
+msgstr ""
+
+#: mouse.cpp:632
+msgid "Toggle Reference Dimension"
+msgstr ""
+
+#: mouse.cpp:638
+msgid "Other Supplementary Angle"
+msgstr ""
+
+#: mouse.cpp:643
+msgid "Snap to Grid"
+msgstr ""
+
+#: mouse.cpp:652
+msgid "Remove Spline Point"
+msgstr ""
+
+#: mouse.cpp:687
+msgid "Add Spline Point"
+msgstr ""
+
+#: mouse.cpp:691
+msgid "Cannot add spline point: maximum number of points reached."
+msgstr ""
+
+#: mouse.cpp:716
+msgid "Toggle Construction"
+msgstr ""
+
+#: mouse.cpp:731
+msgid "Delete Point-Coincident Constraint"
+msgstr ""
+
+#: mouse.cpp:750
+msgid "Cut"
+msgstr ""
+
+#: mouse.cpp:752
+msgid "Copy"
+msgstr ""
+
+#: mouse.cpp:756
+msgid "Select All"
+msgstr ""
+
+#: mouse.cpp:761
+msgid "Paste"
+msgstr ""
+
+#: mouse.cpp:763
+msgid "Paste Transformed..."
+msgstr ""
+
+#: mouse.cpp:768
+msgid "Delete"
+msgstr ""
+
+#: mouse.cpp:771
+msgid "Unselect All"
+msgstr ""
+
+#: mouse.cpp:778
+msgid "Unselect Hovered"
+msgstr ""
+
+#: mouse.cpp:787
+msgid "Zoom to Fit"
+msgstr ""
+
+#: mouse.cpp:990 mouse.cpp:1277
+msgid "click next point of line, or press Esc"
+msgstr ""
+
+#: mouse.cpp:996
+msgid "Can't draw rectangle in 3d; first, activate a workplane with Sketch -> In Workplane."
+msgstr ""
+
+#: mouse.cpp:1030
+msgid "click to place other corner of rectangle"
+msgstr ""
+
+#: mouse.cpp:1050
+msgid "click to set radius"
+msgstr ""
+
+#: mouse.cpp:1055
+msgid "Can't draw arc in 3d; first, activate a workplane with Sketch -> In Workplane."
+msgstr ""
+
+#: mouse.cpp:1074
+msgid "click to place point"
+msgstr ""
+
+#: mouse.cpp:1090
+msgid "click next point of cubic, or press Esc"
+msgstr ""
+
+#: mouse.cpp:1095
+msgid "Sketching in a workplane already; sketch in 3d before creating new workplane."
+msgstr ""
+
+#: mouse.cpp:1111
+msgid "Can't draw text in 3d; first, activate a workplane with Sketch -> In Workplane."
+msgstr ""
+
+#: mouse.cpp:1128
+msgid "click to place bottom right of text"
+msgstr ""
+
+#: mouse.cpp:1134
+msgid "Can't draw image in 3d; first, activate a workplane with Sketch -> In Workplane."
+msgstr ""
+
+#: mouse.cpp:1161
+msgid "NEW COMMENT -- DOUBLE-CLICK TO EDIT"
+msgstr ""
+
+#: platform/gui.cpp:85 platform/gui.cpp:89
+msgctxt "file-type"
+msgid "SolveSpace models"
+msgstr ""
+
+#: platform/gui.cpp:90
+msgctxt "file-type"
+msgid "IDF circuit board"
+msgstr ""
+
+#: platform/gui.cpp:94
+msgctxt "file-type"
+msgid "PNG image"
+msgstr ""
+
+#: platform/gui.cpp:98
+msgctxt "file-type"
+msgid "STL mesh"
+msgstr ""
+
+#: platform/gui.cpp:99
+msgctxt "file-type"
+msgid "Wavefront OBJ mesh"
+msgstr ""
+
+#: platform/gui.cpp:100
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, with viewer"
+msgstr ""
+
+#: platform/gui.cpp:101
+msgctxt "file-type"
+msgid "Three.js-compatible mesh, mesh only"
+msgstr ""
+
+#: platform/gui.cpp:102
+msgctxt "file-type"
+msgid "Q3D Object file"
+msgstr ""
+
+#: platform/gui.cpp:103
+msgctxt "file-type"
+msgid "VRML text file"
+msgstr ""
+
+#: platform/gui.cpp:107 platform/gui.cpp:114 platform/gui.cpp:121
+msgctxt "file-type"
+msgid "STEP file"
+msgstr ""
+
+#: platform/gui.cpp:111
+msgctxt "file-type"
+msgid "PDF file"
+msgstr ""
+
+#: platform/gui.cpp:112
+msgctxt "file-type"
+msgid "Encapsulated PostScript"
+msgstr ""
+
+#: platform/gui.cpp:113
+msgctxt "file-type"
+msgid "Scalable Vector Graphics"
+msgstr ""
+
+#: platform/gui.cpp:115 platform/gui.cpp:122
+msgctxt "file-type"
+msgid "DXF file (AutoCAD 2007)"
+msgstr ""
+
+#: platform/gui.cpp:116
+msgctxt "file-type"
+msgid "HPGL file"
+msgstr ""
+
+#: platform/gui.cpp:117
+msgctxt "file-type"
+msgid "G Code"
+msgstr ""
+
+#: platform/gui.cpp:126
+msgctxt "file-type"
+msgid "AutoCAD DXF and DWG files"
+msgstr ""
+
+#: platform/gui.cpp:130
+msgctxt "file-type"
+msgid "Comma-separated values"
+msgstr ""
+
+#: platform/guigtk.cpp:1317 platform/guimac.mm:1360 platform/guiwin.cpp:1608
+msgid "untitled"
+msgstr ""
+
+#: platform/guigtk.cpp:1328 platform/guigtk.cpp:1361 platform/guimac.mm:1318
+#: platform/guiwin.cpp:1555
+msgctxt "title"
+msgid "Save File"
+msgstr ""
+
+#: platform/guigtk.cpp:1329 platform/guigtk.cpp:1362 platform/guimac.mm:1301
+#: platform/guiwin.cpp:1557
+msgctxt "title"
+msgid "Open File"
+msgstr ""
+
+#: platform/guigtk.cpp:1332 platform/guigtk.cpp:1368
+msgctxt "button"
+msgid "_Cancel"
+msgstr ""
+
+#: platform/guigtk.cpp:1333 platform/guigtk.cpp:1366
+msgctxt "button"
+msgid "_Save"
+msgstr ""
+
+#: platform/guigtk.cpp:1334 platform/guigtk.cpp:1367
+msgctxt "button"
+msgid "_Open"
+msgstr ""
+
+#: style.cpp:166
+msgid ""
+"Can't assign style to an entity that's derived from another entity; try assigning a style to this "
+"entity's parent."
+msgstr ""
+
+#: style.cpp:665
+msgid "Style name cannot be empty"
+msgstr ""
+
+#: textscreens.cpp:741
+msgid "Can't repeat fewer than 1 time."
+msgstr ""
+
+#: textscreens.cpp:745
+msgid "Can't repeat more than 999 times."
+msgstr ""
+
+#: textscreens.cpp:770
+msgid "Group name cannot be empty"
+msgstr ""
+
+#: textscreens.cpp:813
+msgid "Opacity must be between zero and one."
+msgstr ""
+
+#: textscreens.cpp:848
+msgid "Radius cannot be zero or negative."
+msgstr ""
+
+#: toolbar.cpp:18
+msgid "Sketch line segment"
+msgstr ""
+
+#: toolbar.cpp:20
+msgid "Sketch rectangle"
+msgstr ""
+
+#: toolbar.cpp:22
+msgid "Sketch circle"
+msgstr ""
+
+#: toolbar.cpp:24
+msgid "Sketch arc of a circle"
+msgstr ""
+
+#: toolbar.cpp:26
+msgid "Sketch curves from text in a TrueType font"
+msgstr ""
+
+#: toolbar.cpp:28
+msgid "Sketch image from a file"
+msgstr ""
+
+#: toolbar.cpp:30
+msgid "Create tangent arc at selected point"
+msgstr ""
+
+#: toolbar.cpp:32
+msgid "Sketch cubic Bezier spline"
+msgstr ""
+
+#: toolbar.cpp:34
+msgid "Sketch datum point"
+msgstr ""
+
+#: toolbar.cpp:36
+msgid "Toggle construction"
+msgstr ""
+
+#: toolbar.cpp:38
+msgid "Split lines / curves where they intersect"
+msgstr ""
+
+#: toolbar.cpp:42
+msgid "Constrain distance / diameter / length"
+msgstr ""
+
+#: toolbar.cpp:44
+msgid "Constrain angle"
+msgstr ""
+
+#: toolbar.cpp:46
+msgid "Constrain to be horizontal"
+msgstr ""
+
+#: toolbar.cpp:48
+msgid "Constrain to be vertical"
+msgstr ""
+
+#: toolbar.cpp:50
+msgid "Constrain to be parallel or tangent"
+msgstr ""
+
+#: toolbar.cpp:52
+msgid "Constrain to be perpendicular"
+msgstr ""
+
+#: toolbar.cpp:54
+msgid "Constrain point on line / curve / plane / point"
+msgstr ""
+
+#: toolbar.cpp:56
+msgid "Constrain symmetric"
+msgstr ""
+
+#: toolbar.cpp:58
+msgid "Constrain equal length / radius / angle"
+msgstr ""
+
+#: toolbar.cpp:60
+msgid "Constrain normals in same orientation"
+msgstr ""
+
+#: toolbar.cpp:62
+msgid "Other supplementary angle"
+msgstr ""
+
+#: toolbar.cpp:64
+msgid "Toggle reference dimension"
+msgstr ""
+
+#: toolbar.cpp:68
+msgid "New group extruding active sketch"
+msgstr ""
+
+#: toolbar.cpp:70
+msgid "New group rotating active sketch"
+msgstr ""
+
+#: toolbar.cpp:72
+msgid "New group step and repeat rotating"
+msgstr ""
+
+#: toolbar.cpp:74
+msgid "New group step and repeat translating"
+msgstr ""
+
+#: toolbar.cpp:76
+msgid "New group in new workplane (thru given entities)"
+msgstr ""
+
+#: toolbar.cpp:78
+msgid "New group in 3d"
+msgstr ""
+
+#: toolbar.cpp:80
+msgid "New group linking / assembling file"
+msgstr ""
+
+#: toolbar.cpp:84
+msgid "Nearest isometric view"
+msgstr ""
+
+#: toolbar.cpp:86
+msgid "Align view to active workplane"
+msgstr ""
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Error"
+msgstr ""
+
+#: util.cpp:165
+msgctxt "title"
+msgid "Message"
+msgstr ""
+
+#: util.cpp:170
+msgctxt "button"
+msgid "&OK"
+msgstr ""
+
+#: view.cpp:78
+msgid "Scale cannot be zero or negative."
+msgstr ""
+
+#: view.cpp:90 view.cpp:99
+msgid "Bad format: specify x, y, z"
+msgstr ""
--- /dev/null
+//-----------------------------------------------------------------------------
+// Edge rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+const float feather = 0.5;
+
+uniform vec4 color;
+uniform float pixel;
+uniform float width;
+uniform float patternLen;
+uniform float patternScale;
+uniform sampler2D pattern;
+
+varying vec3 fragLoc;
+
+void main() {
+ // lookup distance texture
+ vec4 v = texture2D(pattern, vec2(fragLoc.z / patternScale, 0.0));
+
+ // decode distance value
+ float val = dot(v, vec4(1.0, 1.0 / 255.0, 1.0 / 65025.0, 1.0 / 160581375.0));
+
+ // calculate cap
+ float dist = length(vec2(val * patternScale / (patternLen * width) + abs(fragLoc.x), fragLoc.y));
+
+ // perform antialiasing
+ float k = smoothstep(1.0 - 2.0 * feather * pixel / (width + feather * pixel), 1.0, abs(dist));
+
+ // perform alpha-test
+ if(k == 1.0) discard;
+
+ // write resulting color
+ gl_FragColor = vec4(color.rgb, color.a * (1.0 - k));
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Edge rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+const float feather = 0.5;
+
+attribute vec3 pos;
+attribute vec3 loc;
+attribute vec3 tgt;
+
+uniform mat4 modelview;
+uniform mat4 projection;
+uniform float width;
+uniform float pixel;
+
+varying vec3 fragLoc;
+
+void main() {
+ // get camera direction from modelview matrix
+ vec3 dir = vec3(modelview[0].z, modelview[1].z, modelview[2].z);
+
+ // calculate line contour extension basis for constant width and caps
+ vec3 norm = normalize(cross(tgt, dir));
+ norm = normalize(norm - dir * dot(dir, norm));
+ vec3 perp = normalize(cross(dir, norm));
+
+ // calculate line extension width considering antialiasing
+ float ext = width + feather * pixel;
+
+ // extend line contour
+ vec3 vertex = pos;
+ vertex += ext * loc.x * normalize(perp);
+ vertex += ext * loc.y * normalize(norm);
+
+ // write fragment location for calculating caps and antialiasing
+ fragLoc = loc;
+
+ // transform resulting vertex with modelview and projection matrices
+ gl_Position = projection * modelview * vec4(vertex, 1.0);
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Indexed Mesh rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+uniform vec4 color;
+uniform sampler2D texture_;
+
+void main() {
+ if(texture2D(texture_, gl_FragCoord.xy / 32.0).a < 0.5) discard;
+ gl_FragColor = color;
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Indexed Mesh rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+attribute vec3 pos;
+
+uniform mat4 modelview;
+uniform mat4 projection;
+
+void main() {
+ gl_Position = projection * modelview * vec4(pos, 1.0);
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Point rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+const float feather = 0.5;
+
+uniform vec4 color;
+uniform float pixel;
+uniform float width;
+
+void main() {
+ // Rectangular points
+ gl_FragColor = color;
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Point rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+const float feather = 0.5;
+
+attribute vec3 pos;
+attribute vec2 loc;
+
+uniform mat4 modelview;
+uniform mat4 projection;
+uniform float width;
+uniform float pixel;
+
+void main() {
+ // get camera vectors from modelview matrix
+ vec3 u = vec3(modelview[0].x, modelview[1].x, modelview[2].x);
+ vec3 v = vec3(modelview[0].y, modelview[1].y, modelview[2].y);
+
+ // calculate point contour extension basis for constant width and caps
+
+ // calculate point extension width considering antialiasing
+ float ext = width + feather * pixel;
+
+ // extend point contour
+ vec3 vertex = pos;
+ vertex += ext * loc.x * normalize(u);
+ vertex += ext * loc.y * normalize(v);
+
+ // transform resulting vertex with modelview and projection matrices
+ gl_Position = projection * modelview * vec4(vertex, 1.0);
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Indexed Mesh rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+uniform vec4 color;
+uniform sampler2D texture_;
+
+varying vec2 fragTex;
+
+void main() {
+ vec4 texColor = texture2D(texture_, fragTex);
+ if(texColor.a == 0.0) discard;
+ gl_FragColor = texColor * color;
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Indexed Mesh rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+attribute vec3 pos;
+attribute vec2 tex;
+
+uniform mat4 modelview;
+uniform mat4 projection;
+
+varying vec2 fragTex;
+
+void main() {
+ fragTex = tex;
+ gl_Position = projection * modelview * vec4(pos, 1.0);
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Indexed Mesh rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+uniform vec4 color;
+uniform sampler2D texture_;
+
+varying vec2 fragTex;
+
+void main() {
+ gl_FragColor = vec4(color.rgb, color.a * texture2D(texture_, fragTex).TEX_ALPHA);
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Mesh rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+uniform vec3 lightDir0;
+uniform vec3 lightDir1;
+uniform float lightInt0;
+uniform float lightInt1;
+uniform float ambient;
+
+varying vec3 fragNormal;
+varying vec4 fragColor;
+
+void main() {
+ vec3 result = fragColor.xyz * ambient;
+ vec3 normal = normalize(fragNormal);
+
+ float light0 = clamp(dot(lightDir0, normal), 0.0, 1.0) * lightInt0 * (1.0 - ambient);
+ result += fragColor.rgb * light0;
+
+ float light1 = clamp(dot(lightDir1, normal), 0.0, 1.0) * lightInt1 * (1.0 - ambient);
+ result += fragColor.rgb * light1;
+
+ gl_FragColor = vec4(result, fragColor.a);
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Mesh rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+attribute vec3 pos;
+attribute vec3 nor;
+attribute vec4 col;
+
+uniform mat4 modelview;
+uniform mat4 projection;
+
+varying vec3 fragNormal;
+varying vec4 fragColor;
+
+void main() {
+ fragNormal = vec3(modelview * vec4(nor, 0.0));
+ fragColor = col;
+
+ gl_Position = projection * modelview * vec4(pos, 1.0);
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Mesh rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+uniform vec4 color;
+uniform sampler2D texture_;
+
+void main() {
+ if(texture2D(texture_, gl_FragCoord.xy / 32.0).TEX_ALPHA < 0.5) discard;
+ gl_FragColor = color;
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Mesh rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+attribute vec3 pos;
+
+uniform mat4 modelview;
+uniform mat4 projection;
+
+void main() {
+ gl_Position = projection * modelview * vec4(pos, 1.0);
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Outline rendering shader
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+const int EMPHASIZED_AND_CONTOUR = 0;
+const int EMPHASIZED_WITHOUT_CONTOUR = 1;
+const int CONTOUR_ONLY = 2;
+
+const float feather = 0.5;
+
+attribute vec3 pos;
+attribute vec4 loc;
+attribute vec3 tgt;
+attribute vec3 nol;
+attribute vec3 nor;
+
+uniform mat4 modelview;
+uniform mat4 projection;
+uniform float width;
+uniform float pixel;
+uniform int mode;
+
+varying vec3 fragLoc;
+
+void main() {
+ // get camera direction from modelview matrix
+ vec3 dir = vec3(modelview[0].z, modelview[1].z, modelview[2].z);
+
+ // perform outline visibility test
+ float ldot = dot(nol, dir);
+ float rdot = dot(nor, dir);
+
+ bool isOutline = (ldot > -1e-6) == (rdot < 1e-6) ||
+ (rdot > -1e-6) == (ldot < 1e-6);
+ bool isTagged = loc.w > 0.5;
+
+ float visible = float((mode == CONTOUR_ONLY && isOutline) ||
+ (mode == EMPHASIZED_AND_CONTOUR && (isOutline || isTagged)) ||
+ (mode == EMPHASIZED_WITHOUT_CONTOUR && isTagged && !isOutline));
+
+ // calculate line contour extension basis for constant width and caps
+ vec3 norm = normalize(cross(tgt, dir));
+ norm = normalize(norm - dir * dot(dir, norm));
+ vec3 perp = normalize(cross(dir, norm));
+
+ // calculate line extension width considering antialiasing
+ float ext = (width + feather * pixel) * visible;
+
+ // extend line contour
+ vec3 vertex = pos;
+ vertex += ext * loc.x * normalize(perp);
+ vertex += ext * loc.y * normalize(norm);
+
+ // write fragment location for calculating caps and antialiasing
+ fragLoc = vec3(loc);
+
+ // transform resulting vertex with modelview and projection matrices
+ gl_Position = projection * modelview * vec4(vertex, 1.0);
+}
--- /dev/null
+window.devicePixelRatio = window.devicePixelRatio || 1;
+
+SolvespaceCamera = function(renderWidth, renderHeight, scale, up, right, offset) {
+ THREE.Camera.call(this);
+
+ this.type = 'SolvespaceCamera';
+
+ this.renderWidth = renderWidth;
+ this.renderHeight = renderHeight;
+ this.zoomScale = scale; /* Avoid namespace collision w/ THREE.Object.scale */
+ this.up = up;
+ this.right = right;
+ this.offset = offset;
+ this.depthBias = 0;
+
+ this.updateProjectionMatrix();
+};
+
+SolvespaceCamera.prototype = Object.create(THREE.Camera.prototype);
+SolvespaceCamera.prototype.constructor = SolvespaceCamera;
+SolvespaceCamera.prototype.updateProjectionMatrix = function() {
+ var temp = new THREE.Matrix4();
+ var offset = new THREE.Matrix4().makeTranslation(this.offset.x, this.offset.y, this.offset.z);
+ // Convert to right handed- do up cross right instead.
+ var n = new THREE.Vector3().crossVectors(this.up, this.right);
+ var rotate = new THREE.Matrix4().makeBasis(this.right, this.up, n);
+ rotate.transpose();
+ /* Transpose of rotation matrix == inverse. Rotating the camera by a
+ basis is equivalent to rotating an object by the inverse of the
+ basis. To mimic Solvespace's behavior, we pan relative to the camera.
+ So we need to be rotated to where the camera is pointing before panning.
+ See: https://en.wikipedia.org/wiki/Change_of_basis#Two_dimensions */
+
+ /* TODO: If we want perspective, we need an additional matrix
+ here which will modify w for perspective divide. */
+ var scale = new THREE.Matrix4().makeScale(2 * this.zoomScale / this.renderWidth,
+ 2 * this.zoomScale / this.renderHeight, this.zoomScale / 30000.0);
+
+ temp.multiply(scale);
+ temp.multiply(rotate);
+ temp.multiply(offset);
+
+ this.projectionMatrix.copy(temp);
+};
+
+SolvespaceCamera.prototype.NormalizeProjectionVectors = function() {
+ /* After rotating, up and right may no longer be orthogonal.
+ However, their cross product will produce the correct
+ rotated plane, and we can recover an orthogonal basis. */
+ var n = new THREE.Vector3().crossVectors(this.right, this.up);
+ this.up = new THREE.Vector3().crossVectors(n, this.right);
+ this.right.normalize();
+ this.up.normalize();
+};
+
+SolvespaceCamera.prototype.rotate = function(right, up) {
+ var oldRight = new THREE.Vector3().copy(this.right).normalize();
+ var oldUp = new THREE.Vector3().copy(this.up).normalize();
+ this.up.applyAxisAngle(oldRight, up);
+ this.right.applyAxisAngle(oldUp, right);
+ this.NormalizeProjectionVectors();
+};
+
+SolvespaceCamera.prototype.offsetProj = function(right, up) {
+ var shift = new THREE.Vector3(right * this.right.x + up * this.up.x,
+ right * this.right.y + up * this.up.y,
+ right * this.right.z + up * this.up.z);
+ this.offset.add(shift);
+};
+
+/* Calculate the offset in terms of up and right projection vectors
+that will preserve the world coordinates of the current mouse position after
+the zoom. */
+SolvespaceCamera.prototype.zoomTo = function(x, y, delta) {
+ // Get offset components in world coordinates, in terms of up/right.
+ var projOffsetX = this.offset.dot(this.right);
+ var projOffsetY = this.offset.dot(this.up);
+
+ /* Remove offset before scaling so, that mouse position changes
+ proportionally to the model and independent of current offset. */
+ var centerRightI = x/this.zoomScale - projOffsetX;
+ var centerUpI = y/this.zoomScale - projOffsetY;
+ var zoomFactor;
+
+ /* Zoom 20% every 100 delta. */
+ if(delta < 0) {
+ zoomFactor = (-delta * 0.002 + 1);
+ }
+ else if(delta > 0) {
+ zoomFactor = (delta * (-1.0/600.0) + 1);
+ }
+ else {
+ return;
+ }
+
+ this.zoomScale = this.zoomScale * zoomFactor;
+ var centerRightF = x/this.zoomScale - projOffsetX;
+ var centerUpF = y/this.zoomScale - projOffsetY;
+
+ this.offset.addScaledVector(this.right, centerRightF - centerRightI);
+ this.offset.addScaledVector(this.up, centerUpF - centerUpI);
+};
+
+SolvespaceControls = function(object, domElement) {
+ var _this = this;
+ this.object = object;
+ this.domElement = ( domElement !== undefined ) ? domElement : document;
+
+ var threePan = new Hammer.Pan({event : 'threepan', pointers : 3, enable : false});
+ var panAfterTap = new Hammer.Pan({event : 'panaftertap', enable : false});
+
+ this.touchControls = new Hammer.Manager(domElement, {
+ recognizers: [
+ [Hammer.Pinch, { enable: true }],
+ [Hammer.Pan],
+ [Hammer.Tap],
+ ]
+ });
+
+ this.touchControls.add(threePan);
+ this.touchControls.add(panAfterTap);
+
+ var changeEvent = {
+ type: 'change'
+ };
+ var startEvent = {
+ type: 'start'
+ };
+ var endEvent = {
+ type: 'end'
+ };
+
+ var _changed = false;
+ var _offsetPrev = new THREE.Vector2(0, 0);
+ var _offsetCur = new THREE.Vector2(0, 0);
+ var _rotatePrev = new THREE.Vector2(0, 0);
+ var _rotateCur = new THREE.Vector2(0, 0);
+
+ // Used during touch events.
+ var _rotateOrig = new THREE.Vector2(0, 0);
+ var _offsetOrig = new THREE.Vector2(0, 0);
+ var _prevScale = 1.0;
+
+ this.handleEvent = function(event) {
+ if (typeof this[event.type] == 'function') {
+ this[event.type](event);
+ }
+ }
+
+ function mousedown(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ switch (event.button) {
+ case 0:
+ _rotateCur.set(event.screenX,
+ event.screenY);
+ _rotatePrev.copy(_rotateCur);
+ document.addEventListener('mousemove', mousemove_rotate, false);
+ document.addEventListener('mouseup', mouseup, false);
+ break;
+ case 2:
+ _offsetCur.set(event.screenX / window.devicePixelRatio,
+ event.screenY / window.devicePixelRatio);
+ _offsetPrev.copy(_offsetCur);
+ document.addEventListener('mousemove', mousemove_pan, false);
+ document.addEventListener('mouseup', mouseup, false);
+ break;
+ default:
+ break;
+ }
+ }
+
+ function wheel( event ) {
+ event.preventDefault();
+ /* FIXME: Width and height might not be supported universally, but
+ can be calculated? */
+ var box = _this.domElement.getBoundingClientRect();
+ object.zoomTo(event.clientX - box.width/2 - box.left,
+ -(event.clientY - box.height/2 - box.top), event.deltaY);
+ _changed = true;
+ }
+
+ function mousemove_rotate(event) {
+ _rotateCur.set(event.screenX,
+ event.screenY);
+ var diff = new THREE.Vector2().subVectors(_rotateCur, _rotatePrev)
+ .multiplyScalar(1 / object.zoomScale);
+ object.rotate(-0.3 * Math.PI / 180 * diff.x * object.zoomScale,
+ -0.3 * Math.PI / 180 * diff.y * object.zoomScale);
+ _changed = true;
+ _rotatePrev.copy(_rotateCur);
+ }
+
+ function mousemove_pan(event) {
+ _offsetCur.set(event.screenX / window.devicePixelRatio,
+ event.screenY / window.devicePixelRatio);
+ var diff = new THREE.Vector2().subVectors(_offsetCur, _offsetPrev)
+ .multiplyScalar(window.devicePixelRatio / object.zoomScale);
+ object.offsetProj(diff.x, -diff.y);
+ _changed = true;
+ _offsetPrev.copy(_offsetCur);
+ }
+
+ function mouseup(event) {
+ /* TODO: Opera mouse gestures will intercept this event, making it
+ possible to have multiple mousedown events consecutively without
+ a corresponding mouseup (so multiple viewports can be rotated/panned
+ simultaneously). Disable mouse gestures for now. */
+ event.preventDefault();
+ event.stopPropagation();
+
+ switch (event.button) {
+ case 0:
+ document.removeEventListener('mousemove', mousemove_rotate);
+ document.removeEventListener('mouseup', mouseup);
+ break;
+ case 2:
+ document.removeEventListener('mousemove', mousemove_pan);
+ document.removeEventListener('mouseup', mouseup);
+ break;
+ }
+
+ _this.dispatchEvent(endEvent);
+ }
+
+ function pan(event) {
+ /* neWcur - prev does not necessarily equal (cur + diff) - prev.
+ Floating point is not associative. */
+ var touchDiff = new THREE.Vector2(event.deltaX, event.deltaY);
+ _rotateCur.addVectors(_rotateOrig, touchDiff);
+ var incDiff = new THREE.Vector2().subVectors(_rotateCur, _rotatePrev)
+ .multiplyScalar(1 / object.zoomScale);
+ object.rotate(-0.3 * Math.PI / 180 * incDiff.x * object.zoomScale,
+ -0.3 * Math.PI / 180 * incDiff.y * object.zoomScale);
+ _changed = true;
+ _rotatePrev.copy(_rotateCur);
+ }
+
+ function panstart(event) {
+ /* TODO: Dynamically enable pan function? */
+ _rotateOrig.copy(_rotateCur);
+ }
+
+ function pinchstart(event) {
+ _prevScale = event.scale;
+ }
+
+ function pinch(event) {
+ /* FIXME: Width and height might not be supported universally, but
+ can be calculated? */
+ var box = _this.domElement.getBoundingClientRect();
+
+ /* 16.6... pixels chosen heuristically... matches my touchpad. */
+ if (event.scale < _prevScale) {
+ object.zoomTo(event.center.x - box.width/2 - box.left,
+ -(event.center.y - box.height/2 - box.top), 100/6.0);
+ _changed = true;
+ } else if (event.scale > _prevScale) {
+ object.zoomTo(event.center.x - box.width/2 - box.left,
+ -(event.center.y - box.height/2 - box.top), -100/6.0);
+ _changed = true;
+ }
+
+ _prevScale = event.scale;
+ }
+
+ /* A tap will enable panning/disable rotate. */
+ function tap(event) {
+ panAfterTap.set({enable : true});
+ _this.touchControls.get('pan').set({enable : false});
+ }
+
+ function panaftertap(event) {
+ var touchDiff = new THREE.Vector2(event.deltaX, event.deltaY);
+ _offsetCur.addVectors(_offsetOrig, touchDiff);
+ var incDiff = new THREE.Vector2().subVectors(_offsetCur, _offsetPrev)
+ .multiplyScalar(1 / object.zoomScale);
+ object.offsetProj(incDiff.x, -incDiff.y);
+ _changed = true;
+ _offsetPrev.copy(_offsetCur);
+ }
+
+ function panaftertapstart(event) {
+ _offsetOrig.copy(_offsetCur);
+ }
+
+ function panaftertapend(event) {
+ panAfterTap.set({enable : false});
+ _this.touchControls.get('pan').set({enable : true});
+ }
+
+ function contextmenu(event) {
+ event.preventDefault();
+ }
+
+ this.update = function() {
+ if (_changed) {
+ _this.dispatchEvent(changeEvent);
+ _changed = false;
+ }
+ };
+
+ this.domElement.addEventListener('mousedown', mousedown, false);
+ this.domElement.addEventListener('wheel', wheel, false);
+ this.domElement.addEventListener('contextmenu', contextmenu, false);
+
+ /* Hammer.on wraps addEventListener */
+ // Rotate
+ this.touchControls.on('pan', pan);
+ this.touchControls.on('panstart', panstart);
+
+ // Zoom
+ this.touchControls.on('pinch', pinch);
+ this.touchControls.on('pinchstart', pinchstart);
+
+ //Pan
+ this.touchControls.on('tap', tap);
+ this.touchControls.on('panaftertapstart', panaftertapstart);
+ this.touchControls.on('panaftertap', panaftertap);
+ this.touchControls.on('panaftertapend', panaftertapend);
+};
+
+SolvespaceControls.prototype = Object.create(THREE.EventDispatcher.prototype);
+SolvespaceControls.prototype.constructor = SolvespaceControls;
+
+
+solvespace = function(obj, params) {
+ var scene, edgeScene, camera, edgeCamera, renderer;
+ var geometry, controls, material, mesh, edges;
+ var width, height, scale, offset, projRight, projUp;
+ var directionalLightArray = [];
+ var inheritedWidth = false, inheritedHeight = false;
+
+ if (typeof params === "undefined" || !("width" in params)) {
+ width = window.innerWidth;
+ inheritedWidth = true;
+ } else {
+ width = params.width;
+ }
+
+ if (typeof params === "undefined" || !("height" in params)) {
+ height = window.innerHeight;
+ inheritedHeight = true;
+ } else {
+ height = params.height;
+ }
+
+ if (typeof params === "undefined" || !("scale" in params)) {
+ scale = 5;
+ } else {
+ scale = params.scale;
+ }
+
+ if (typeof params === "undefined" || !("offset" in params)) {
+ offset = new THREE.Vector3(0, 0, 0);
+ } else {
+ offset = params.offset;
+ }
+
+ if (typeof params === "undefined" || !("projUp" in params)) {
+ projUp = new THREE.Vector3(0, 1, -1);
+ } else {
+ projUp = params.projUp;
+ }
+
+ if (typeof params === "undefined" || !("projRight" in params)) {
+ projRight = new THREE.Vector3(1, 0, -1);
+ } else {
+ projRight = params.projRight;
+ }
+
+ var domElement = init();
+ lightUpdate();
+ render();
+ return domElement;
+
+ function init() {
+ scene = new THREE.Scene();
+ edgeScene = new THREE.Scene();
+
+ camera = new SolvespaceCamera(width, height, scale, projUp, projRight, offset);
+ camera.NormalizeProjectionVectors();
+
+ mesh = createMesh(obj);
+ scene.add(mesh);
+ edges = createEdges(obj);
+ edgeScene.add(edges);
+
+ for (var i = 0; i < obj.lights.d.length; i++) {
+ var lightColor = new THREE.Color(obj.lights.d[i].intensity,
+ obj.lights.d[i].intensity, obj.lights.d[i].intensity);
+ var directionalLight = new THREE.DirectionalLight(lightColor, 1);
+ directionalLight.position.set(obj.lights.d[i].direction[0],
+ obj.lights.d[i].direction[1], obj.lights.d[i].direction[2]);
+ directionalLightArray.push(directionalLight);
+ scene.add(directionalLight);
+ }
+
+ var lightColor = new THREE.Color(obj.lights.a, obj.lights.a, obj.lights.a);
+ var ambientLight = new THREE.AmbientLight(lightColor.getHex());
+ scene.add(ambientLight);
+
+ renderer = new THREE.WebGLRenderer({ antialias: true});
+ renderer.setSize(width * window.devicePixelRatio, height * window.devicePixelRatio);
+ renderer.autoClear = false;
+ renderer.domElement.style =
+ "width: " + width + "px;" +
+ "height: " + height + "px;";
+
+ controls = new SolvespaceControls(camera, renderer.domElement);
+ controls.addEventListener("change", render);
+ controls.addEventListener("change", lightUpdate);
+
+ if(inheritedWidth || inheritedHeight) {
+ window.addEventListener("resize", resize);
+ }
+
+ animate();
+ return renderer.domElement;
+ }
+
+ function resize() {
+ scale = camera.zoomScale;
+ if(inheritedWidth) {
+ scale *= window.innerWidth / width;
+ width = window.innerWidth;
+ }
+ if(inheritedHeight) {
+ scale *= window.innerHeight / height;
+ height = window.innerHeight;
+ }
+
+ camera.renderWidth = width;
+ camera.renderHeight = height;
+ camera.zoomScale = scale;
+
+ renderer.setSize(width * window.devicePixelRatio, height * window.devicePixelRatio);
+ renderer.domElement.style =
+ "width: " + width + "px;" +
+ "height: " + height + "px;";
+
+ render();
+ }
+
+ function animate() {
+ requestAnimationFrame(animate);
+ controls.update();
+ }
+
+ function render() {
+ var context = renderer.getContext();
+ camera.updateProjectionMatrix();
+ renderer.clear();
+
+ context.depthRange(0.1, 1);
+ renderer.render(scene, camera);
+
+ context.depthRange(0.1-(2/60000.0), 1-(2/60000.0));
+ renderer.render(edgeScene, camera);
+ }
+
+ function lightUpdate() {
+ var changeBasis = new THREE.Matrix4();
+
+ // The original light positions were in camera space.
+ // Project them into standard space using camera's basis
+ // vectors (up, target, and their cross product).
+ var n = new THREE.Vector3().crossVectors(camera.up, camera.right);
+ changeBasis.makeBasis(camera.right, camera.up, n);
+
+ for (var i = 0; i < 2; i++) {
+ var newLightPos = changeBasis.applyToVector3Array(
+ [obj.lights.d[i].direction[0], obj.lights.d[i].direction[1],
+ obj.lights.d[i].direction[2]]);
+ directionalLightArray[i].position.set(newLightPos[0],
+ newLightPos[1], newLightPos[2]);
+ }
+ }
+
+ function createMesh(meshObj) {
+ var geometry = new THREE.Geometry();
+ var materialIndex = 0;
+ var materialList = [];
+ var opacitiesSeen = {};
+
+ for (var i = 0; i < meshObj.points.length; i++) {
+ geometry.vertices.push(new THREE.Vector3(meshObj.points[i][0],
+ meshObj.points[i][1], meshObj.points[i][2]));
+ }
+
+ for (var i = 0; i < meshObj.faces.length; i++) {
+ var currOpacity = ((meshObj.colors[i] & 0xFF000000) >>> 24) / 255.0;
+ if (opacitiesSeen[currOpacity] === undefined) {
+ opacitiesSeen[currOpacity] = materialIndex;
+ materialIndex++;
+ materialList.push(new THREE.MeshLambertMaterial({
+ vertexColors: THREE.FaceColors,
+ opacity: currOpacity,
+ transparent: true,
+ side: THREE.DoubleSide
+ }));
+ }
+
+ geometry.faces.push(new THREE.Face3(meshObj.faces[i][0],
+ meshObj.faces[i][1], meshObj.faces[i][2],
+ [new THREE.Vector3(meshObj.normals[i][0][0],
+ meshObj.normals[i][0][1], meshObj.normals[i][0][2]),
+ new THREE.Vector3(meshObj.normals[i][1][0],
+ meshObj.normals[i][1][1], meshObj.normals[i][1][2]),
+ new THREE.Vector3(meshObj.normals[i][2][0],
+ meshObj.normals[i][2][1], meshObj.normals[i][2][2])],
+ new THREE.Color(meshObj.colors[i] & 0x00FFFFFF),
+ opacitiesSeen[currOpacity]));
+ }
+
+ geometry.computeBoundingSphere();
+ return new THREE.Mesh(geometry, new THREE.MultiMaterial(materialList));
+ }
+
+ function createEdges(meshObj) {
+ var geometry = new THREE.Geometry();
+ var material = new THREE.LineBasicMaterial();
+
+ for (var i = 0; i < meshObj.edges.length; i++) {
+ geometry.vertices.push(new THREE.Vector3(meshObj.edges[i][0][0],
+ meshObj.edges[i][0][1], meshObj.edges[i][0][2]),
+ new THREE.Vector3(meshObj.edges[i][1][0],
+ meshObj.edges[i][1][1], meshObj.edges[i][1][2]));
+ }
+
+ geometry.computeBoundingSphere();
+ return new THREE.LineSegments(geometry, material);
+ }
+};
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
+<assemblyIdentity
+ version="1.0.0.0"
+ processorArchitecture="*"
+ name="JonathanWesthues.3dCAD.SolveSpace"
+ type="win32"
+/>
+<description>Parametric 3d CAD tool.</description>
+<asmv3:application>
+ <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
+ <dpiAware>true</dpiAware>
+ </asmv3:windowsSettings>
+</asmv3:application>
+<dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="*"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+</dependency>
+</assembly>
--- /dev/null
+1 VERSIONINFO
+FILEVERSION ${solvespace_VERSION_MAJOR},${solvespace_VERSION_MINOR},0,0
+PRODUCTVERSION ${solvespace_VERSION_MAJOR},${solvespace_VERSION_MINOR},0,0
+FILEFLAGSMASK 0
+FILEFLAGS 0
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+FILESUBTYPE 0
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "04090000"
+ BEGIN
+ VALUE "CompanyName", "The SolveSpace authors"
+ VALUE "ProductName", "SolveSpace"
+ VALUE "ProductVersion", "${solvespace_VERSION_MAJOR}.${solvespace_VERSION_MINOR}~${solvespace_GIT_HASH}"
+ VALUE "FileDescription", "SolveSpace, a parametric 2d/3d CAD"
+ VALUE "FileVersion", "${solvespace_VERSION_MAJOR}.${solvespace_VERSION_MINOR}~${solvespace_GIT_HASH}"
+ VALUE "OriginalFilename", "solvespace.exe"
+ VALUE "InternalName", "solvespace"
+ VALUE "LegalCopyright", "(c) 2008-2016 Jonathan Westhues and other authors"
+ END
+ END
+
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 0
+ END
+END
-# global
-
include(GNUInstallDirs)
-include_directories(
- ${OPENGL_INCLUDE_DIR}
- ${PNG_INCLUDE_DIRS}
- ${FREETYPE_INCLUDE_DIRS})
-
-link_directories(
- ${PNG_LIBRARY_DIRS}
- ${FREETYPE_LIBRARY_DIRS})
-
-add_definitions(
- ${PNG_CFLAGS_OTHER})
+# configuration
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR}/built
${CMAKE_CURRENT_BINARY_DIR})
-if(SPACEWARE_FOUND)
- include_directories(
- ${SPACEWARE_INCLUDE_DIR})
+set(HAVE_SPACEWARE ${SPACEWARE_FOUND})
+
+if(NOT WIN32 OR APPLE)
+ if(GTKMM_gtkmm-3.0_VERSION VERSION_LESS "3.24.0")
+ set(HAVE_GTK_FILECHOOSERNATIVE 0)
+ else()
+ set(HAVE_GTK_FILECHOOSERNATIVE 1)
+ endif()
endif()
-set(HAVE_SPACEWARE ${SPACEWARE_FOUND})
-set(HAVE_GTK ${GTKMM_FOUND})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
${CMAKE_CURRENT_BINARY_DIR}/config.h)
# platform utilities
-if(WIN32)
- set(util_SOURCES
- win32/w32util.cpp)
-else()
- set(util_SOURCES
- unix/unixutil.cpp)
+if(APPLE)
+ set(util_LIBRARIES
+ ${APPKIT_LIBRARY})
endif()
# libslvs
expr.cpp
constraint.cpp
constrainteq.cpp
- system.cpp)
+ system.cpp
+ platform/platform.cpp)
set(libslvs_HEADERS
- solvespace.h)
+ solvespace.h
+ platform/platform.h)
add_library(slvs SHARED
${libslvs_SOURCES}
target_include_directories(slvs
PUBLIC ${CMAKE_SOURCE_DIR}/include)
+target_link_libraries(slvs
+ ${util_LIBRARIES}
+ mimalloc-static)
+
+add_dependencies(slvs
+ mimalloc-static)
+
set_target_properties(slvs PROPERTIES
PUBLIC_HEADER ${CMAKE_SOURCE_DIR}/include/slvs.h
VERSION ${solvespace_VERSION_MAJOR}.${solvespace_VERSION_MINOR}
if(NOT WIN32)
install(TARGETS slvs
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
endif()
-# generated files
-
-# `$<TARGET_FILE:tool>` allows us to use binfmt support on Linux
-# without special-casing anything; running `tool.exe` would succeed
-# but unlike Windows, Linux does not have the machinery to map
-# an invocation of `tool` to an executable `tool.exe` in $PATH.
-
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/generated)
-
-file(GLOB icons ${CMAKE_CURRENT_SOURCE_DIR}/icons/*.png)
-add_custom_command(
- OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/icons.cpp
- ${CMAKE_CURRENT_BINARY_DIR}/generated/icons.h
- COMMAND $<TARGET_FILE:png2c>
- ${CMAKE_CURRENT_BINARY_DIR}/generated/icons.cpp
- ${CMAKE_CURRENT_BINARY_DIR}/generated/icons.h
- ${icons}
- DEPENDS png2c ${icons}
- VERBATIM)
-
-file(GLOB chars ${CMAKE_CURRENT_SOURCE_DIR}/fonts/private/*.png)
-list(SORT chars)
-add_custom_command(
- OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/bitmapfont.table.h
- COMMAND $<TARGET_FILE:unifont2c>
- ${CMAKE_CURRENT_BINARY_DIR}/generated/bitmapfont.table.h
- ${CMAKE_CURRENT_SOURCE_DIR}/fonts/unifont-8.0.01.hex.gz
- ${chars}
- DEPENDS unifont2c
- ${CMAKE_CURRENT_SOURCE_DIR}/fonts/unifont-8.0.01.hex.gz
- ${chars}
- VERBATIM)
-
-add_custom_command(
- OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/vectorfont.table.h
- COMMAND $<TARGET_FILE:lff2c>
- ${CMAKE_CURRENT_BINARY_DIR}/generated/vectorfont.table.h
- ${CMAKE_CURRENT_SOURCE_DIR}/fonts/unicode.lff.gz
- DEPENDS lff2c
- ${CMAKE_CURRENT_SOURCE_DIR}/fonts/unicode.lff.gz
- VERBATIM)
-
-set(generated_HEADERS
- ${CMAKE_CURRENT_BINARY_DIR}/generated/vectorfont.table.h
- ${CMAKE_CURRENT_BINARY_DIR}/generated/bitmapfont.table.h
- ${CMAKE_CURRENT_BINARY_DIR}/generated/icons.h)
-
-set(generated_SOURCES
- ${CMAKE_CURRENT_BINARY_DIR}/generated/icons.cpp)
-
-# platform dependencies
-
-if(WIN32)
- set(platform_SOURCES
- win32/w32main.cpp
- win32/resource.rc)
-
- set(platform_LIBRARIES
- comctl32)
-elseif(APPLE)
- add_definitions(
- -fobjc-arc)
-
- set(platform_SOURCES
- cocoa/cocoamain.mm
- unix/gloffscreen.cpp)
-
- set(platform_XIBS
- cocoa/MainMenu.xib
- cocoa/SaveFormatAccessory.xib)
+# solvespace dependencies
- set(platform_ICONS
- cocoa/AppIcon.iconset)
-
- set(platform_RESOURCES
- unix/solvespace-48x48.png)
-
- set(platform_BUNDLED_LIBS
- ${PNG_LIBRARIES}
- ${FREETYPE_LIBRARIES})
-
- set(platform_LIBRARIES
- ${APPKIT_LIBRARY})
-elseif(HAVE_GTK)
+include_directories(
+ ${OPENGL_INCLUDE_DIR}
+ ${ZLIB_INCLUDE_DIR}
+ ${PNG_PNG_INCLUDE_DIR}
+ ${FREETYPE_INCLUDE_DIRS}
+ ${CAIRO_INCLUDE_DIRS}
+ ${Q3D_INCLUDE_DIR}
+ ${MIMALLOC_INCLUDE_DIR})
+
+if(Backtrace_FOUND)
include_directories(
- ${GTKMM_INCLUDE_DIRS}
- ${JSONC_INCLUDE_DIRS}
- ${FONTCONFIG_INCLUDE_DIRS}
- ${GLEW_INCLUDE_DIRS})
-
- link_directories(
- ${GTKMM_LIBRARY_DIRS}
- ${JSONC_LIBRARY_DIRS}
- ${FONTCONFIG_LIBRARY_DIRS}
- ${GLEW_LIBRARY_DIRS})
-
- add_definitions(
- ${GTKMM_CFLAGS_OTHER}
- ${JSONC_CFLAGS_OTHER}
- ${FONTCONFIG_CFLAGS_OTHER}
- ${GLEW_CFLAGS_OTHER})
-
- set(platform_SOURCES
- gtk/gtkmain.cpp
- unix/gloffscreen.cpp)
-
- set(platform_LIBRARIES
- ${GTKMM_LIBRARIES}
- ${JSONC_LIBRARIES}
- ${FONTCONFIG_LIBRARIES}
- ${GLEW_LIBRARIES})
+ ${Backtrace_INCLUDE_DIRS})
endif()
-set(platform_BUNDLED_RESOURCES)
+if(SPACEWARE_FOUND)
+ include_directories(
+ ${SPACEWARE_INCLUDE_DIR})
+endif()
-foreach(xib ${platform_XIBS})
- get_filename_component(nib ${xib} NAME_WE)
- set(source ${CMAKE_CURRENT_SOURCE_DIR}/${xib})
- set(target ${CMAKE_CURRENT_BINARY_DIR}/solvespace.app/Contents/Resources/${nib}.nib)
- list(APPEND platform_BUNDLED_RESOURCES ${target})
+if(OPENGL STREQUAL 3)
+ set(gl_SOURCES
+ render/gl3shader.cpp
+ render/rendergl3.cpp)
+elseif(OPENGL STREQUAL 1)
+ set(gl_SOURCES
+ render/rendergl1.cpp)
+else()
+ message(FATAL_ERROR "Unsupported OpenGL version ${OPENGL}")
+endif()
- add_custom_command(
- OUTPUT ${target}
- COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/solvespace.app/Contents/Resources
- COMMAND ibtool --errors --warnings --notices
- --output-format human-readable-text --compile
- ${target} ${source}
- COMMENT "Building Interface Builder file ${xib}"
- DEPENDS ${xib}
- VERBATIM)
-endforeach()
+set(platform_SOURCES
+ ${gl_SOURCES}
+ platform/entrygui.cpp)
-foreach(icon ${platform_ICONS})
- get_filename_component(name ${icon} NAME_WE)
- set(source ${CMAKE_CURRENT_SOURCE_DIR}/${icon})
- set(target ${CMAKE_CURRENT_BINARY_DIR}/solvespace.app/Contents/Resources/${name}.icns)
- list(APPEND platform_BUNDLED_RESOURCES ${target})
+if(WIN32)
+ list(APPEND platform_SOURCES
+ platform/guiwin.cpp)
- add_custom_command(
- OUTPUT ${target}
- COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/solvespace.app/Contents/Resources
- COMMAND iconutil -c icns -o ${target} ${source}
- COMMENT "Building icon set ${icon}"
- DEPENDS ${source}
- VERBATIM)
-endforeach()
+ set(platform_LIBRARIES
+ comctl32
+ ${SPACEWARE_LIBRARIES})
+elseif(APPLE)
+ add_compile_options(
+ -fobjc-arc)
-foreach(res ${platform_RESOURCES})
- get_filename_component(name ${res} NAME)
- set(source ${CMAKE_CURRENT_SOURCE_DIR}/${res})
- set(target ${CMAKE_CURRENT_BINARY_DIR}/solvespace.app/Contents/Resources/${name})
- list(APPEND platform_BUNDLED_RESOURCES ${target})
+ list(APPEND platform_SOURCES
+ platform/guimac.mm)
+else()
+ list(APPEND platform_SOURCES
+ platform/guigtk.cpp)
- add_custom_command(
- OUTPUT ${target}
- COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/solvespace.app/Contents/Resources
- COMMAND ${CMAKE_COMMAND} -E copy ${source} ${target}
- COMMENT "Copying resource file ${res}"
- DEPENDS ${res}
- VERBATIM)
-endforeach()
+ set(platform_LIBRARIES
+ ${SPACEWARE_LIBRARIES})
-foreach(lib ${platform_BUNDLED_LIBS})
- get_filename_component(name ${lib} NAME)
- set(target ${CMAKE_CURRENT_BINARY_DIR}/solvespace.app/Contents/MacOS/${name})
- list(APPEND platform_BUNDLED_RESOURCES ${target})
+ foreach(pkg_config_lib GTKMM JSONC FONTCONFIG)
+ include_directories(${${pkg_config_lib}_INCLUDE_DIRS})
+ link_directories(${${pkg_config_lib}_LIBRARY_DIRS})
+ list(APPEND platform_LIBRARIES ${${pkg_config_lib}_LIBRARIES})
+ endforeach()
+endif()
- add_custom_command(
- OUTPUT ${target}
- COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/solvespace.app/Contents/Resources
- COMMAND ${CMAKE_COMMAND} -E copy ${lib} ${target}
- COMMENT "Bundling shared library ${lib}"
- DEPENDS ${lib}
- VERBATIM)
-endforeach()
+set(every_platform_SOURCES
+ platform/guiwin.cpp
+ platform/guigtk.cpp
+ platform/guimac.mm)
-# solvespace executable
+# solvespace library
-set(solvespace_HEADERS
- config.h
+set(solvespace_core_HEADERS
dsc.h
expr.h
polygon.h
sketch.h
solvespace.h
ui.h
+ platform/platform.h
+ render/render.h
+ render/gl3shader.h
srf/surface.h)
-set(solvespace_SOURCES
+set(solvespace_core_SOURCES
bsp.cpp
clipboard.cpp
confscreen.cpp
expr.cpp
file.cpp
generate.cpp
- glhelper.cpp
graphicswin.cpp
group.cpp
groupmesh.cpp
importdxf.cpp
+ importidf.cpp
mesh.cpp
modify.cpp
mouse.cpp
+ polyline.cpp
polygon.cpp
+ resource.cpp
request.cpp
- solvespace.cpp
style.cpp
system.cpp
textscreens.cpp
undoredo.cpp
util.cpp
view.cpp
+ platform/platform.cpp
+ platform/gui.cpp
+ render/render.cpp
+ render/render2d.cpp
srf/boolean.cpp
srf/curve.cpp
srf/merge.cpp
srf/surfinter.cpp
srf/triangulate.cpp)
-add_executable(solvespace WIN32 MACOSX_BUNDLE
- ${libslvs_HEADERS}
- ${libslvs_SOURCES}
+set(solvespace_core_gl_SOURCES
+ solvespace.cpp)
+
+add_library(solvespace-core STATIC
${util_SOURCES}
- ${platform_SOURCES}
- ${platform_BUNDLED_RESOURCES}
- ${generated_SOURCES}
- ${generated_HEADERS}
- ${solvespace_HEADERS}
- ${solvespace_SOURCES})
-
-target_link_libraries(solvespace
+ ${solvespace_core_HEADERS}
+ ${solvespace_core_SOURCES})
+
+add_dependencies(solvespace-core
+ q3d_header
+ mimalloc-static)
+
+target_link_libraries(solvespace-core
+ ${OpenMP_CXX_LIBRARIES}
dxfrw
- ${OPENGL_LIBRARIES}
- ${PNG_LIBRARIES}
- ${ZLIB_LIBRARIES}
- ${FREETYPE_LIBRARIES}
- ${platform_LIBRARIES})
-
-if(WIN32 AND NOT MINGW)
- set_target_properties(solvespace PROPERTIES
- LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO")
+ ${util_LIBRARIES}
+ ${ZLIB_LIBRARY}
+ ${PNG_LIBRARY}
+ ${FREETYPE_LIBRARY}
+ flatbuffers
+ mimalloc-static)
+
+if(Backtrace_FOUND)
+ target_link_libraries(solvespace-core
+ ${Backtrace_LIBRARY})
endif()
-if(SPACEWARE_FOUND)
- target_link_libraries(solvespace
- ${SPACEWARE_LIBRARIES})
-endif()
+target_compile_options(solvespace-core
+ PRIVATE ${COVERAGE_FLAGS})
-if(APPLE)
- set(fixups)
- foreach(lib ${platform_BUNDLED_LIBS})
- get_filename_component(name ${lib} NAME)
- execute_process(COMMAND otool -D ${lib}
- OUTPUT_VARIABLE canonical_lib OUTPUT_STRIP_TRAILING_WHITESPACE)
- string(REGEX REPLACE "^.+:\n" "" canonical_lib ${canonical_lib})
- add_custom_command(TARGET solvespace POST_BUILD
- COMMAND install_name_tool -change ${canonical_lib} @executable_path/${name}
- $<TARGET_FILE:solvespace>
- COMMENT "Fixing up rpath for dylib ${name}"
- VERBATIM)
+# solvespace translations
+
+if(HAVE_GETTEXT)
+ set(inputs
+ ${solvespace_core_SOURCES}
+ ${solvespace_core_HEADERS}
+ ${every_platform_SOURCES})
+
+ set(templ_po ${CMAKE_CURRENT_BINARY_DIR}/../res/messages.po)
+
+ set(output_pot ${CMAKE_CURRENT_SOURCE_DIR}/../res/messages.pot)
+ set(output_po ${CMAKE_CURRENT_SOURCE_DIR}/../res/locales/en_US.po)
+ file(GLOB locale_pos ${CMAKE_CURRENT_SOURCE_DIR}/../res/locales/*.po)
+
+ string(REPLACE ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}
+ gen_output_pot ${output_pot}.gen)
+ string(REPLACE ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}
+ gen_output_po ${output_po}.gen)
+ foreach(locale_po ${locale_pos})
+ string(REPLACE ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}
+ gen_locale_po ${locale_po}.gen)
+ list(APPEND gen_locale_pos ${gen_locale_po})
endforeach()
- set(bundle solvespace)
- add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/${bundle}.dmg
- COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/${bundle}.dmg
- COMMAND hdiutil create -srcfolder ${CMAKE_CURRENT_BINARY_DIR}/${bundle}.app
- ${CMAKE_BINARY_DIR}/${bundle}.dmg
- DEPENDS $<TARGET_FILE:${bundle}>
- COMMENT "Building ${bundle}.dmg"
+ add_custom_command(
+ OUTPUT ${gen_output_pot}
+ COMMAND ${XGETTEXT}
+ --language=C++
+ --keyword --keyword=_ --keyword=N_ --keyword=C_:2,1c --keyword=CN_:2,1c
+ --force-po --width=100 --sort-by-file
+ --package-name=SolveSpace
+ --package-version=${solvespace_VERSION_MAJOR}.${solvespace_VERSION_MINOR}
+ "--copyright-holder=the PACKAGE authors"
+ --msgid-bugs-address=whitequark@whitequark.org
+ --from-code=utf-8 --output=${gen_output_pot} ${inputs}
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${gen_output_pot} ${output_pot}
+ DEPENDS ${inputs}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMENT "Extracting translations"
+ VERBATIM)
+
+ file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../res/locales)
+
+ # en_US is a bit special; we pre-fill the msgstrs from msgids, instead of (as would normally
+ # happen) leaving them empty.
+ add_custom_command(
+ OUTPUT ${gen_output_po}
+ COMMAND ${MSGINIT}
+ --locale=en_US --no-translator
+ --output=${templ_po} --input=${gen_output_pot}
+ COMMAND ${MSGMERGE}
+ --force-po --no-fuzzy-matching
+ --output=${gen_output_po} ${output_po} ${templ_po}
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${gen_output_po} ${output_po}
+ DEPENDS ${gen_output_pot}
+ COMMENT "Updating en_US translations"
VERBATIM)
- add_custom_target(${bundle}-dmg ALL
- DEPENDS ${CMAKE_BINARY_DIR}/${bundle}.dmg)
+ foreach(locale_po ${locale_pos})
+ string(REPLACE ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}
+ gen_locale_po ${locale_po}.gen)
+
+ get_filename_component(locale_name ${locale_po} NAME_WE)
+ if(locale_name STREQUAL "en_US")
+ continue()
+ endif()
+
+ add_custom_command(
+ OUTPUT ${gen_locale_po}
+ COMMAND ${MSGMERGE}
+ --no-fuzzy-matching
+ --output=${gen_locale_po} ${locale_po} ${gen_output_pot}
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${gen_locale_po} ${locale_po}
+ DEPENDS ${gen_output_pot}
+ COMMENT "Updating ${locale_name} translations"
+ VERBATIM)
+ endforeach()
+
+ add_custom_target(translate_solvespace
+ DEPENDS ${gen_output_pot} ${gen_output_po} ${gen_locale_pos})
endif()
-if(NOT WIN32)
- install(TARGETS solvespace
- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
- BUNDLE DESTINATION .)
+# solvespace graphical executable
+
+if(ENABLE_GUI)
+ add_executable(solvespace WIN32 MACOSX_BUNDLE
+ ${solvespace_core_gl_SOURCES}
+ ${platform_SOURCES}
+ $<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
+
+ add_dependencies(solvespace
+ resources)
+
+ target_link_libraries(solvespace
+ solvespace-core
+ ${OPENGL_LIBRARIES}
+ ${platform_LIBRARIES}
+ ${COVERAGE_LIBRARY})
+
+ if(MSVC)
+ set_target_properties(solvespace PROPERTIES
+ LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /INCREMENTAL:NO /OPT:REF")
+ elseif(APPLE)
+ set_target_properties(solvespace PROPERTIES
+ OUTPUT_NAME SolveSpace)
+ endif()
+endif()
+
+# solvespace headless library
+
+set(headless_SOURCES
+ platform/guinone.cpp
+ render/rendercairo.cpp)
+
+add_library(solvespace-headless STATIC EXCLUDE_FROM_ALL
+ ${solvespace_core_gl_SOURCES}
+ ${headless_SOURCES})
+
+target_compile_definitions(solvespace-headless
+ PRIVATE -DHEADLESS)
+
+target_include_directories(solvespace-headless
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
+
+target_link_libraries(solvespace-headless
+ solvespace-core
+ ${CAIRO_LIBRARIES})
+
+target_compile_options(solvespace-headless
+ PRIVATE ${COVERAGE_FLAGS})
+
+# solvespace command-line executable
+
+if(ENABLE_CLI)
+ add_executable(solvespace-cli
+ platform/entrycli.cpp
+ $<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
+
+ target_link_libraries(solvespace-cli
+ solvespace-core
+ solvespace-headless)
+
+ add_dependencies(solvespace-cli
+ resources)
+
+ if(MSVC)
+ set_target_properties(solvespace-cli PROPERTIES
+ LINK_FLAGS "/INCREMENTAL:NO /OPT:REF")
+ endif()
endif()
-install(FILES unix/solvespace.desktop
- DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
-foreach(SIZE 16x16 24x24 32x32 48x48)
- install(FILES unix/solvespace-${SIZE}.png
- DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/apps
- RENAME solvespace.png)
- install(FILES unix/solvespace-${SIZE}.png
- DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/mimetypes
- RENAME application.x-solvespace.png)
-endforeach()
-foreach(SIZE 16x16 24x24 32x32 48x48)
- install(FILES unix/solvespace-${SIZE}.xpm
- DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps/)
-endforeach()
-
-# valgrind
-
-add_custom_target(solvespace-valgrind
- valgrind
- --tool=memcheck
- --verbose
- --track-fds=yes
- --log-file=vg.%p.out
- --num-callers=50
- --error-limit=no
- --read-var-info=yes
- --leak-check=full
- --leak-resolution=high
- --show-reachable=yes
- --track-origins=yes
- --malloc-fill=0xac
- --free-fill=0xde
- $<TARGET_FILE:solvespace>)
+# solvespace unix package
+
+if(NOT (WIN32 OR APPLE))
+ if(ENABLE_GUI)
+ install(TARGETS solvespace
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+ endif()
+ if(ENABLE_CLI)
+ install(TARGETS solvespace-cli
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+ endif()
+endif()
+
+# solvespace macOS package
+
+if(APPLE)
+ set(bundle SolveSpace)
+ set(bundle_bin ${EXECUTABLE_OUTPUT_PATH}/${bundle}.app/Contents/MacOS)
+ set(bundle_resources ${EXECUTABLE_OUTPUT_PATH}/${bundle}.app/Contents/Resources/lib)
+ execute_process(
+ COMMAND mkdir -p ${bundle_resources}
+ COMMAND cp -p /usr/local/opt/libomp/lib/libomp.dylib ${bundle_resources}/libomp.dylib
+ )
+ add_custom_command(TARGET solvespace POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${bundle_bin}
+ COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:solvespace-cli> ${bundle_bin}
+ COMMAND install_name_tool -change /usr/local/opt/libomp/lib/libomp.dylib "@executable_path/../Resources/lib/libomp.dylib" ${bundle_bin}/${bundle}
+ COMMENT "Bundling executable solvespace-cli"
+ VERBATIM)
+endif()
//-----------------------------------------------------------------------------
#include "solvespace.h"
-SBsp2 *SBsp2::Alloc(void) { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); }
-SBsp3 *SBsp3::Alloc(void) { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); }
-
-SBsp3 *SBsp3::FromMesh(SMesh *m) {
- SBsp3 *bsp3 = NULL;
- int i;
+SBsp2 *SBsp2::Alloc() { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); }
+SBsp3 *SBsp3::Alloc() { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); }
+SBsp3 *SBsp3::FromMesh(const SMesh *m) {
SMesh mc = {};
- for(i = 0; i < m->l.n; i++) {
- mc.AddTriangle(&(m->l.elem[i]));
- }
+ for(auto const &elt : m->l) { mc.AddTriangle(&elt); }
srand(0); // Let's be deterministic, at least!
int n = mc.l.n;
while(n > 1) {
int k = rand() % n;
n--;
- swap(mc.l.elem[k], mc.l.elem[n]);
- }
-
- for(i = 0; i < mc.l.n; i++) {
- bsp3 = InsertOrCreate(bsp3, &(mc.l.elem[i]), NULL);
+ swap(mc.l[k], mc.l[n]);
}
+ SBsp3 *bsp3 = NULL;
+ for(auto &elt : mc.l) { bsp3 = InsertOrCreate(bsp3, &elt, NULL); }
mc.Clear();
return bsp3;
}
-Vector SBsp3::IntersectionWith(Vector a, Vector b) {
+Vector SBsp3::IntersectionWith(Vector a, Vector b) const {
double da = a.Dot(n) - d;
double db = b.Dot(n) - d;
- if(da*db > 0) oops();
+ ssassert(da*db < 0, "Expected segment to intersect BSP node");
double dab = (db - da);
return (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab));
ll = ll->more;
}
- if(m->flipNormal && ((!pos2 && !onFace) ||
- (onFace && !sameNormal && m->keepCoplanar)))
- {
- m->AddTriangle(tr->meta, tr->c, tr->b, tr->a);
- } else if(!(m->flipNormal) && ((pos2 && !onFace) ||
- (onFace && sameNormal && m->keepCoplanar)))
- {
- m->AddTriangle(tr->meta, tr->a, tr->b, tr->c);
+ if((!onFace && ((m->keepInsideOtherShell && !pos2) ||
+ (!m->keepInsideOtherShell && pos2))) ||
+ (onFace && ((m->keepCoplanar && m->flipNormal && !sameNormal) ||
+ (m->keepCoplanar && !m->flipNormal && sameNormal)))) {
+ // We have decided that we need to keep a triangle either inside,
+ // outside or on the other shell. So add it and flip it if requested.
+ if(!(m->flipNormal)) {
+ m->AddTriangle(tr->meta, tr->a, tr->b, tr->c);
+ } else {
+ m->AddTriangle(tr->meta, tr->c, tr->b, tr->a);
+ }
} else {
m->atLeastOneDiscarded = true;
}
}
-void SBsp3::InsertHow(int how, STriangle *tr, SMesh *instead) {
+void SBsp3::InsertHow(BspClass how, STriangle *tr, SMesh *instead) {
switch(how) {
- case POS:
+ case BspClass::POS:
if(instead && !pos) goto alt;
pos = InsertOrCreate(pos, tr, instead);
break;
- case NEG:
+ case BspClass::NEG:
if(instead && !neg) goto alt;
neg = InsertOrCreate(neg, tr, instead);
break;
- case COPLANAR: {
+ case BspClass::COPLANAR: {
if(instead) goto alt;
SBsp3 *m = Alloc();
m->n = n;
more = m;
break;
}
- default: oops();
}
return;
alt:
- if(how == POS && !(instead->flipNormal)) {
- instead->AddTriangle(tr->meta, tr->a, tr->b, tr->c);
- } else if(how == NEG && instead->flipNormal) {
- instead->AddTriangle(tr->meta, tr->c, tr->b, tr->a);
- } else if(how == COPLANAR) {
+ if(((BspClass::POS == how) && !instead->keepInsideOtherShell) ||
+ ((BspClass::NEG == how) && instead->keepInsideOtherShell)) {
+ // We have decided that we need to keep a triangle (either inside or
+ // outside the other shell. So add it and flip it if requested.
+ if(!(instead->flipNormal)) {
+ instead->AddTriangle(tr->meta, tr->a, tr->b, tr->c);
+ } else {
+ instead->AddTriangle(tr->meta, tr->c, tr->b, tr->a);
+ }
+ } else if(how == BspClass::COPLANAR) {
if(edges) {
edges->InsertTriangle(tr, instead, this);
} else {
// I suppose this actually is allowed to happen, if the coplanar
// face is the leaf, and all of its neighbors are earlier in tree?
- InsertInPlane(false, tr, instead);
+ InsertInPlane(/*pos2=*/false, tr, instead);
}
} else {
instead->atLeastOneDiscarded = true;
}
}
-void SBsp3::InsertConvexHow(int how, STriMeta meta, Vector *vertex, int n,
- SMesh *instead)
-{
- switch(how) {
- case POS:
- if(pos) {
- pos = pos->InsertConvex(meta, vertex, n, instead);
- return;
- }
- break;
+class BspUtil {
+public:
+ SBsp3 *bsp;
- case NEG:
- if(neg) {
- neg = neg->InsertConvex(meta, vertex, n, instead);
- return;
- }
- break;
+ size_t onc;
+ size_t posc;
+ size_t negc;
+ bool *isPos;
+ bool *isNeg;
+ bool *isOn;
+
+ // triangle operations
+ STriangle *tr;
+ STriangle *btri; // also as alone
+ STriangle *ctri;
- default: oops();
+ // convex operations
+ Vector *on;
+ size_t npos;
+ size_t nneg;
+ Vector *vpos; // also as quad
+ Vector *vneg;
+
+ static BspUtil *Alloc() {
+ return (BspUtil *)AllocTemporary(sizeof(BspUtil));
}
- int i;
- for(i = 0; i < n - 2; i++) {
- STriangle tr = STriangle::From(meta,
- vertex[0], vertex[i+1], vertex[i+2]);
- InsertHow(how, &tr, instead);
+
+ void AllocOn() {
+ on = (Vector *)AllocTemporary(sizeof(Vector) * 2);
}
-}
-SBsp3 *SBsp3::InsertConvex(STriMeta meta, Vector *vertex, int cnt,
- SMesh *instead)
-{
- Vector e01 = (vertex[1]).Minus(vertex[0]);
- Vector e12 = (vertex[2]).Minus(vertex[1]);
- Vector out = e01.Cross(e12);
-
-#define MAX_VERTICES 50
- if(cnt+1 >= MAX_VERTICES) goto triangulate;
-
- int i;
- Vector on[2];
- bool isPos[MAX_VERTICES];
- bool isNeg[MAX_VERTICES];
- bool isOn[MAX_VERTICES];
- int posc, negc, onc; posc = negc = onc = 0;
- for(i = 0; i < cnt; i++) {
- double dt = n.Dot(vertex[i]);
- isPos[i] = isNeg[i] = isOn[i] = false;
- if(fabs(dt - d) < LENGTH_EPS) {
- isOn[i] = true;
- if(onc < 2) {
- on[onc] = vertex[i];
+ void AllocTriangle() {
+ btri = (STriangle *)AllocTemporary(sizeof(STriangle));
+ }
+
+ void AllocTriangles() {
+ btri = (STriangle *)AllocTemporary(sizeof(STriangle) * 2);
+ ctri = &btri[1];
+ }
+
+ void AllocQuad() {
+ vpos = (Vector *)AllocTemporary(sizeof(Vector) * 4);
+ }
+
+ void AllocClassify(size_t size) {
+ // Allocate a one big piece is faster than a small ones.
+ isPos = (bool *)AllocTemporary(sizeof(bool) * size * 3);
+ isNeg = &isPos[size];
+ isOn = &isNeg[size];
+ }
+
+ void AllocVertices(size_t size) {
+ vpos = (Vector *)AllocTemporary(sizeof(Vector) * size * 2);
+ vneg = &vpos[size];
+ }
+
+ void ClassifyTriangle(STriangle *tri, SBsp3 *node) {
+ tr = tri;
+ bsp = node;
+ onc = 0;
+ posc = 0;
+ negc = 0;
+
+ AllocClassify(3);
+
+ double dt[3] = { (tr->a).Dot(bsp->n), (tr->b).Dot(bsp->n), (tr->c).Dot(bsp->n) };
+ double d = bsp->d;
+ // Count vertices in the plane
+ for(int i = 0; i < 3; i++) {
+ if(dt[i] > d + LENGTH_EPS) {
+ posc++;
+ isPos[i] = true;
+ } else if(dt[i] < d - LENGTH_EPS) {
+ negc++;
+ isNeg[i] = true;
+ } else {
+ onc++;
+ isOn[i] = true;
}
- onc++;
- } else if(dt > d) {
- isPos[i] = true;
- posc++;
- } else {
- isNeg[i] = true;
- negc++;
}
}
- if(onc != 2 && onc != 1 && onc != 0) goto triangulate;
- if(onc == 2) {
- if(!instead) {
- SEdge se = SEdge::From(on[0], on[1]);
- edges = SBsp2::InsertOrCreateEdge(edges, &se, n, out);
+ bool ClassifyConvex(Vector *vertex, size_t cnt, SBsp3 *node, bool insertEdge) {
+ bsp = node;
+ onc = 0;
+ posc = 0;
+ negc = 0;
+
+ AllocClassify(cnt);
+ AllocOn();
+
+ for(size_t i = 0; i < cnt; i++) {
+ double dt = bsp->n.Dot(vertex[i]);
+ isPos[i] = isNeg[i] = isOn[i] = false;
+ if(fabs(dt - bsp->d) < LENGTH_EPS) {
+ isOn[i] = true;
+ if(onc < 2) {
+ on[onc] = vertex[i];
+ }
+ onc++;
+ } else if(dt > bsp->d) {
+ isPos[i] = true;
+ posc++;
+ } else {
+ isNeg[i] = true;
+ negc++;
+ }
}
- }
- if(posc == 0) {
- InsertConvexHow(NEG, meta, vertex, cnt, instead);
- return this;
- }
- if(negc == 0) {
- InsertConvexHow(POS, meta, vertex, cnt, instead);
- return this;
+ if(onc != 2 && onc != 1 && onc != 0) return false;
+ if(onc == 2) {
+ if(insertEdge) {
+ Vector e01 = (vertex[1]).Minus(vertex[0]);
+ Vector e12 = (vertex[2]).Minus(vertex[1]);
+ Vector out = e01.Cross(e12);
+ SEdge se = SEdge::From(on[0], on[1]);
+ bsp->edges = SBsp2::InsertOrCreateEdge(bsp->edges, &se, bsp->n, out);
+ }
+ }
+ return true;
}
- Vector vpos[MAX_VERTICES];
- Vector vneg[MAX_VERTICES];
- int npos, nneg; npos = nneg = 0;
+ bool ClassifyConvexVertices(Vector *vertex, size_t cnt, bool insertEdges) {
+ Vector inter[2];
+ int inters = 0;
+
+ npos = 0;
+ nneg = 0;
- Vector inter[2];
- int inters; inters = 0;
+ // Enlarge vertices list to consider two intersections
+ AllocVertices(cnt + 4);
- for(i = 0; i < cnt; i++) {
- int ip = WRAP((i + 1), cnt);
+ for(size_t i = 0; i < cnt; i++) {
+ size_t ip = WRAP((i + 1), cnt);
- if(isPos[i]) {
- vpos[npos++] = vertex[i];
+ if(isPos[i]) {
+ vpos[npos++] = vertex[i];
+ }
+ if(isNeg[i]) {
+ vneg[nneg++] = vertex[i];
+ }
+ if(isOn[i]) {
+ vneg[nneg++] = vertex[i];
+ vpos[npos++] = vertex[i];
+ }
+ if((isPos[i] && isNeg[ip]) || (isNeg[i] && isPos[ip])) {
+ Vector vi = bsp->IntersectionWith(vertex[i], vertex[ip]);
+ vpos[npos++] = vi;
+ vneg[nneg++] = vi;
+
+ if(inters >= 2) return false; // triangulate: XXX shouldn't happen but does
+ inter[inters++] = vi;
+ }
}
- if(isNeg[i]) {
- vneg[nneg++] = vertex[i];
+ ssassert(npos <= cnt + 1 && nneg <= cnt + 1, "Impossible");
+
+ if(insertEdges) {
+ Vector e01 = (vertex[1]).Minus(vertex[0]);
+ Vector e12 = (vertex[2]).Minus(vertex[1]);
+ Vector out = e01.Cross(e12);
+ if(inters == 2) {
+ SEdge se = SEdge::From(inter[0], inter[1]);
+ bsp->edges = SBsp2::InsertOrCreateEdge(bsp->edges, &se, bsp->n, out);
+ } else if(inters == 1 && onc == 1) {
+ SEdge se = SEdge::From(inter[0], on[0]);
+ bsp->edges = SBsp2::InsertOrCreateEdge(bsp->edges, &se, bsp->n, out);
+ } else if(inters == 0 && onc == 2) {
+ // We already handled this on-plane existing edge
+ } else {
+ return false; //triangulate;
+ }
}
- if(isOn[i]) {
- vneg[nneg++] = vertex[i];
- vpos[npos++] = vertex[i];
+ if(nneg < 3 || npos < 3) return false; // triangulate; // XXX
+
+ return true;
+ }
+
+ void ProcessEdgeInsert() {
+ ssassert(onc == 2, "Impossible");
+
+ Vector a, b;
+ if (!isOn[0]) { a = tr->b; b = tr->c; }
+ else if(!isOn[1]) { a = tr->c; b = tr->a; }
+ else if(!isOn[2]) { a = tr->a; b = tr->b; }
+ else ssassert(false, "Impossible");
+
+ SEdge se = SEdge::From(a, b);
+ bsp->edges = SBsp2::InsertOrCreateEdge(bsp->edges, &se, bsp->n, tr->Normal());
+ }
+
+ bool SplitIntoTwoTriangles(bool insertEdge) {
+ ssassert(posc == 1 && negc == 1 && onc == 1, "Impossible");
+
+ bool bpos;
+ Vector a, b, c;
+
+ // Standardize so that a is on the plane
+ if (isOn[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = isPos[1];
+ } else if(isOn[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = isPos[2];
+ } else if(isOn[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = isPos[0];
+ } else ssassert(false, "Impossible");
+
+ AllocTriangles();
+ Vector bPc = bsp->IntersectionWith(b, c);
+ *btri = STriangle::From(tr->meta, a, b, bPc);
+ *ctri = STriangle::From(tr->meta, c, a, bPc);
+
+ if(insertEdge) {
+ SEdge se = SEdge::From(a, bPc);
+ bsp->edges = SBsp2::InsertOrCreateEdge(bsp->edges, &se, bsp->n, tr->Normal());
}
- if((isPos[i] && isNeg[ip]) || (isNeg[i] && isPos[ip])) {
- Vector vi = IntersectionWith(vertex[i], vertex[ip]);
- vpos[npos++] = vi;
- vneg[nneg++] = vi;
- if(inters >= 2) goto triangulate; // XXX shouldn't happen but does
- inter[inters++] = vi;
+ return bpos;
+ }
+
+ bool SplitIntoTwoPieces(bool insertEdge) {
+ Vector a, b, c;
+ if(posc == 2 && negc == 1) {
+ // Standardize so that a is on one side, and b and c are on the other.
+ if (isNeg[0]) { a = tr->a; b = tr->b; c = tr->c;
+ } else if(isNeg[1]) { a = tr->b; b = tr->c; c = tr->a;
+ } else if(isNeg[2]) { a = tr->c; b = tr->a; c = tr->b;
+ } else ssassert(false, "Impossible");
+ } else if(posc == 1 && negc == 2) {
+ if (isPos[0]) { a = tr->a; b = tr->b; c = tr->c;
+ } else if(isPos[1]) { a = tr->b; b = tr->c; c = tr->a;
+ } else if(isPos[2]) { a = tr->c; b = tr->a; c = tr->b;
+ } else ssassert(false, "Impossible");
+ } else ssassert(false, "Impossible");
+
+ Vector aPb = bsp->IntersectionWith(a, b);
+ Vector cPa = bsp->IntersectionWith(c, a);
+ AllocTriangle();
+ AllocQuad();
+
+ *btri = STriangle::From(tr->meta, a, aPb, cPa);
+
+ vpos[0] = aPb;
+ vpos[1] = b;
+ vpos[2] = c;
+ vpos[3] = cPa;
+
+ if(insertEdge) {
+ SEdge se = SEdge::From(aPb, cPa);
+ bsp->edges = SBsp2::InsertOrCreateEdge(bsp->edges, &se, bsp->n, btri->Normal());
}
+
+ return posc == 2 && negc == 1;
}
- if(npos > cnt + 1 || nneg > cnt + 1) oops();
-
- if(!instead) {
- if(inters == 2) {
- SEdge se = SEdge::From(inter[0], inter[1]);
- edges = SBsp2::InsertOrCreateEdge(edges, &se, n, out);
- } else if(inters == 1 && onc == 1) {
- SEdge se = SEdge::From(inter[0], on[0]);
- edges = SBsp2::InsertOrCreateEdge(edges, &se, n, out);
- } else if(inters == 0 && onc == 2) {
- // We already handled this on-plane existing edge
- } else {
- goto triangulate;
+
+ static SBsp3 *Triangulate(SBsp3 *bsp, const STriMeta &meta, Vector *vertex,
+ size_t cnt, SMesh *instead) {
+ for(size_t i = 0; i < cnt - 2; i++) {
+ STriangle tr = STriangle::From(meta, vertex[0], vertex[i + 1], vertex[i + 2]);
+ bsp = SBsp3::InsertOrCreate(bsp, &tr, instead);
}
+ return bsp;
}
- if(nneg < 3 || npos < 3) goto triangulate; // XXX
+};
- InsertConvexHow(NEG, meta, vneg, nneg, instead);
- InsertConvexHow(POS, meta, vpos, npos, instead);
- return this;
+void SBsp3::InsertConvexHow(BspClass how, STriMeta meta, Vector *vertex, size_t n,
+ SMesh *instead) {
+ switch(how) {
+ case BspClass::POS:
+ if(pos) {
+ pos = pos->InsertConvex(meta, vertex, n, instead);
+ return;
+ }
+ break;
-triangulate:
- // We don't handle the special case for this; do it as triangles
- SBsp3 *r = this;
- for(i = 0; i < cnt - 2; i++) {
+ case BspClass::NEG:
+ if(neg) {
+ neg = neg->InsertConvex(meta, vertex, n, instead);
+ return;
+ }
+ break;
+
+ default: ssassert(false, "Unexpected BSP insert type");
+ }
+
+ for(size_t i = 0; i < n - 2; i++) {
STriangle tr = STriangle::From(meta,
vertex[0], vertex[i+1], vertex[i+2]);
- r = InsertOrCreate(r, &tr, instead);
+ InsertHow(how, &tr, instead);
+ }
+}
+
+SBsp3 *SBsp3::InsertConvex(STriMeta meta, Vector *vertex, size_t cnt, SMesh *instead) {
+ BspUtil *u = BspUtil::Alloc();
+ if(u->ClassifyConvex(vertex, cnt, this, !instead)) {
+ if(u->posc == 0) {
+ InsertConvexHow(BspClass::NEG, meta, vertex, cnt, instead);
+ return this;
+ }
+ if(u->negc == 0) {
+ InsertConvexHow(BspClass::POS, meta, vertex, cnt, instead);
+ return this;
+ }
+
+ if(u->ClassifyConvexVertices(vertex, cnt, !instead)) {
+ InsertConvexHow(BspClass::NEG, meta, u->vneg, u->nneg, instead);
+ InsertConvexHow(BspClass::POS, meta, u->vpos, u->npos, instead);
+ return this;
+ }
}
- return r;
+
+ // We don't handle the special case for this; do it as triangles
+ return BspUtil::Triangulate(this, meta, vertex, cnt, instead);
}
SBsp3 *SBsp3::InsertOrCreate(SBsp3 *where, STriangle *tr, SMesh *instead) {
if(where == NULL) {
if(instead) {
+ // ruevs: I do not think this code is reachable, but in
+ // principle should we use instead->keepInsideOtherShell
+ // in place of instead->flipNormal ?
if(instead->flipNormal) {
instead->atLeastOneDiscarded = true;
} else {
}
void SBsp3::Insert(STriangle *tr, SMesh *instead) {
- double dt[3] = { (tr->a).Dot(n), (tr->b).Dot(n), (tr->c).Dot(n) };
-
- int inc = 0, posc = 0, negc = 0;
- bool isPos[3] = {}, isNeg[3] = {}, isOn[3] = {};
- // Count vertices in the plane
- for(int i = 0; i < 3; i++) {
- if(fabs(dt[i] - d) < LENGTH_EPS) {
- inc++;
- isOn[i] = true;
- } else if(dt[i] > d) {
- posc++;
- isPos[i] = true;
- } else {
- negc++;
- isNeg[i] = true;
- }
- }
+ BspUtil *u = BspUtil::Alloc();
+ u->ClassifyTriangle(tr, this);
// All vertices in-plane
- if(inc == 3) {
- InsertHow(COPLANAR, tr, instead);
+ if(u->onc == 3) {
+ InsertHow(BspClass::COPLANAR, tr, instead);
return;
}
// No split required
- if(posc == 0 || negc == 0) {
- if(inc == 2) {
- Vector a, b;
- if (!isOn[0]) { a = tr->b; b = tr->c; }
- else if(!isOn[1]) { a = tr->c; b = tr->a; }
- else if(!isOn[2]) { a = tr->a; b = tr->b; }
- else oops();
- if(!instead) {
- SEdge se = SEdge::From(a, b);
- edges = SBsp2::InsertOrCreateEdge(edges, &se, n, tr->Normal());
- }
+ if(u->posc == 0 || u->negc == 0) {
+ if(!instead && u->onc == 2) {
+ u->ProcessEdgeInsert();
}
- if(posc > 0) {
- InsertHow(POS, tr, instead);
+ if(u->posc > 0) {
+ InsertHow(BspClass::POS, tr, instead);
} else {
- InsertHow(NEG, tr, instead);
+ InsertHow(BspClass::NEG, tr, instead);
}
return;
}
- // The polygon must be split into two pieces, one above, one below.
- Vector a, b, c;
-
- if(posc == 1 && negc == 1 && inc == 1) {
- bool bpos;
- // Standardize so that a is on the plane
- if (isOn[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = isPos[1];
- } else if(isOn[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = isPos[2];
- } else if(isOn[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = isPos[0];
- } else oops();
-
- Vector bPc = IntersectionWith(b, c);
- STriangle btri = STriangle::From(tr->meta, a, b, bPc);
- STriangle ctri = STriangle::From(tr->meta, c, a, bPc);
-
- if(bpos) {
- InsertHow(POS, &btri, instead);
- InsertHow(NEG, &ctri, instead);
+ // The polygon must be split into two triangles, one above, one below.
+ if(u->posc == 1 && u->negc == 1 && u->onc == 1) {
+ if(u->SplitIntoTwoTriangles(!instead)) {
+ InsertHow(BspClass::POS, u->btri, instead);
+ InsertHow(BspClass::NEG, u->ctri, instead);
} else {
- InsertHow(POS, &ctri, instead);
- InsertHow(NEG, &btri, instead);
- }
-
- if(!instead) {
- SEdge se = SEdge::From(a, bPc);
- edges = SBsp2::InsertOrCreateEdge(edges, &se, n, tr->Normal());
+ InsertHow(BspClass::POS, u->ctri, instead);
+ InsertHow(BspClass::NEG, u->btri, instead);
}
-
return;
}
- if(posc == 2 && negc == 1) {
- // Standardize so that a is on one side, and b and c are on the other.
- if (isNeg[0]) { a = tr->a; b = tr->b; c = tr->c;
- } else if(isNeg[1]) { a = tr->b; b = tr->c; c = tr->a;
- } else if(isNeg[2]) { a = tr->c; b = tr->a; c = tr->b;
- } else oops();
-
- } else if(posc == 1 && negc == 2) {
- if (isPos[0]) { a = tr->a; b = tr->b; c = tr->c;
- } else if(isPos[1]) { a = tr->b; b = tr->c; c = tr->a;
- } else if(isPos[2]) { a = tr->c; b = tr->a; c = tr->b;
- } else oops();
- } else oops();
-
- Vector aPb = IntersectionWith(a, b);
- Vector cPa = IntersectionWith(c, a);
-
- STriangle alone = STriangle::From(tr->meta, a, aPb, cPa);
- Vector quad[4] = { aPb, b, c, cPa };
-
- if(posc == 2 && negc == 1) {
- InsertConvexHow(POS, tr->meta, quad, 4, instead);
- InsertHow(NEG, &alone, instead);
+ // The polygon must be split into two pieces: a triangle and a quad.
+ if(u->SplitIntoTwoPieces(!instead)) {
+ InsertConvexHow(BspClass::POS, tr->meta, u->vpos, 4, instead);
+ InsertHow(BspClass::NEG, u->btri, instead);
} else {
- InsertConvexHow(NEG, tr->meta, quad, 4, instead);
- InsertHow(POS, &alone, instead);
- }
- if(!instead) {
- SEdge se = SEdge::From(aPb, cPa);
- edges = SBsp2::InsertOrCreateEdge(edges, &se, n, alone.Normal());
+ InsertConvexHow(BspClass::NEG, tr->meta, u->vpos, 4, instead);
+ InsertHow(BspClass::POS, u->btri, instead);
}
-
- return;
}
-void SBsp3::GenerateInPaintOrder(SMesh *m) {
-
+void SBsp3::GenerateInPaintOrder(SMesh *m) const {
// Doesn't matter which branch we take if the normal has zero z
// component, so don't need a separate case for that.
if(n.z < 0) {
if(neg) neg->GenerateInPaintOrder(m);
}
- SBsp3 *flip = this;
+ const SBsp3 *flip = this;
while(flip) {
m->AddTriangle(&(flip->tri));
flip = flip->more;
}
}
-void SBsp3::DebugDraw(void) {
-
- if(pos) pos->DebugDraw();
- Vector norm = tri.Normal();
- glNormal3d(norm.x, norm.y, norm.z);
-
- glEnable(GL_DEPTH_TEST);
- glEnable(GL_LIGHTING);
- glBegin(GL_TRIANGLES);
- ssglVertex3v(tri.a);
- ssglVertex3v(tri.b);
- ssglVertex3v(tri.c);
- glEnd();
-
- glDisable(GL_LIGHTING);
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- ssglDepthRangeOffset(2);
- glBegin(GL_TRIANGLES);
- ssglVertex3v(tri.a);
- ssglVertex3v(tri.b);
- ssglVertex3v(tri.c);
- glEnd();
-
- glDisable(GL_LIGHTING);
- glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
- glPointSize(10);
- ssglDepthRangeOffset(2);
- glBegin(GL_TRIANGLES);
- ssglVertex3v(tri.a);
- ssglVertex3v(tri.b);
- ssglVertex3v(tri.c);
- glEnd();
-
- ssglDepthRangeOffset(0);
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-
- if(more) more->DebugDraw();
- if(neg) neg->DebugDraw();
-
- if(edges) edges->DebugDraw(n, d);
-}
-
/////////////////////////////////
-Vector SBsp2::IntersectionWith(Vector a, Vector b) {
+Vector SBsp2::IntersectionWith(Vector a, Vector b) const {
double da = a.Dot(no) - d;
double db = b.Dot(no) - d;
- if(da*db > 0) oops();
+ ssassert(da*db < 0, "Expected segment to intersect BSP node");
double dab = (db - da);
return (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab));
}
return;
}
- oops();
+ ssassert(false, "Impossible");
}
-void SBsp2::InsertTriangleHow(int how, STriangle *tr, SMesh *m, SBsp3 *bsp3) {
+void SBsp2::InsertTriangleHow(BspClass how, STriangle *tr, SMesh *m, SBsp3 *bsp3) {
switch(how) {
- case POS:
+ case BspClass::POS:
if(pos) {
pos->InsertTriangle(tr, m, bsp3);
} else {
- bsp3->InsertInPlane(true, tr, m);
+ bsp3->InsertInPlane(/*pos2=*/true, tr, m);
}
break;
- case NEG:
+ case BspClass::NEG:
if(neg) {
neg->InsertTriangle(tr, m, bsp3);
} else {
- bsp3->InsertInPlane(false, tr, m);
+ bsp3->InsertInPlane(/*pos2=*/false, tr, m);
}
break;
- default: oops();
+ default: ssassert(false, "Unexpected BSP insert type");
}
}
// No split required
if(posc == 0 || negc == 0) {
if(posc > 0) {
- InsertTriangleHow(POS, tr, m, bsp3);
+ InsertTriangleHow(BspClass::POS, tr, m, bsp3);
} else {
- InsertTriangleHow(NEG, tr, m, bsp3);
+ InsertTriangleHow(BspClass::NEG, tr, m, bsp3);
}
return;
}
if (isOn[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = isPos[1];
} else if(isOn[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = isPos[2];
} else if(isOn[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = isPos[0];
- } else oops();
+ } else ssassert(false, "Impossible");
Vector bPc = IntersectionWith(b, c);
STriangle btri = STriangle::From(tr->meta, a, b, bPc);
STriangle ctri = STriangle::From(tr->meta, c, a, bPc);
if(bpos) {
- InsertTriangleHow(POS, &btri, m, bsp3);
- InsertTriangleHow(NEG, &ctri, m, bsp3);
+ InsertTriangleHow(BspClass::POS, &btri, m, bsp3);
+ InsertTriangleHow(BspClass::NEG, &ctri, m, bsp3);
} else {
- InsertTriangleHow(POS, &ctri, m, bsp3);
- InsertTriangleHow(NEG, &btri, m, bsp3);
+ InsertTriangleHow(BspClass::POS, &ctri, m, bsp3);
+ InsertTriangleHow(BspClass::NEG, &btri, m, bsp3);
}
return;
if (isNeg[0]) { a = tr->a; b = tr->b; c = tr->c;
} else if(isNeg[1]) { a = tr->b; b = tr->c; c = tr->a;
} else if(isNeg[2]) { a = tr->c; b = tr->a; c = tr->b;
- } else oops();
+ } else ssassert(false, "Impossible");
} else if(posc == 1 && negc == 2) {
if (isPos[0]) { a = tr->a; b = tr->b; c = tr->c;
} else if(isPos[1]) { a = tr->b; b = tr->c; c = tr->a;
} else if(isPos[2]) { a = tr->c; b = tr->a; c = tr->b;
- } else oops();
- } else oops();
+ } else ssassert(false, "Impossible");
+ } else ssassert(false, "Impossible");
Vector aPb = IntersectionWith(a, b);
Vector cPa = IntersectionWith(c, a);
STriangle quad2 = STriangle::From(tr->meta, aPb, c, cPa);
if(posc == 2 && negc == 1) {
- InsertTriangleHow(POS, &quad1, m, bsp3);
- InsertTriangleHow(POS, &quad2, m, bsp3);
- InsertTriangleHow(NEG, &alone, m, bsp3);
+ InsertTriangleHow(BspClass::POS, &quad1, m, bsp3);
+ InsertTriangleHow(BspClass::POS, &quad2, m, bsp3);
+ InsertTriangleHow(BspClass::NEG, &alone, m, bsp3);
} else {
- InsertTriangleHow(NEG, &quad1, m, bsp3);
- InsertTriangleHow(NEG, &quad2, m, bsp3);
- InsertTriangleHow(POS, &alone, m, bsp3);
+ InsertTriangleHow(BspClass::NEG, &quad1, m, bsp3);
+ InsertTriangleHow(BspClass::NEG, &quad2, m, bsp3);
+ InsertTriangleHow(BspClass::POS, &alone, m, bsp3);
}
return;
}
-
-void SBsp2::DebugDraw(Vector n, double d) {
- if(fabs((edge.a).Dot(n) - d) > LENGTH_EPS) oops();
- if(fabs((edge.b).Dot(n) - d) > LENGTH_EPS) oops();
-
- ssglLineWidth(10);
- glBegin(GL_LINES);
- ssglVertex3v(edge.a);
- ssglVertex3v(edge.b);
- glEnd();
- if(pos) pos->DebugDraw(n, d);
- if(neg) neg->DebugDraw(n, d);
- if(more) more->DebugDraw(n, d);
- ssglLineWidth(1);
-}
-
//-----------------------------------------------------------------------------
#include "solvespace.h"
-void SolveSpaceUI::Clipboard::Clear(void) {
+void SolveSpaceUI::Clipboard::Clear() {
c.Clear();
r.Clear();
}
bool SolveSpaceUI::Clipboard::ContainsEntity(hEntity he) {
- if(he.v == Entity::NO_ENTITY.v)
+ if(he == Entity::NO_ENTITY)
return true;
ClipboardRequest *cr;
for(cr = r.First(); cr; cr = r.NextAfter(cr)) {
- if(cr->oldEnt.v == he.v)
+ if(cr->oldEnt == he)
return true;
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
- if(cr->oldPointEnt[i].v == he.v)
+ if(cr->oldPointEnt[i] == he)
return true;
}
}
}
hEntity SolveSpaceUI::Clipboard::NewEntityFor(hEntity he) {
- if(he.v == Entity::NO_ENTITY.v)
+ if(he == Entity::NO_ENTITY)
return Entity::NO_ENTITY;
ClipboardRequest *cr;
for(cr = r.First(); cr; cr = r.NextAfter(cr)) {
- if(cr->oldEnt.v == he.v)
+ if(cr->oldEnt == he)
return cr->newReq.entity(0);
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
- if(cr->oldPointEnt[i].v == he.v)
+ if(cr->oldPointEnt[i] == he)
return cr->newReq.entity(1+i);
}
}
- oops();
+
+ ssassert(false, "Expected to find entity in some clipboard request");
}
-void GraphicsWindow::DeleteSelection(void) {
+void GraphicsWindow::DeleteSelection() {
SK.request.ClearTags();
SK.constraint.ClearTags();
List<Selection> *ls = &(selection);
DeleteTaggedRequests();
}
-void GraphicsWindow::CopySelection(void) {
+void GraphicsWindow::CopySelection() {
SS.clipboard.Clear();
Entity *wrkpl = SK.GetEntity(ActiveWorkplane());
// Work only on entities that have requests that will generate them.
Entity *e = SK.GetEntity(s->entity);
bool hasDistance;
- int req, pts;
+ Request::Type req;
+ int pts;
if(!EntReqTable::GetEntityInfo(e->type, e->extraPoints,
&req, &pts, NULL, &hasDistance))
{
- continue;
+ if(!e->h.isFromRequest()) continue;
+ Request *r = SK.GetRequest(e->h.request());
+ if(r->type != Request::Type::DATUM_POINT) continue;
+ EntReqTable::GetEntityInfo((Entity::Type)0, e->extraPoints,
+ &req, &pts, NULL, &hasDistance);
}
- if(req == Request::WORKPLANE) continue;
+ if(req == Request::Type::WORKPLANE) continue;
ClipboardRequest cr = {};
cr.type = req;
cr.style = e->style;
cr.str = e->str;
cr.font = e->font;
+ cr.file = e->file;
cr.construction = e->construction;
{for(int i = 0; i < pts; i++) {
- Vector pt = SK.GetEntity(e->point[i])->PointGetNum();
+ Vector pt;
+ if(req == Request::Type::DATUM_POINT) {
+ pt = e->PointGetNum();
+ } else {
+ pt = SK.GetEntity(e->point[i])->PointGetNum();
+ }
pt = pt.Minus(p);
pt = pt.DotInToCsys(u, v, n);
cr.point[i] = pt;
if(!s->constraint.v) continue;
Constraint *c = SK.GetConstraint(s->constraint);
- if(c->type == Constraint::COMMENT) {
+ if(c->type == Constraint::Type::COMMENT) {
SS.clipboard.c.Add(c);
}
}
!SS.clipboard.ContainsEntity(c->entityB) ||
!SS.clipboard.ContainsEntity(c->entityC) ||
!SS.clipboard.ContainsEntity(c->entityD) ||
- c->type == Constraint::COMMENT) {
+ c->type == Constraint::Type::COMMENT) {
continue;
}
SS.clipboard.c.Add(c);
n = wrkpln->NormalN(),
p = SK.GetEntity(wrkpl->point[0])->PointGetNum();
+ // For arcs, reflection involves swapping the endpoints, or otherwise
+ // the arc gets inverted.
+ auto mapPoint = [scale](hEntity he) {
+ if(he.v == 0) return he;
+
+ if(scale < 0) {
+ hRequest hr = he.request();
+ Request *r = SK.GetRequest(hr);
+ if(r->type == Request::Type::ARC_OF_CIRCLE) {
+ if(he == hr.entity(2)) {
+ return hr.entity(3);
+ } else if(he == hr.entity(3)) {
+ return hr.entity(2);
+ }
+ }
+ }
+ return he;
+ };
+
ClipboardRequest *cr;
for(cr = SS.clipboard.r.First(); cr; cr = SS.clipboard.r.NextAfter(cr)) {
- hRequest hr = AddRequest(cr->type, false);
+ hRequest hr = AddRequest(cr->type, /*rememberForUndo=*/false);
Request *r = SK.GetRequest(hr);
r->extraPoints = cr->extraPoints;
r->style = cr->style;
r->str = cr->str;
r->font = cr->font;
+ r->file = cr->file;
r->construction = cr->construction;
// Need to regen to get the right number of points, if extraPoints
// changed.
- SS.GenerateAll(SolveSpaceUI::GENERATE_REGEN);
+ SS.GenerateAll(SolveSpaceUI::Generate::REGEN);
SS.MarkGroupDirty(r->group);
bool hasDistance;
int i, pts;
pt = pt.Plus(p);
pt = pt.RotatedAbout(n, theta);
pt = pt.Plus(trans);
- SK.GetEntity(hr.entity(i+1))->PointForceTo(pt);
+ int j = (r->type == Request::Type::DATUM_POINT) ? i : i + 1;
+ SK.GetEntity(mapPoint(hr.entity(j)))->PointForceTo(pt);
}
if(hasDistance) {
SK.GetEntity(hr.entity(64))->DistanceForceTo(
cr->newReq = hr;
MakeSelected(hr.entity(0));
for(i = 0; i < pts; i++) {
- MakeSelected(hr.entity(i+1));
+ int j = (r->type == Request::Type::DATUM_POINT) ? i : i + 1;
+ MakeSelected(hr.entity(j));
}
}
c.workplane = SS.GW.ActiveWorkplane();
c.type = cc->type;
c.valA = cc->valA;
- c.ptA = SS.clipboard.NewEntityFor(cc->ptA);
- c.ptB = SS.clipboard.NewEntityFor(cc->ptB);
+ c.ptA = SS.clipboard.NewEntityFor(mapPoint(cc->ptA));
+ c.ptB = SS.clipboard.NewEntityFor(mapPoint(cc->ptB));
c.entityA = SS.clipboard.NewEntityFor(cc->entityA);
c.entityB = SS.clipboard.NewEntityFor(cc->entityB);
c.entityC = SS.clipboard.NewEntityFor(cc->entityC);
c.disp = cc->disp;
c.comment = cc->comment;
switch(c.type) {
- case Constraint::COMMENT:
+ case Constraint::Type::COMMENT:
c.disp.offset = c.disp.offset.Plus(trans);
break;
- case Constraint::PT_PT_DISTANCE:
- case Constraint::PT_LINE_DISTANCE:
- case Constraint::PROJ_PT_DISTANCE:
- case Constraint::DIAMETER:
+ case Constraint::Type::PT_PT_DISTANCE:
+ case Constraint::Type::PT_LINE_DISTANCE:
+ case Constraint::Type::PROJ_PT_DISTANCE:
+ case Constraint::Type::DIAMETER:
c.valA *= fabs(scale);
break;
}
hConstraint hc = Constraint::AddConstraint(&c, /*rememberForUndo=*/false);
- if(c.type == Constraint::COMMENT) {
+ if(c.type == Constraint::Type::COMMENT) {
MakeSelected(hc);
}
}
-
- SS.ScheduleGenerateAll();
}
-void GraphicsWindow::MenuClipboard(int id) {
- if(id != MNU_DELETE && !SS.GW.LockedInWorkplane()) {
- Error("Cut, paste, and copy work only in a workplane.\n\n"
- "Select one with Sketch -> In Workplane.");
+void GraphicsWindow::MenuClipboard(Command id) {
+ if(id != Command::DELETE && !SS.GW.LockedInWorkplane()) {
+ Error(_("Cut, paste, and copy work only in a workplane.\n\n"
+ "Activate one with Sketch -> In Workplane."));
return;
}
switch(id) {
- case MNU_PASTE: {
+ case Command::PASTE: {
SS.UndoRemember();
Vector trans = SS.GW.projRight.ScaledBy(80/SS.GW.scale).Plus(
SS.GW.projUp .ScaledBy(40/SS.GW.scale));
break;
}
- case MNU_PASTE_TRANSFORM: {
- if(SS.clipboard.r.n == 0) {
- Error("Clipboard is empty; nothing to paste.");
+ case Command::PASTE_TRANSFORM: {
+ if(SS.clipboard.r.IsEmpty()) {
+ Error(_("Clipboard is empty; nothing to paste."));
break;
}
SS.TW.shown.paste.theta = 0;
SS.TW.shown.paste.origin = p;
SS.TW.shown.paste.scale = 1;
- SS.TW.GoToScreen(TextWindow::SCREEN_PASTE_TRANSFORMED);
+ SS.TW.GoToScreen(TextWindow::Screen::PASTE_TRANSFORMED);
SS.GW.ForceTextWindowShown();
SS.ScheduleShowTW();
break;
}
- case MNU_COPY:
+ case Command::COPY:
SS.GW.CopySelection();
SS.GW.ClearSelection();
break;
- case MNU_CUT:
+ case Command::CUT:
SS.UndoRemember();
SS.GW.CopySelection();
SS.GW.DeleteSelection();
break;
- case MNU_DELETE:
+ case Command::DELETE:
SS.UndoRemember();
SS.GW.DeleteSelection();
break;
- default: oops();
+ default: ssassert(false, "Unexpected menu ID");
}
}
-bool TextWindow::EditControlDoneForPaste(const char *s) {
+bool TextWindow::EditControlDoneForPaste(const std::string &s) {
Expr *e;
switch(edit.meaning) {
- case EDIT_PASTE_TIMES_REPEATED: {
- e = Expr::From(s, true);
+ case Edit::PASTE_TIMES_REPEATED: {
+ e = Expr::From(s, /*popUpError=*/true);
if(!e) break;
int v = (int)e->Eval();
if(v > 0) {
shown.paste.times = v;
} else {
- Error("Number of copies to paste must be at least one.");
+ Error(_("Number of copies to paste must be at least one."));
}
break;
}
- case EDIT_PASTE_ANGLE:
- e = Expr::From(s, true);
+ case Edit::PASTE_ANGLE:
+ e = Expr::From(s, /*popUpError=*/true);
if(!e) break;
shown.paste.theta = WRAP_SYMMETRIC((e->Eval())*PI/180, 2*PI);
break;
- case EDIT_PASTE_SCALE: {
- e = Expr::From(s, true);
+ case Edit::PASTE_SCALE: {
+ e = Expr::From(s, /*popUpError=*/true);
double v = e->Eval();
if(fabs(v) > 1e-6) {
- shown.paste.scale = v;
+ shown.paste.scale = shown.paste.scale < 0 ? -v : v;
} else {
- Error("Scale cannot be zero.");
+ Error(_("Scale cannot be zero."));
}
break;
}
switch(link) {
case 't':
SS.TW.ShowEditControl(13, ssprintf("%d", SS.TW.shown.paste.times));
- SS.TW.edit.meaning = EDIT_PASTE_TIMES_REPEATED;
+ SS.TW.edit.meaning = Edit::PASTE_TIMES_REPEATED;
break;
case 'r':
SS.TW.ShowEditControl(13, ssprintf("%.3f", SS.TW.shown.paste.theta*180/PI));
- SS.TW.edit.meaning = EDIT_PASTE_ANGLE;
+ SS.TW.edit.meaning = Edit::PASTE_ANGLE;
break;
case 's':
- SS.TW.ShowEditControl(13, ssprintf("%.3f", SS.TW.shown.paste.scale));
- SS.TW.edit.meaning = EDIT_PASTE_SCALE;
+ SS.TW.ShowEditControl(13, ssprintf("%.3f", fabs(SS.TW.shown.paste.scale)));
+ SS.TW.edit.meaning = Edit::PASTE_SCALE;
+ break;
+
+ case 'f':
+ SS.TW.shown.paste.scale *= -1;
break;
}
}
Entity *e = SK.GetEntity(SS.GW.gs.point[0]);
SS.TW.shown.paste.origin = e->PointGetNum();
} else {
- Error("Select one point to define origin of rotation.");
+ Error(_("Select one point to define origin of rotation."));
}
SS.GW.ClearSelection();
break;
SS.TW.shown.paste.trans =
(pb->PointGetNum()).Minus(pa->PointGetNum());
} else {
- Error("Select two points to define translation vector.");
+ Error(_("Select two points to define translation vector."));
}
SS.GW.ClearSelection();
break;
SS.TW.shown.paste.trans.Magnitude() < LENGTH_EPS &&
SS.TW.shown.paste.times != 1)
{
- Message("Transformation is identity. So all copies will be "
- "exactly on top of each other.");
+ Message(_("Transformation is identity. So all copies will be "
+ "exactly on top of each other."));
}
if(SS.TW.shown.paste.times*SS.clipboard.r.n > 100) {
- Error("Too many items to paste; split this into smaller "
- "pastes.");
+ Error(_("Too many items to paste; split this into smaller "
+ "pastes."));
break;
}
if(!SS.GW.LockedInWorkplane()) {
- Error("No workplane active.");
+ Error(_("No workplane active."));
break;
}
Entity *wrkpl = SK.GetEntity(SS.GW.ActiveWorkplane());
SS.GW.PasteClipboard(t, theta, SS.TW.shown.paste.scale);
}
- SS.TW.GoToScreen(SCREEN_LIST_OF_GROUPS);
+ SS.TW.GoToScreen(Screen::LIST_OF_GROUPS);
SS.ScheduleShowTW();
break;
}
}
}
-void TextWindow::ShowPasteTransformed(void) {
+void TextWindow::ShowPasteTransformed() {
Printf(true, "%FtPASTE TRANSFORMED%E");
Printf(true, "%Ba %Ftrepeat%E %d time%s %Fl%Lt%f[change]%E",
shown.paste.times, (shown.paste.times == 1) ? "" : "s",
SS.MmToString(shown.paste.trans.z).c_str(),
&ScreenPasteTransformed);
Printf(false, "%Ba %Ftscale%E %@ %Fl%Ls%f[change]%E",
- shown.paste.scale,
+ fabs(shown.paste.scale),
&ScreenChangePasteTransformed);
+ Printf(false, "%Ba %Ftmirror%E %Fd%Lf%f%s flip%E",
+ &ScreenChangePasteTransformed,
+ shown.paste.scale < 0 ? CHECK_TRUE : CHECK_FALSE);
Printf(true, " %Fl%Lg%fpaste transformed now%E", &ScreenPasteTransformed);
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="12F45" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
- <dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
- </dependencies>
- <objects>
- <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
- <customObject id="-2" userLabel="File's Owner" customClass="NSApplication"/>
- <customObject id="-3" userLabel="Application"/>
- <customObject id="-4" userLabel="Application Delegate" customClass="ApplicationDelegate"/>
- <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
- <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
- <items>
- <menuItem title="SolveSpace" id="1Xt-HY-uBw">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="SolveSpace" systemMenu="apple" id="uQy-DD-JDr">
- <items>
- <menuItem title="About SolveSpace" id="5kV-Vb-QxS">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
- <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW">
- <connections>
- <action selector="preferences:" target="-4" id="xyv-2f-7kO"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
- <menuItem title="Services" id="NMo-om-nkz">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
- <menuItem title="Hide SolveSpace" keyEquivalent="h" id="Olw-nP-bQN">
- <connections>
- <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
- </connections>
- </menuItem>
- <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
- <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
- <connections>
- <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
- </connections>
- </menuItem>
- <menuItem title="Show All" id="Kd2-mp-pUS">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
- <menuItem title="Quit SolveSpace" keyEquivalent="q" id="4sb-4s-VLi">
- <connections>
- <action selector="terminate:" target="-3" id="Te7-pn-YzF"/>
- </connections>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- </items>
- </menu>
- </objects>
-</document>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="12F45" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
- <dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
- <capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
- </dependencies>
- <objects>
- <customObject id="-2" userLabel="File's Owner" customClass="NSViewController">
- <connections>
- <outlet property="button" destination="nNy-fR-AhK" id="w3z-a4-Khs"/>
- <outlet property="view" destination="c22-O7-iKe" id="w2z-a4-Khs"/>
- </connections>
- </customObject>
- <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
- <customObject id="-3" userLabel="Application"/>
- <customView id="c22-O7-iKe">
- <rect key="frame" x="0.0" y="0.0" width="294" height="51"/>
- <subviews>
- <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nNy-fR-AhK">
- <rect key="frame" x="105" y="12" width="34" height="26"/>
- <popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="f8z-Qp-Igm">
- <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="menu"/>
- <menu key="menu" title="OtherViews" id="VgN-HZ-Q4a"/>
- </popUpButtonCell>
- <connections>
- <binding destination="-2" name="selectedIndex" keyPath="index" id="AXx-uh-tee"/>
- </connections>
- </popUpButton>
- <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="z9Z-cA-QIW">
- <rect key="frame" x="17" y="17" width="73" height="17"/>
- <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="File Format:" id="2vD-ht-BhF">
- <font key="font" metaFont="system"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- </subviews>
- <constraints>
- <constraint firstItem="nNy-fR-AhK" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" constant="15" id="B3P-9V-lvu"/>
- <constraint firstItem="nNy-fR-AhK" firstAttribute="leading" secondItem="z9Z-cA-QIW" secondAttribute="trailing" constant="20" id="Uex-s1-7UF"/>
- <constraint firstAttribute="bottom" secondItem="nNy-fR-AhK" secondAttribute="bottom" constant="15" id="pPM-eQ-gOD"/>
- <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="nNy-fR-AhK" secondAttribute="trailing" constant="20" id="rPT-bl-Md7"/>
- <constraint firstItem="z9Z-cA-QIW" firstAttribute="baseline" secondItem="nNy-fR-AhK" secondAttribute="baseline" constant="1" id="spD-eU-Frq"/>
- <constraint firstItem="nNy-fR-AhK" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="centerX" constant="-40" id="whX-1w-qMB"/>
- </constraints>
- </customView>
- <userDefaultsController representsSharedInstance="YES" id="ybp-e7-hms"/>
- </objects>
-</document>
+++ /dev/null
-//-----------------------------------------------------------------------------
-// Our main() function, and Cocoa-specific stuff to set up our windows and
-// otherwise handle our interface to the operating system. Everything
-// outside gtk/... should be standard C++ and OpenGL.
-//
-// Copyright 2015 <whitequark@whitequark.org>
-//-----------------------------------------------------------------------------
-#include <mach/mach.h>
-#include <mach/clock.h>
-
-#import <AppKit/AppKit.h>
-
-#include <iostream>
-#include <map>
-
-#include "solvespace.h"
-#include "../unix/gloffscreen.h"
-#include <config.h>
-
-using SolveSpace::dbp;
-
-#define GL_CHECK() \
- do { \
- int err = (int)glGetError(); \
- if(err) dbp("%s:%d: glGetError() == 0x%X", __FILE__, __LINE__, err); \
- } while (0)
-
-/* Settings */
-
-namespace SolveSpace {
-void CnfFreezeInt(uint32_t val, const std::string &key) {
- [[NSUserDefaults standardUserDefaults]
- setInteger:val forKey:[NSString stringWithUTF8String:key.c_str()]];
-}
-
-uint32_t CnfThawInt(uint32_t val, const std::string &key) {
- NSString *nsKey = [NSString stringWithUTF8String:key.c_str()];
- if([[NSUserDefaults standardUserDefaults] objectForKey:nsKey])
- return [[NSUserDefaults standardUserDefaults] integerForKey:nsKey];
- return val;
-}
-
-void CnfFreezeFloat(float val, const std::string &key) {
- [[NSUserDefaults standardUserDefaults]
- setFloat:val forKey:[NSString stringWithUTF8String:key.c_str()]];
-}
-
-float CnfThawFloat(float val, const std::string &key) {
- NSString *nsKey = [NSString stringWithUTF8String:key.c_str()];
- if([[NSUserDefaults standardUserDefaults] objectForKey:nsKey])
- return [[NSUserDefaults standardUserDefaults] floatForKey:nsKey];
- return val;
-}
-
-void CnfFreezeString(const std::string &val, const std::string &key) {
- [[NSUserDefaults standardUserDefaults]
- setObject:[NSString stringWithUTF8String:val.c_str()]
- forKey:[NSString stringWithUTF8String:key.c_str()]];
-}
-
-std::string CnfThawString(const std::string &val, const std::string &key) {
- NSString *nsKey = [NSString stringWithUTF8String:key.c_str()];
- if([[NSUserDefaults standardUserDefaults] objectForKey:nsKey]) {
- NSString *nsNewVal = [[NSUserDefaults standardUserDefaults] stringForKey:nsKey];
- return [nsNewVal UTF8String];
- }
- return val;
-}
-};
-
-/* Timer */
-
-int64_t SolveSpace::GetMilliseconds(void) {
- clock_serv_t cclock;
- mach_timespec_t mts;
-
- host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
- clock_get_time(cclock, &mts);
- mach_port_deallocate(mach_task_self(), cclock);
-
- return mts.tv_sec * 1000 + mts.tv_nsec / 1000000;
-}
-
-@interface DeferredHandler : NSObject
-+ (void) runLater:(id)dummy;
-+ (void) runCallback;
-+ (void) doAutosave;
-@end
-
-@implementation DeferredHandler
-+ (void) runLater:(id)dummy {
- SolveSpace::SS.DoLater();
-}
-+ (void) runCallback {
- SolveSpace::SS.GW.TimerCallback();
- SolveSpace::SS.TW.TimerCallback();
-}
-+ (void) doAutosave {
- SolveSpace::SS.Autosave();
-}
-@end
-
-static void Schedule(SEL selector, double interval) {
- NSMethodSignature *signature = [[DeferredHandler class]
- methodSignatureForSelector:selector];
- NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
- [invocation setSelector:selector];
- [invocation setTarget:[DeferredHandler class]];
- [NSTimer scheduledTimerWithTimeInterval:interval
- invocation:invocation repeats:NO];
-}
-
-void SolveSpace::SetTimerFor(int milliseconds) {
- Schedule(@selector(runCallback), milliseconds / 1000.0);
-}
-
-void SolveSpace::SetAutosaveTimerFor(int minutes) {
- Schedule(@selector(doAutosave), minutes * 60.0);
-}
-
-void SolveSpace::ScheduleLater() {
- [[NSRunLoop currentRunLoop]
- performSelector:@selector(runLater:)
- target:[DeferredHandler class] argument:nil
- order:0 modes:@[NSDefaultRunLoopMode]];
-}
-
-/* OpenGL view */
-
-@interface GLViewWithEditor : NSView
-- (void)drawGL;
-
-@property BOOL wantsBackingStoreScaling;
-
-@property(readonly, getter=isEditing) BOOL editing;
-- (void)startEditing:(NSString*)text at:(NSPoint)origin withHeight:(double)fontHeight
- usingMonospace:(BOOL)isMonospace;
-- (void)stopEditing;
-- (void)didEdit:(NSString*)text;
-@end
-
-@implementation GLViewWithEditor
-{
- GLOffscreen *offscreen;
- NSOpenGLContext *glContext;
-@protected
- NSTextField *editor;
-}
-
-- initWithFrame:(NSRect)frameRect {
- self = [super initWithFrame:frameRect];
- [self setWantsLayer:YES];
-
- NSOpenGLPixelFormatAttribute attrs[] = {
- NSOpenGLPFAColorSize, 24,
- NSOpenGLPFADepthSize, 24,
- 0
- };
- NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
- glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:NULL];
-
- editor = [[NSTextField alloc] init];
- [editor setEditable:YES];
- [[editor cell] setWraps:NO];
- [[editor cell] setScrollable:YES];
- [editor setBezeled:NO];
- [editor setTarget:self];
- [editor setAction:@selector(editorAction:)];
-
- return self;
-}
-
-- (void)dealloc {
- delete offscreen;
-}
-
-#define CONVERT1(name, to_from) \
- - (NS##name)convert##name##to_from##Backing:(NS##name)input { \
- return _wantsBackingStoreScaling ? [super convert##name##to_from##Backing:input] : input; }
-#define CONVERT(name) CONVERT1(name, To) CONVERT1(name, From)
-CONVERT(Size)
-CONVERT(Rect)
-#undef CONVERT
-#undef CONVERT1
-
-- (NSPoint)convertPointToBacking:(NSPoint)input {
- if(_wantsBackingStoreScaling) return [super convertPointToBacking:input];
- else {
- input.y *= -1;
- return input;
- }
-}
-
-- (NSPoint)convertPointFromBacking:(NSPoint)input {
- if(_wantsBackingStoreScaling) return [super convertPointFromBacking:input];
- else {
- input.y *= -1;
- return input;
- }
-}
-
-- (void)drawRect:(NSRect)aRect {
- [glContext makeCurrentContext];
-
- if(!offscreen)
- offscreen = new GLOffscreen;
-
- NSSize size = [self convertSizeToBacking:[self bounds].size];
- offscreen->begin(size.width, size.height);
-
- [self drawGL];
- GL_CHECK();
-
- uint8_t *pixels = offscreen->end(![self isFlipped]);
- CGDataProviderRef provider = CGDataProviderCreateWithData(
- NULL, pixels, size.width * size.height * 4, NULL);
- CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
- CGImageRef image = CGImageCreate(size.width, size.height, 8, 32,
- size.width * 4, colorspace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
- provider, NULL, true, kCGRenderingIntentDefault);
-
- CGContextDrawImage((CGContextRef) [[NSGraphicsContext currentContext] graphicsPort],
- [self bounds], image);
-
- CGImageRelease(image);
- CGDataProviderRelease(provider);
-}
-
-- (void)drawGL {
-}
-
-@synthesize editing;
-
-- (void)startEditing:(NSString*)text at:(NSPoint)origin withHeight:(double)fontHeight
- usingMonospace:(BOOL)isMonospace {
- if(!self->editing) {
- [self addSubview:editor];
- self->editing = YES;
- }
-
- NSFont *font;
- if(isMonospace)
- font = [NSFont fontWithName:@"Monaco" size:fontHeight];
- else
- font = [NSFont controlContentFontOfSize:fontHeight];
- [editor setFont:font];
-
- origin.x -= 3; /* left padding; no way to get it from NSTextField */
- origin.y -= [editor intrinsicContentSize].height;
- origin.y += [editor baselineOffsetFromBottom];
-
- [editor setFrameOrigin:origin];
- [editor setStringValue:text];
- [[self window] makeFirstResponder:editor];
-}
-
-- (void)stopEditing {
- if(self->editing) {
- [editor removeFromSuperview];
- self->editing = NO;
- }
-}
-
-- (void)editorAction:(id)sender {
- [self didEdit:[editor stringValue]];
- [self stopEditing];
-}
-
-- (void)didEdit:(NSString*)text {
-}
-@end
-
-/* Graphics window */
-
-@interface GraphicsWindowView : GLViewWithEditor
-{
- NSTrackingArea *trackingArea;
-}
-
-@property(readonly) NSEvent *lastContextMenuEvent;
-@end
-
-@implementation GraphicsWindowView
-- (BOOL)isFlipped {
- return YES;
-}
-
-- (void)drawGL {
- SolveSpace::SS.GW.Paint();
-}
-
-- (BOOL)acceptsFirstResponder {
- return YES;
-}
-
-- (void) createTrackingArea {
- trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
- options:(NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved |
- NSTrackingActiveInKeyWindow)
- owner:self userInfo:nil];
- [self addTrackingArea:trackingArea];
-}
-
-- (void) updateTrackingAreas
-{
- [self removeTrackingArea:trackingArea];
- [self createTrackingArea];
- [super updateTrackingAreas];
-}
-
-- (void)mouseMoved:(NSEvent*)event {
- NSPoint point = [self ij_to_xy:[self convertPoint:[event locationInWindow] fromView:nil]];
- NSUInteger flags = [event modifierFlags];
- NSUInteger buttons = [NSEvent pressedMouseButtons];
- SolveSpace::SS.GW.MouseMoved(point.x, point.y,
- buttons & (1 << 0),
- buttons & (1 << 2),
- buttons & (1 << 1),
- flags & NSShiftKeyMask,
- flags & NSCommandKeyMask);
-}
-
-- (void)mouseDragged:(NSEvent*)event {
- [self mouseMoved:event];
-}
-
-- (void)rightMouseDragged:(NSEvent*)event {
- [self mouseMoved:event];
-}
-
-- (void)otherMouseDragged:(NSEvent*)event {
- [self mouseMoved:event];
-}
-
-- (void)mouseDown:(NSEvent*)event {
- NSPoint point = [self ij_to_xy:[self convertPoint:[event locationInWindow] fromView:nil]];
- if([event clickCount] == 1)
- SolveSpace::SS.GW.MouseLeftDown(point.x, point.y);
- else if([event clickCount] == 2)
- SolveSpace::SS.GW.MouseLeftDoubleClick(point.x, point.y);
-}
-
-- (void)rightMouseDown:(NSEvent*)event {
- NSPoint point = [self ij_to_xy:[self convertPoint:[event locationInWindow] fromView:nil]];
- SolveSpace::SS.GW.MouseMiddleOrRightDown(point.x, point.y);
-}
-
-- (void)otherMouseDown:(NSEvent*)event {
- [self rightMouseDown:event];
-}
-
-- (void)mouseUp:(NSEvent*)event {
- NSPoint point = [self ij_to_xy:[self convertPoint:[event locationInWindow] fromView:nil]];
- SolveSpace::SS.GW.MouseLeftUp(point.x, point.y);
-}
-
-- (void)rightMouseUp:(NSEvent*)event {
- NSPoint point = [self ij_to_xy:[self convertPoint:[event locationInWindow] fromView:nil]];
- self->_lastContextMenuEvent = event;
- SolveSpace::SS.GW.MouseRightUp(point.x, point.y);
-}
-
-- (void)scrollWheel:(NSEvent*)event {
- NSPoint point = [self ij_to_xy:[self convertPoint:[event locationInWindow] fromView:nil]];
- SolveSpace::SS.GW.MouseScroll(point.x, point.y, -[event deltaY]);
-}
-
-- (void)mouseExited:(NSEvent*)event {
- SolveSpace::SS.GW.MouseLeave();
-}
-
-- (void)keyDown:(NSEvent*)event {
- int chr = 0;
- if(NSString *nsChr = [event charactersIgnoringModifiers])
- chr = [nsChr characterAtIndex:0];
-
- if(chr >= NSF1FunctionKey && chr <= NSF12FunctionKey)
- chr = SolveSpace::GraphicsWindow::FUNCTION_KEY_BASE + (chr - NSF1FunctionKey);
-
- NSUInteger flags = [event modifierFlags];
- if(flags & NSShiftKeyMask)
- chr |= SolveSpace::GraphicsWindow::SHIFT_MASK;
- if(flags & NSCommandKeyMask)
- chr |= SolveSpace::GraphicsWindow::CTRL_MASK;
-
- // override builtin behavior: "focus on next cell", "close window"
- if(chr == '\t' || chr == '\x1b')
- [[NSApp mainMenu] performKeyEquivalent:event];
- else if(!chr || !SolveSpace::SS.GW.KeyDown(chr))
- [super keyDown:event];
-}
-
-- (void)startEditing:(NSString*)text at:(NSPoint)xy withHeight:(double)fontHeight
- withMinWidthInChars:(int)minWidthChars {
- // Convert to ij (vs. xy) style coordinates
- NSSize size = [self convertSizeToBacking:[self bounds].size];
- NSPoint point = {
- .x = xy.x + size.width / 2,
- .y = xy.y - size.height / 2
- };
- [[self window] makeKeyWindow];
- [super startEditing:text at:[self convertPointFromBacking:point]
- withHeight:fontHeight usingMonospace:FALSE];
- [self prepareEditorWithMinWidthInChars:minWidthChars];
-}
-
-- (void)prepareEditorWithMinWidthInChars:(int)minWidthChars {
- NSFont *font = [editor font];
- NSGlyph glyphA = [font glyphWithName:@"a"];
- if(glyphA == -1) oops();
- CGFloat glyphAWidth = [font advancementForGlyph:glyphA].width;
-
- [editor sizeToFit];
-
- NSSize frameSize = [editor frame].size;
- frameSize.width = std::max(frameSize.width, glyphAWidth * minWidthChars);
- [editor setFrameSize:frameSize];
-}
-
-- (void)didEdit:(NSString*)text {
- SolveSpace::SS.GW.EditControlDone([text UTF8String]);
- [self setNeedsDisplay:YES];
-}
-
-- (void)cancelOperation:(id)sender {
- [self stopEditing];
-}
-
-- (NSPoint)ij_to_xy:(NSPoint)ij {
- // Convert to xy (vs. ij) style coordinates,
- // with (0, 0) at center
- NSSize size = [self bounds].size;
- return [self convertPointToBacking:(NSPoint){
- .x = ij.x - size.width / 2, .y = ij.y - size.height / 2 }];
-}
-@end
-
-@interface GraphicsWindowDelegate : NSObject<NSWindowDelegate>
-- (BOOL)windowShouldClose:(id)sender;
-
-@property(readonly, getter=isFullscreen) BOOL fullscreen;
-- (void)windowDidEnterFullScreen:(NSNotification *)notification;
-- (void)windowDidExitFullScreen:(NSNotification *)notification;
-@end
-
-@implementation GraphicsWindowDelegate
-- (BOOL)windowShouldClose:(id)sender {
- [NSApp terminate:sender];
- return FALSE; /* in case NSApp changes its mind */
-}
-
-@synthesize fullscreen;
-- (void)windowDidEnterFullScreen:(NSNotification *)notification {
- fullscreen = true;
- /* Update the menus */
- SolveSpace::SS.GW.EnsureValidActives();
-}
-- (void)windowDidExitFullScreen:(NSNotification *)notification {
- fullscreen = false;
- /* Update the menus */
- SolveSpace::SS.GW.EnsureValidActives();
-}
-@end
-
-static NSWindow *GW;
-static GraphicsWindowView *GWView;
-static GraphicsWindowDelegate *GWDelegate;
-
-namespace SolveSpace {
-void InitGraphicsWindow() {
- GW = [[NSWindow alloc] init];
- GWDelegate = [[GraphicsWindowDelegate alloc] init];
- [GW setDelegate:GWDelegate];
- [GW setStyleMask:(NSTitledWindowMask | NSClosableWindowMask |
- NSMiniaturizableWindowMask | NSResizableWindowMask)];
- [GW setFrameAutosaveName:@"GraphicsWindow"];
- [GW setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
- if(![GW setFrameUsingName:[GW frameAutosaveName]])
- [GW setContentSize:(NSSize){ .width = 600, .height = 600 }];
- GWView = [[GraphicsWindowView alloc] init];
- [GW setContentView:GWView];
-}
-
-void GetGraphicsWindowSize(int *w, int *h) {
- NSSize size = [GWView convertSizeToBacking:[GWView frame].size];
- *w = size.width;
- *h = size.height;
-}
-
-void InvalidateGraphics(void) {
- [GWView setNeedsDisplay:YES];
-}
-
-void PaintGraphics(void) {
- [GWView setNeedsDisplay:YES];
- CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, YES);
-}
-
-void SetCurrentFilename(const std::string &filename) {
- if(!filename.empty()) {
- [GW setTitleWithRepresentedFilename:[NSString stringWithUTF8String:filename.c_str()]];
- } else {
- [GW setTitle:@"(new sketch)"];
- [GW setRepresentedFilename:@""];
- }
-}
-
-void ToggleFullScreen(void) {
- [GW toggleFullScreen:nil];
-}
-
-bool FullScreenIsActive(void) {
- return [GWDelegate isFullscreen];
-}
-
-void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars,
- const std::string &str) {
- [GWView startEditing:[NSString stringWithUTF8String:str.c_str()]
- at:(NSPoint){(CGFloat)x, (CGFloat)y}
- withHeight:fontHeight
- withMinWidthInChars:minWidthChars];
-}
-
-void HideGraphicsEditControl(void) {
- [GWView stopEditing];
-}
-
-bool GraphicsEditControlIsVisible(void) {
- return [GWView isEditing];
-}
-}
-
-/* Context menus */
-
-static int contextMenuChoice;
-
-@interface ContextMenuResponder : NSObject
-+ (void)handleClick:(id)sender;
-@end
-
-@implementation ContextMenuResponder
-+ (void)handleClick:(id)sender {
- contextMenuChoice = [sender tag];
-}
-@end
-
-namespace SolveSpace {
-NSMenu *contextMenu, *contextSubmenu;
-
-void AddContextMenuItem(const char *label, int id_) {
- NSMenuItem *menuItem;
- if(label) {
- menuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithUTF8String:label]
- action:@selector(handleClick:) keyEquivalent:@""];
- [menuItem setTarget:[ContextMenuResponder class]];
- [menuItem setTag:id_];
- } else {
- menuItem = [NSMenuItem separatorItem];
- }
-
- if(id_ == CONTEXT_SUBMENU) {
- [menuItem setSubmenu:contextSubmenu];
- contextSubmenu = nil;
- }
-
- if(contextSubmenu) {
- [contextSubmenu addItem:menuItem];
- } else {
- if(!contextMenu) {
- contextMenu = [[NSMenu alloc]
- initWithTitle:[NSString stringWithUTF8String:label]];
- }
-
- [contextMenu addItem:menuItem];
- }
-}
-
-void CreateContextSubmenu(void) {
- if(contextSubmenu) oops();
-
- contextSubmenu = [[NSMenu alloc] initWithTitle:@""];
-}
-
-int ShowContextMenu(void) {
- if(!contextMenu)
- return -1;
-
- [NSMenu popUpContextMenu:contextMenu
- withEvent:[GWView lastContextMenuEvent] forView:GWView];
-
- contextMenu = nil;
-
- return contextMenuChoice;
-}
-};
-
-/* Main menu */
-
-@interface MainMenuResponder : NSObject
-+ (void)handleStatic:(id)sender;
-+ (void)handleRecent:(id)sender;
-@end
-
-@implementation MainMenuResponder
-+ (void)handleStatic:(id)sender {
- SolveSpace::GraphicsWindow::MenuEntry *entry =
- (SolveSpace::GraphicsWindow::MenuEntry*)[sender tag];
-
- if(entry->fn && ![(NSMenuItem*)sender hasSubmenu])
- entry->fn(entry->id);
-}
-
-+ (void)handleRecent:(id)sender {
- int id_ = [sender tag];
- if(id_ >= RECENT_OPEN && id_ < (RECENT_OPEN + MAX_RECENT))
- SolveSpace::SolveSpaceUI::MenuFile(id_);
- else if(id_ >= RECENT_LINK && id_ < (RECENT_LINK + MAX_RECENT))
- SolveSpace::Group::MenuGroup(id_);
-}
-@end
-
-namespace SolveSpace {
-std::map<int, NSMenuItem*> mainMenuItems;
-
-void InitMainMenu(NSMenu *mainMenu) {
- NSMenuItem *menuItem = NULL;
- NSMenu *levels[5] = {mainMenu, 0};
- NSString *label;
-
- const GraphicsWindow::MenuEntry *entry = &GraphicsWindow::menu[0];
- int current_level = 0;
- while(entry->level >= 0) {
- if(entry->level > current_level) {
- NSMenu *menu = [[NSMenu alloc] initWithTitle:label];
- [menu setAutoenablesItems:NO];
- [menuItem setSubmenu:menu];
-
- if(entry->level >= sizeof(levels) / sizeof(levels[0]))
- oops();
-
- levels[entry->level] = menu;
- }
-
- current_level = entry->level;
-
- if(entry->label) {
- /* OS X does not support mnemonics */
- label = [[NSString stringWithUTF8String:entry->label]
- stringByReplacingOccurrencesOfString:@"&" withString:@""];
-
- unichar accelChar = entry->accel &
- ~(GraphicsWindow::SHIFT_MASK | GraphicsWindow::CTRL_MASK);
- if(accelChar > GraphicsWindow::FUNCTION_KEY_BASE &&
- accelChar <= GraphicsWindow::FUNCTION_KEY_BASE + 12) {
- accelChar = NSF1FunctionKey + (accelChar - GraphicsWindow::FUNCTION_KEY_BASE - 1);
- } else if(accelChar == GraphicsWindow::DELETE_KEY) {
- accelChar = NSBackspaceCharacter;
- }
- NSString *accel = [NSString stringWithCharacters:&accelChar length:1];
-
- menuItem = [levels[entry->level] addItemWithTitle:label
- action:NULL keyEquivalent:[accel lowercaseString]];
-
- NSUInteger modifierMask = 0;
- if(entry->accel & GraphicsWindow::SHIFT_MASK)
- modifierMask |= NSShiftKeyMask;
- else if(entry->accel & GraphicsWindow::CTRL_MASK)
- modifierMask |= NSCommandKeyMask;
- [menuItem setKeyEquivalentModifierMask:modifierMask];
-
- [menuItem setTag:(NSInteger)entry];
- [menuItem setTarget:[MainMenuResponder class]];
- [menuItem setAction:@selector(handleStatic:)];
- } else {
- [levels[entry->level] addItem:[NSMenuItem separatorItem]];
- }
-
- mainMenuItems[entry->id] = menuItem;
-
- ++entry;
- }
-}
-
-void EnableMenuById(int id_, bool enabled) {
- [mainMenuItems[id_] setEnabled:enabled];
-}
-
-void CheckMenuById(int id_, bool checked) {
- [mainMenuItems[id_] setState:(checked ? NSOnState : NSOffState)];
-}
-
-void RadioMenuById(int id_, bool selected) {
- CheckMenuById(id_, selected);
-}
-
-static void RefreshRecentMenu(int id_, int base) {
- NSMenuItem *recent = mainMenuItems[id_];
- NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
- [recent setSubmenu:menu];
-
- if(std::string(RecentFile[0]).empty()) {
- NSMenuItem *placeholder = [[NSMenuItem alloc]
- initWithTitle:@"(no recent files)" action:nil keyEquivalent:@""];
- [placeholder setEnabled:NO];
- [menu addItem:placeholder];
- } else {
- for(int i = 0; i < MAX_RECENT; i++) {
- if(std::string(RecentFile[i]).empty())
- break;
-
- NSMenuItem *item = [[NSMenuItem alloc]
- initWithTitle:[[NSString stringWithUTF8String:RecentFile[i].c_str()]
- stringByAbbreviatingWithTildeInPath]
- action:nil keyEquivalent:@""];
- [item setTag:(base + i)];
- [item setAction:@selector(handleRecent:)];
- [item setTarget:[MainMenuResponder class]];
- [menu addItem:item];
- }
- }
-}
-
-void RefreshRecentMenus(void) {
- RefreshRecentMenu(GraphicsWindow::MNU_OPEN_RECENT, RECENT_OPEN);
- RefreshRecentMenu(GraphicsWindow::MNU_GROUP_RECENT, RECENT_LINK);
-}
-
-void ToggleMenuBar(void) {
- [NSMenu setMenuBarVisible:![NSMenu menuBarVisible]];
-}
-
-bool MenuBarIsVisible(void) {
- return [NSMenu menuBarVisible];
-}
-}
-
-/* Save/load */
-
-bool SolveSpace::GetOpenFile(std::string *file, const std::string &defExtension,
- const FileFilter ssFilters[]) {
- NSOpenPanel *panel = [NSOpenPanel openPanel];
- NSMutableArray *filters = [[NSMutableArray alloc] init];
- for(const FileFilter *ssFilter = ssFilters; ssFilter->name; ssFilter++) {
- for(const char *const *ssPattern = ssFilter->patterns; *ssPattern; ssPattern++) {
- [filters addObject:[NSString stringWithUTF8String:*ssPattern]];
- }
- }
- [filters removeObjectIdenticalTo:@"*"];
- [panel setAllowedFileTypes:filters];
-
- if([panel runModal] == NSFileHandlingPanelOKButton) {
- *file = [[NSFileManager defaultManager]
- fileSystemRepresentationWithPath:[[panel URL] path]];
- return true;
- } else {
- return false;
- }
-}
-
-@interface SaveFormatController : NSViewController
-@property NSSavePanel *panel;
-@property NSArray *extensions;
-@property (nonatomic) IBOutlet NSPopUpButton *button;
-@property (nonatomic) NSInteger index;
-@end
-
-@implementation SaveFormatController
-@synthesize panel, extensions, button, index;
-- (void)setIndex:(NSInteger)newIndex {
- self->index = newIndex;
- NSString *extension = [extensions objectAtIndex:newIndex];
- if(![extension isEqual:@"*"]) {
- NSString *filename = [panel nameFieldStringValue];
- NSString *basename = [[filename componentsSeparatedByString:@"."] objectAtIndex:0];
- [panel setNameFieldStringValue:[basename stringByAppendingPathExtension:extension]];
- }
-}
-@end
-
-bool SolveSpace::GetSaveFile(std::string *file, const std::string &defExtension,
- const FileFilter ssFilters[]) {
- NSSavePanel *panel = [NSSavePanel savePanel];
-
- SaveFormatController *controller =
- [[SaveFormatController alloc] initWithNibName:@"SaveFormatAccessory" bundle:nil];
- [controller setPanel:panel];
- [panel setAccessoryView:[controller view]];
-
- NSMutableArray *extensions = [[NSMutableArray alloc] init];
- [controller setExtensions:extensions];
-
- NSPopUpButton *button = [controller button];
- [button removeAllItems];
- for(const FileFilter *ssFilter = ssFilters; ssFilter->name; ssFilter++) {
- std::string desc;
- for(const char *const *ssPattern = ssFilter->patterns; *ssPattern; ssPattern++) {
- if(desc == "") {
- desc = *ssPattern;
- } else {
- desc += ", ";
- desc += *ssPattern;
- }
- }
- std::string title = std::string(ssFilter->name) + " (" + desc + ")";
- [button addItemWithTitle:[NSString stringWithUTF8String:title.c_str()]];
- [extensions addObject:[NSString stringWithUTF8String:ssFilter->patterns[0]]];
- }
-
- int extensionIndex = 0;
- if(defExtension != "") {
- extensionIndex = [extensions indexOfObject:
- [NSString stringWithUTF8String:defExtension.c_str()]];
- if(extensionIndex == -1) {
- extensionIndex = 0;
- }
- }
-
- [button selectItemAtIndex:extensionIndex];
- [panel setNameFieldStringValue:[@"untitled"
- stringByAppendingPathExtension:[extensions objectAtIndex:extensionIndex]]];
-
- if([panel runModal] == NSFileHandlingPanelOKButton) {
- *file = [[NSFileManager defaultManager]
- fileSystemRepresentationWithPath:[[panel URL] path]];
- return true;
- } else {
- return false;
- }
-}
-
-SolveSpace::DialogChoice SolveSpace::SaveFileYesNoCancel(void) {
- NSAlert *alert = [[NSAlert alloc] init];
- if(!std::string(SolveSpace::SS.saveFile).empty()) {
- [alert setMessageText:
- [[@"Do you want to save the changes you made to the sketch “"
- stringByAppendingString:
- [[NSString stringWithUTF8String:SolveSpace::SS.saveFile.c_str()]
- stringByAbbreviatingWithTildeInPath]]
- stringByAppendingString:@"”?"]];
- } else {
- [alert setMessageText:@"Do you want to save the changes you made to the new sketch?"];
- }
- [alert setInformativeText:@"Your changes will be lost if you don't save them."];
- [alert addButtonWithTitle:@"Save"];
- [alert addButtonWithTitle:@"Cancel"];
- [alert addButtonWithTitle:@"Don't Save"];
- switch([alert runModal]) {
- case NSAlertFirstButtonReturn:
- return DIALOG_YES;
- case NSAlertSecondButtonReturn:
- default:
- return DIALOG_CANCEL;
- case NSAlertThirdButtonReturn:
- return DIALOG_NO;
- }
-}
-
-SolveSpace::DialogChoice SolveSpace::LoadAutosaveYesNo(void) {
- NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:
- @"An autosave file is availible for this project."];
- [alert setInformativeText:
- @"Do you want to load the autosave file instead?"];
- [alert addButtonWithTitle:@"Load"];
- [alert addButtonWithTitle:@"Don't Load"];
- switch([alert runModal]) {
- case NSAlertFirstButtonReturn:
- return DIALOG_YES;
- case NSAlertSecondButtonReturn:
- default:
- return DIALOG_NO;
- }
-}
-
-SolveSpace::DialogChoice SolveSpace::LocateImportedFileYesNoCancel(
- const std::string &filename, bool canCancel) {
- NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:[NSString stringWithUTF8String:
- ("The linked file " + filename + " is not present.").c_str()]];
- [alert setInformativeText:
- @"Do you want to locate it manually?\n"
- "If you select \"No\", any geometry that depends on "
- "the missing file will be removed."];
- [alert addButtonWithTitle:@"Yes"];
- if(canCancel)
- [alert addButtonWithTitle:@"Cancel"];
- [alert addButtonWithTitle:@"No"];
- switch([alert runModal]) {
- case NSAlertFirstButtonReturn:
- return DIALOG_YES;
- case NSAlertSecondButtonReturn:
- default:
- if(canCancel)
- return DIALOG_CANCEL;
- /* fallthrough */
- case NSAlertThirdButtonReturn:
- return DIALOG_NO;
- }
-}
-
-/* Text window */
-
-@interface TextWindowView : GLViewWithEditor
-{
- NSTrackingArea *trackingArea;
-}
-
-@property (nonatomic, getter=isCursorHand) BOOL cursorHand;
-@end
-
-@implementation TextWindowView
-- (BOOL)isFlipped {
- return YES;
-}
-
-- (void)drawGL {
- SolveSpace::SS.TW.Paint();
-}
-
-- (BOOL)acceptsFirstMouse:(NSEvent*)event {
- return YES;
-}
-
-- (void) createTrackingArea {
- trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
- options:(NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved |
- NSTrackingActiveAlways)
- owner:self userInfo:nil];
- [self addTrackingArea:trackingArea];
-}
-
-- (void) updateTrackingAreas
-{
- [self removeTrackingArea:trackingArea];
- [self createTrackingArea];
- [super updateTrackingAreas];
-}
-
-- (void)mouseMoved:(NSEvent*)event {
- NSPoint point = [self convertPointToBacking:
- [self convertPoint:[event locationInWindow] fromView:nil]];
- SolveSpace::SS.TW.MouseEvent(/*leftClick*/ false, /*leftDown*/ false,
- point.x, -point.y);
-}
-
-- (void)mouseDown:(NSEvent*)event {
- NSPoint point = [self convertPointToBacking:
- [self convertPoint:[event locationInWindow] fromView:nil]];
- SolveSpace::SS.TW.MouseEvent(/*leftClick*/ true, /*leftDown*/ true,
- point.x, -point.y);
-}
-
-- (void)mouseDragged:(NSEvent*)event {
- NSPoint point = [self convertPointToBacking:
- [self convertPoint:[event locationInWindow] fromView:nil]];
- SolveSpace::SS.TW.MouseEvent(/*leftClick*/ false, /*leftDown*/ true,
- point.x, -point.y);
-}
-
-- (void)setCursorHand:(BOOL)cursorHand {
- if(_cursorHand != cursorHand) {
- if(cursorHand)
- [[NSCursor pointingHandCursor] push];
- else
- [NSCursor pop];
- }
- _cursorHand = cursorHand;
-}
-
-- (void)mouseExited:(NSEvent*)event {
- [self setCursorHand:FALSE];
- SolveSpace::SS.TW.MouseLeave();
-}
-
-- (void)startEditing:(NSString*)text at:(NSPoint)point {
- point = [self convertPointFromBacking:point];
- point.y = -point.y + 2;
- [[self window] makeKeyWindow];
- [super startEditing:text at:point withHeight:15.0 usingMonospace:TRUE];
- [editor setFrameSize:(NSSize){
- .width = [self bounds].size.width - [editor frame].origin.x,
- .height = [editor intrinsicContentSize].height }];
-}
-
-- (void)stopEditing {
- [super stopEditing];
- [GW makeKeyWindow];
-}
-
-- (void)didEdit:(NSString*)text {
- SolveSpace::SS.TW.EditControlDone([text UTF8String]);
-}
-
-- (void)cancelOperation:(id)sender {
- [self stopEditing];
-}
-@end
-
-@interface TextWindowDelegate : NSObject<NSWindowDelegate>
-- (BOOL)windowShouldClose:(id)sender;
-- (void)windowDidResize:(NSNotification *)notification;
-@end
-
-@implementation TextWindowDelegate
-- (BOOL)windowShouldClose:(id)sender {
- SolveSpace::GraphicsWindow::MenuView(SolveSpace::GraphicsWindow::MNU_SHOW_TEXT_WND);
- return NO;
-}
-
-- (void)windowDidResize:(NSNotification *)notification {
- NSClipView *view = [[[notification object] contentView] contentView];
- NSView *document = [view documentView];
- NSSize size = [document frame].size;
- size.width = [view frame].size.width;
- [document setFrameSize:size];
-}
-@end
-
-static NSPanel *TW;
-static TextWindowView *TWView;
-static TextWindowDelegate *TWDelegate;
-
-namespace SolveSpace {
-void InitTextWindow() {
- TW = [[NSPanel alloc] init];
- TWDelegate = [[TextWindowDelegate alloc] init];
- [TW setStyleMask:(NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask |
- NSUtilityWindowMask)];
- [[TW standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
- [[TW standardWindowButton:NSWindowZoomButton] setHidden:YES];
- [TW setTitle:@"Property Browser"];
- [TW setFrameAutosaveName:@"TextWindow"];
- [TW setFloatingPanel:YES];
- [TW setBecomesKeyOnlyIfNeeded:YES];
-
- NSScrollView *scrollView = [[NSScrollView alloc] init];
- [TW setContentView:scrollView];
- [scrollView setBackgroundColor:[NSColor blackColor]];
- [scrollView setHasVerticalScroller:YES];
- [scrollView setScrollerKnobStyle:NSScrollerKnobStyleLight];
- [[scrollView contentView] setCopiesOnScroll:YES];
-
- TWView = [[TextWindowView alloc] init];
- [scrollView setDocumentView:TWView];
-
- [TW setDelegate:TWDelegate];
- if(![TW setFrameUsingName:[TW frameAutosaveName]])
- [TW setContentSize:(NSSize){ .width = 420, .height = 300 }];
- [TWView setFrame:[[scrollView contentView] frame]];
-}
-
-void ShowTextWindow(bool visible) {
- if(visible)
- [TW orderFront:nil];
- else
- [TW close];
-}
-
-void GetTextWindowSize(int *w, int *h) {
- NSSize size = [TWView convertSizeToBacking:[TWView frame].size];
- *w = size.width;
- *h = size.height;
-}
-
-void InvalidateText(void) {
- NSSize size = [TWView convertSizeToBacking:[TWView frame].size];
- size.height = (SS.TW.top[SS.TW.rows - 1] + 1) * TextWindow::LINE_HEIGHT / 2;
- [TWView setFrameSize:[TWView convertSizeFromBacking:size]];
- [TWView setNeedsDisplay:YES];
-}
-
-void MoveTextScrollbarTo(int pos, int maxPos, int page) {
- /* unused; we draw the entire text window and scroll in Cocoa */
-}
-
-void SetMousePointerToHand(bool is_hand) {
- [TWView setCursorHand:is_hand];
-}
-
-void ShowTextEditControl(int x, int y, const std::string &str) {
- return [TWView startEditing:[NSString stringWithUTF8String:str.c_str()]
- at:(NSPoint){(CGFloat)x, (CGFloat)y}];
-}
-
-void HideTextEditControl(void) {
- return [TWView stopEditing];
-}
-
-bool TextEditControlIsVisible(void) {
- return [TWView isEditing];
-}
-};
-
-/* Miscellanea */
-
-void SolveSpace::DoMessageBox(const char *str, int rows, int cols, bool error) {
- NSAlert *alert = [[NSAlert alloc] init];
- [alert setAlertStyle:(error ? NSWarningAlertStyle : NSInformationalAlertStyle)];
- [alert addButtonWithTitle:@"OK"];
-
- /* do some additional formatting of the message these are
- heuristics, but they are made failsafe and lead to nice results. */
- NSString *input = [NSString stringWithUTF8String:str];
- NSRange dot = [input rangeOfCharacterFromSet:
- [NSCharacterSet characterSetWithCharactersInString:@".:"]];
- if(dot.location != NSNotFound) {
- [alert setMessageText:[[input substringToIndex:dot.location + 1]
- stringByReplacingOccurrencesOfString:@"\n" withString:@" "]];
- [alert setInformativeText:
- [[input substringFromIndex:dot.location + 1]
- stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]];
- } else {
- [alert setMessageText:[input
- stringByReplacingOccurrencesOfString:@"\n" withString:@" "]];
- }
-
- [alert runModal];
-}
-
-void SolveSpace::OpenWebsite(const char *url) {
- [[NSWorkspace sharedWorkspace] openURL:
- [NSURL URLWithString:[NSString stringWithUTF8String:url]]];
-}
-
-std::vector<std::string> SolveSpace::GetFontFiles() {
- std::vector<std::string> fonts;
-
- NSArray *fontNames = [[NSFontManager sharedFontManager] availableFonts];
- for(NSString *fontName in fontNames) {
- CTFontDescriptorRef fontRef =
- CTFontDescriptorCreateWithNameAndSize ((__bridge CFStringRef)fontName, 10.0);
- CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(fontRef, kCTFontURLAttribute);
- NSString *fontPath = [NSString stringWithString:[(NSURL *)CFBridgingRelease(url) path]];
- fonts.push_back([[NSFileManager defaultManager]
- fileSystemRepresentationWithPath:fontPath]);
- }
-
- return fonts;
-}
-
-/* Application lifecycle */
-
-@interface ApplicationDelegate : NSObject<NSApplicationDelegate>
-- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication;
-- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
-- (void)applicationWillTerminate:(NSNotification *)aNotification;
-- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
-- (IBAction)preferences:(id)sender;
-@end
-
-@implementation ApplicationDelegate
-- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication {
- return YES;
-}
-
-- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
- if(SolveSpace::SS.OkayToStartNewFile())
- return NSTerminateNow;
- else
- return NSTerminateCancel;
-}
-
-- (void)applicationWillTerminate:(NSNotification *)aNotification {
- SolveSpace::SS.Exit();
-}
-
-- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
- return SolveSpace::SS.OpenFile([filename UTF8String]);
-}
-
-- (IBAction)preferences:(id)sender {
- SolveSpace::SS.TW.GoToScreen(SolveSpace::TextWindow::SCREEN_CONFIGURATION);
- SolveSpace::SS.ScheduleShowTW();
-}
-@end
-
-void SolveSpace::ExitNow(void) {
- [NSApp stop:nil];
-}
-
-/*
- * Normally we would just link to the 3DconnexionClient framework.
- * We don't want to (are not allowed to) distribute the official
- * framework, so we're trying to use the one installed on the users
- * computer. There are some different versions of the framework,
- * the official one and re-implementations using an open source driver
- * for older devices (spacenav-plus). So weak-linking isn't an option,
- * either. The only remaining way is using CFBundle to dynamically
- * load the library at runtime, and also detect its availability.
- *
- * We're also defining everything needed from the 3DconnexionClientAPI,
- * so we're not depending on the API headers.
- */
-
-#pragma pack(push,2)
-
-enum {
- kConnexionClientModeTakeOver = 1,
- kConnexionClientModePlugin = 2
-};
-
-#define kConnexionMsgDeviceState '3dSR'
-#define kConnexionMaskButtons 0x00FF
-#define kConnexionMaskAxis 0x3F00
-
-typedef struct {
- uint16_t version;
- uint16_t client;
- uint16_t command;
- int16_t param;
- int32_t value;
- UInt64 time;
- uint8_t report[8];
- uint16_t buttons8;
- int16_t axis[6];
- uint16_t address;
- uint32_t buttons;
-} ConnexionDeviceState, *ConnexionDeviceStatePtr;
-
-#pragma pack(pop)
-
-typedef void (*ConnexionAddedHandlerProc)(io_connect_t);
-typedef void (*ConnexionRemovedHandlerProc)(io_connect_t);
-typedef void (*ConnexionMessageHandlerProc)(io_connect_t, natural_t, void *);
-
-typedef OSErr (*InstallConnexionHandlersProc)(ConnexionMessageHandlerProc, ConnexionAddedHandlerProc, ConnexionRemovedHandlerProc);
-typedef void (*CleanupConnexionHandlersProc)(void);
-typedef UInt16 (*RegisterConnexionClientProc)(UInt32, UInt8 *, UInt16, UInt32);
-typedef void (*UnregisterConnexionClientProc)(UInt16);
-
-static BOOL connexionShiftIsDown = NO;
-static UInt16 connexionClient = 0;
-static UInt32 connexionSignature = 'SoSp';
-static UInt8 *connexionName = (UInt8 *)"SolveSpace";
-static CFBundleRef spaceBundle = NULL;
-static InstallConnexionHandlersProc installConnexionHandlers = NULL;
-static CleanupConnexionHandlersProc cleanupConnexionHandlers = NULL;
-static RegisterConnexionClientProc registerConnexionClient = NULL;
-static UnregisterConnexionClientProc unregisterConnexionClient = NULL;
-
-static void connexionAdded(io_connect_t con) {}
-static void connexionRemoved(io_connect_t con) {}
-static void connexionMessage(io_connect_t con, natural_t type, void *arg) {
- if (type != kConnexionMsgDeviceState) {
- return;
- }
-
- ConnexionDeviceState *device = (ConnexionDeviceState *)arg;
-
- dispatch_async(dispatch_get_main_queue(), ^(void){
- SolveSpace::SS.GW.SpaceNavigatorMoved(
- (double)device->axis[0] * -0.25,
- (double)device->axis[1] * -0.25,
- (double)device->axis[2] * 0.25,
- (double)device->axis[3] * -0.0005,
- (double)device->axis[4] * -0.0005,
- (double)device->axis[5] * -0.0005,
- (connexionShiftIsDown == YES) ? 1 : 0
- );
- });
-}
-
-static void connexionInit() {
- NSString *bundlePath = @"/Library/Frameworks/3DconnexionClient.framework";
- NSURL *bundleURL = [NSURL fileURLWithPath:bundlePath];
- spaceBundle = CFBundleCreate(kCFAllocatorDefault, (__bridge CFURLRef)bundleURL);
-
- // Don't continue if no Spacemouse driver is installed on this machine
- if (spaceBundle == NULL) {
- return;
- }
-
- installConnexionHandlers = (InstallConnexionHandlersProc)
- CFBundleGetFunctionPointerForName(spaceBundle,
- CFSTR("InstallConnexionHandlers"));
-
- cleanupConnexionHandlers = (CleanupConnexionHandlersProc)
- CFBundleGetFunctionPointerForName(spaceBundle,
- CFSTR("CleanupConnexionHandlers"));
-
- registerConnexionClient = (RegisterConnexionClientProc)
- CFBundleGetFunctionPointerForName(spaceBundle,
- CFSTR("RegisterConnexionClient"));
-
- unregisterConnexionClient = (UnregisterConnexionClientProc)
- CFBundleGetFunctionPointerForName(spaceBundle,
- CFSTR("UnregisterConnexionClient"));
-
- // Only continue if all required symbols have been loaded
- if ((installConnexionHandlers == NULL) || (cleanupConnexionHandlers == NULL)
- || (registerConnexionClient == NULL) || (unregisterConnexionClient == NULL)) {
- CFRelease(spaceBundle);
- spaceBundle = NULL;
- return;
- }
-
- installConnexionHandlers(&connexionMessage, &connexionAdded, &connexionRemoved);
- connexionClient = registerConnexionClient(connexionSignature, connexionName,
- kConnexionClientModeTakeOver, kConnexionMaskButtons | kConnexionMaskAxis);
-
- // Monitor modifier flags to detect Shift button state changes
- [NSEvent addLocalMonitorForEventsMatchingMask:(NSKeyDownMask | NSFlagsChangedMask)
- handler:^(NSEvent *event) {
- if (event.modifierFlags & NSShiftKeyMask) {
- connexionShiftIsDown = YES;
- }
- return event;
- }];
-
- [NSEvent addLocalMonitorForEventsMatchingMask:(NSKeyUpMask | NSFlagsChangedMask)
- handler:^(NSEvent *event) {
- if (!(event.modifierFlags & NSShiftKeyMask)) {
- connexionShiftIsDown = NO;
- }
- return event;
- }];
-}
-
-static void connexionClose() {
- if (spaceBundle == NULL) {
- return;
- }
-
- unregisterConnexionClient(connexionClient);
- cleanupConnexionHandlers();
-
- CFRelease(spaceBundle);
-}
-
-int main(int argc, const char *argv[]) {
- [NSApplication sharedApplication];
- ApplicationDelegate *delegate = [[ApplicationDelegate alloc] init];
- [NSApp setDelegate:delegate];
-
- SolveSpace::InitGraphicsWindow();
- SolveSpace::InitTextWindow();
- [[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:nil topLevelObjects:nil];
- SolveSpace::InitMainMenu([NSApp mainMenu]);
-
- connexionInit();
- SolveSpace::SS.Init();
-
- [GW makeKeyAndOrderFront:nil];
- [NSApp run];
-
- connexionClose();
- SolveSpace::SK.Clear();
- SolveSpace::SS.Clear();
-
- return 0;
-}
-#ifndef __CONFIG_H
-#define __CONFIG_H
+#ifndef SOLVESPACE_CONFIG_H
+#define SOLVESPACE_CONFIG_H
#define PACKAGE_VERSION "@solvespace_VERSION_MAJOR@.@solvespace_VERSION_MINOR@~@solvespace_GIT_HASH@"
+/* Non-OS X *nix only */
+#define UNIX_DATADIR "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_DATAROOTDIR@/solvespace"
+
/* Do we have the si library on win32, or libspnav on *nix? */
#cmakedefine HAVE_SPACEWARE
-#cmakedefine HAVE_GTK
-#cmakedefine HAVE_GTK2
-#cmakedefine HAVE_GTK3
+/* What OpenGL version do we use? */
+#define HAVE_OPENGL @OPENGL@
+
+/* If we use GTK, can we use the native file chooser? */
+#cmakedefine HAVE_GTK_FILECHOOSERNATIVE
#endif
void TextWindow::ScreenChangeLightDirection(int link, uint32_t v) {
SS.TW.ShowEditControl(8, ssprintf("%.2f, %.2f, %.2f", CO(SS.lightDir[v])));
- SS.TW.edit.meaning = EDIT_LIGHT_DIRECTION;
+ SS.TW.edit.meaning = Edit::LIGHT_DIRECTION;
SS.TW.edit.i = v;
}
void TextWindow::ScreenChangeLightIntensity(int link, uint32_t v) {
SS.TW.ShowEditControl(31, ssprintf("%.2f", SS.lightIntensity[v]));
- SS.TW.edit.meaning = EDIT_LIGHT_INTENSITY;
+ SS.TW.edit.meaning = Edit::LIGHT_INTENSITY;
SS.TW.edit.i = v;
}
+void TextWindow::ScreenChangeLightAmbient(int link, uint32_t v) {
+ SS.TW.ShowEditControl(31, ssprintf("%.2f", SS.ambientIntensity));
+ SS.TW.edit.meaning = Edit::LIGHT_AMBIENT;
+ SS.TW.edit.i = 0;
+}
+
void TextWindow::ScreenChangeColor(int link, uint32_t v) {
SS.TW.ShowEditControlWithColorPicker(13, SS.modelColor[v]);
- SS.TW.edit.meaning = EDIT_COLOR;
+ SS.TW.edit.meaning = Edit::COLOR;
SS.TW.edit.i = v;
}
void TextWindow::ScreenChangeChordTolerance(int link, uint32_t v) {
SS.TW.ShowEditControl(3, ssprintf("%lg", SS.chordTol));
- SS.TW.edit.meaning = EDIT_CHORD_TOLERANCE;
+ SS.TW.edit.meaning = Edit::CHORD_TOLERANCE;
SS.TW.edit.i = 0;
}
void TextWindow::ScreenChangeMaxSegments(int link, uint32_t v) {
SS.TW.ShowEditControl(3, ssprintf("%d", SS.maxSegments));
- SS.TW.edit.meaning = EDIT_MAX_SEGMENTS;
+ SS.TW.edit.meaning = Edit::MAX_SEGMENTS;
SS.TW.edit.i = 0;
}
void TextWindow::ScreenChangeExportChordTolerance(int link, uint32_t v) {
SS.TW.ShowEditControl(3, ssprintf("%lg", SS.exportChordTol));
- SS.TW.edit.meaning = EDIT_CHORD_TOLERANCE;
+ SS.TW.edit.meaning = Edit::CHORD_TOLERANCE;
SS.TW.edit.i = 1;
}
void TextWindow::ScreenChangeExportMaxSegments(int link, uint32_t v) {
SS.TW.ShowEditControl(3, ssprintf("%d", SS.exportMaxSegments));
- SS.TW.edit.meaning = EDIT_MAX_SEGMENTS;
+ SS.TW.edit.meaning = Edit::MAX_SEGMENTS;
SS.TW.edit.i = 1;
}
void TextWindow::ScreenChangeCameraTangent(int link, uint32_t v) {
SS.TW.ShowEditControl(3, ssprintf("%.3f", 1000*SS.cameraTangent));
- SS.TW.edit.meaning = EDIT_CAMERA_TANGENT;
+ SS.TW.edit.meaning = Edit::CAMERA_TANGENT;
}
void TextWindow::ScreenChangeGridSpacing(int link, uint32_t v) {
SS.TW.ShowEditControl(3, SS.MmToString(SS.gridSpacing));
- SS.TW.edit.meaning = EDIT_GRID_SPACING;
+ SS.TW.edit.meaning = Edit::GRID_SPACING;
}
void TextWindow::ScreenChangeDigitsAfterDecimal(int link, uint32_t v) {
- SS.TW.ShowEditControl(3, ssprintf("%d", SS.UnitDigitsAfterDecimal()));
- SS.TW.edit.meaning = EDIT_DIGITS_AFTER_DECIMAL;
+ SS.TW.ShowEditControl(14, ssprintf("%d", SS.UnitDigitsAfterDecimal()));
+ SS.TW.edit.meaning = Edit::DIGITS_AFTER_DECIMAL;
+}
+
+void TextWindow::ScreenChangeDigitsAfterDecimalDegree(int link, uint32_t v) {
+ SS.TW.ShowEditControl(14, ssprintf("%d", SS.afterDecimalDegree));
+ SS.TW.edit.meaning = Edit::DIGITS_AFTER_DECIMAL_DEGREE;
+}
+
+void TextWindow::ScreenChangeUseSIPrefixes(int link, uint32_t v) {
+ SS.useSIPrefixes = !SS.useSIPrefixes;
+ SS.GW.Invalidate();
}
void TextWindow::ScreenChangeExportScale(int link, uint32_t v) {
SS.TW.ShowEditControl(5, ssprintf("%.3f", (double)SS.exportScale));
- SS.TW.edit.meaning = EDIT_EXPORT_SCALE;
+ SS.TW.edit.meaning = Edit::EXPORT_SCALE;
}
void TextWindow::ScreenChangeExportOffset(int link, uint32_t v) {
SS.TW.ShowEditControl(3, SS.MmToString(SS.exportOffset));
- SS.TW.edit.meaning = EDIT_EXPORT_OFFSET;
+ SS.TW.edit.meaning = Edit::EXPORT_OFFSET;
}
void TextWindow::ScreenChangeFixExportColors(int link, uint32_t v) {
SS.fixExportColors = !SS.fixExportColors;
}
+void TextWindow::ScreenChangeExportBackgroundColor(int link, uint32_t v) {
+ SS.exportBackgroundColor = !SS.exportBackgroundColor;
+}
+
void TextWindow::ScreenChangeBackFaces(int link, uint32_t v) {
SS.drawBackFaces = !SS.drawBackFaces;
- InvalidateGraphics();
+ SS.GW.Invalidate(/*clearPersistent=*/true);
+}
+
+void TextWindow::ScreenChangeTurntableNav(int link, uint32_t v) {
+ SS.turntableNav = !SS.turntableNav;
+ if(SS.turntableNav) {
+ // If turntable nav is being turned on, align view so Z is vertical
+ SS.GW.AnimateOnto(Quaternion::From(Vector::From(-1, 0, 0), Vector::From(0, 0, 1)),
+ SS.GW.offset);
+ }
+}
+
+void TextWindow::ScreenChangeImmediatelyEditDimension(int link, uint32_t v) {
+ SS.immediatelyEditDimension = !SS.immediatelyEditDimension;
+ SS.GW.Invalidate(/*clearPersistent=*/true);
+}
+
+void TextWindow::ScreenChangeShowContourAreas(int link, uint32_t v) {
+ SS.showContourAreas = !SS.showContourAreas;
+ SS.GW.Invalidate();
}
void TextWindow::ScreenChangeCheckClosedContour(int link, uint32_t v) {
SS.checkClosedContour = !SS.checkClosedContour;
- InvalidateGraphics();
+ SS.GW.Invalidate();
+}
+
+void TextWindow::ScreenChangeAutomaticLineConstraints(int link, uint32_t v) {
+ SS.automaticLineConstraints = !SS.automaticLineConstraints;
+ SS.GW.Invalidate();
}
void TextWindow::ScreenChangeShadedTriangles(int link, uint32_t v) {
SS.exportShadedTriangles = !SS.exportShadedTriangles;
- InvalidateGraphics();
+ SS.GW.Invalidate();
}
void TextWindow::ScreenChangePwlCurves(int link, uint32_t v) {
SS.exportPwlCurves = !SS.exportPwlCurves;
- InvalidateGraphics();
+ SS.GW.Invalidate();
}
void TextWindow::ScreenChangeCanvasSizeAuto(int link, uint32_t v) {
} else {
SS.exportCanvasSizeAuto = false;
}
- InvalidateGraphics();
+ SS.GW.Invalidate();
}
void TextWindow::ScreenChangeCanvasSize(int link, uint32_t v) {
int col = 13;
if(v < 10) col = 11;
SS.TW.ShowEditControl(col, SS.MmToString(d));
- SS.TW.edit.meaning = EDIT_CANVAS_SIZE;
+ SS.TW.edit.meaning = Edit::CANVAS_SIZE;
SS.TW.edit.i = v;
}
std::string buf;
switch(link) {
case 'd':
- SS.TW.edit.meaning = EDIT_G_CODE_DEPTH;
+ SS.TW.edit.meaning = Edit::G_CODE_DEPTH;
buf += SS.MmToString(SS.gCode.depth);
break;
case 's':
- SS.TW.edit.meaning = EDIT_G_CODE_PASSES;
+ SS.TW.edit.meaning = Edit::G_CODE_PASSES;
buf += std::to_string(SS.gCode.passes);
break;
case 'F':
- SS.TW.edit.meaning = EDIT_G_CODE_FEED;
+ SS.TW.edit.meaning = Edit::G_CODE_FEED;
buf += SS.MmToString(SS.gCode.feed);
break;
case 'P':
- SS.TW.edit.meaning = EDIT_G_CODE_PLUNGE_FEED;
+ SS.TW.edit.meaning = Edit::G_CODE_PLUNGE_FEED;
buf += SS.MmToString(SS.gCode.plungeFeed);
break;
}
void TextWindow::ScreenChangeAutosaveInterval(int link, uint32_t v) {
SS.TW.ShowEditControl(3, std::to_string(SS.autosaveInterval));
- SS.TW.edit.meaning = EDIT_AUTOSAVE_INTERVAL;
+ SS.TW.edit.meaning = Edit::AUTOSAVE_INTERVAL;
+}
+
+void TextWindow::ScreenChangeFindConstraintTimeout(int link, uint32_t v) {
+ SS.TW.ShowEditControl(3, std::to_string(SS.timeoutRedundantConstr));
+ SS.TW.edit.meaning = Edit::FIND_CONSTRAINT_TIMEOUT;
}
-void TextWindow::ShowConfiguration(void) {
+void TextWindow::ShowConfiguration() {
int i;
Printf(true, "%Ft user color (r, g, b)");
CO(SS.lightDir[i]), i, &ScreenChangeLightDirection,
SS.lightIntensity[i], i, &ScreenChangeLightIntensity);
}
+ Printf(false, "%Bp ambient lighting "
+ "%2 %Fl%D%f%Ll[c]%E",
+ (i & 1) ? 'd' : 'a', i,
+ SS.ambientIntensity, &ScreenChangeLightAmbient);
Printf(false, "");
Printf(false, "%Ft chord tolerance (in percents)%E");
Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E",
SS.MmToString(SS.gridSpacing).c_str(),
&ScreenChangeGridSpacing, 0);
+
+ Printf(false, "");
Printf(false, "%Ft digits after decimal point to show%E");
- Printf(false, "%Ba %d %Fl%Ll%f%D[change]%E (e.g. '%s')",
+ Printf(false, "%Ba%Ft distances: %Fd%d %Fl%Ll%f%D[change]%E (e.g. '%s')",
SS.UnitDigitsAfterDecimal(),
&ScreenChangeDigitsAfterDecimal, 0,
SS.MmToString(SS.StringToMm("1.23456789")).c_str());
+ Printf(false, "%Bd%Ft angles: %Fd%d %Fl%Ll%f%D[change]%E (e.g. '%s')",
+ SS.afterDecimalDegree,
+ &ScreenChangeDigitsAfterDecimalDegree, 0,
+ SS.DegreeToString(1.23456789).c_str());
+ Printf(false, " %Fd%f%Ll%s use SI prefixes for distances%E",
+ &ScreenChangeUseSIPrefixes,
+ SS.useSIPrefixes ? CHECK_TRUE : CHECK_FALSE);
Printf(false, "");
Printf(false, "%Ft export scale factor (1:1=mm, 1:25.4=inch)");
Printf(false, " %Fd%f%Ll%s fix white exported lines%E",
&ScreenChangeFixExportColors,
SS.fixExportColors ? CHECK_TRUE : CHECK_FALSE);
+ Printf(false, " %Fd%f%Ll%s export background color%E",
+ &ScreenChangeExportBackgroundColor,
+ SS.exportBackgroundColor ? CHECK_TRUE : CHECK_FALSE);
Printf(false, "");
Printf(false, "%Ft export canvas size: "
Printf(false, " %Fd%f%Ll%s check sketch for closed contour%E",
&ScreenChangeCheckClosedContour,
SS.checkClosedContour ? CHECK_TRUE : CHECK_FALSE);
-
+ Printf(false, " %Fd%f%Ll%s show areas of closed contours%E",
+ &ScreenChangeShowContourAreas,
+ SS.showContourAreas ? CHECK_TRUE : CHECK_FALSE);
+ Printf(false, " %Fd%f%Ll%s enable automatic line constraints%E",
+ &ScreenChangeAutomaticLineConstraints,
+ SS.automaticLineConstraints ? CHECK_TRUE : CHECK_FALSE);
+ Printf(false, " %Fd%f%Ll%s use turntable mouse navigation%E", &ScreenChangeTurntableNav,
+ SS.turntableNav ? CHECK_TRUE : CHECK_FALSE);
+ Printf(false, " %Fd%f%Ll%s edit newly added dimensions%E",
+ &ScreenChangeImmediatelyEditDimension,
+ SS.immediatelyEditDimension ? CHECK_TRUE : CHECK_FALSE);
Printf(false, "");
Printf(false, "%Ft autosave interval (in minutes)%E");
Printf(false, "%Ba %d %Fl%Ll%f[change]%E",
SS.autosaveInterval, &ScreenChangeAutosaveInterval);
-
Printf(false, "");
- Printf(false, " %Ftgl vendor %E%s", glGetString(GL_VENDOR));
- Printf(false, " %Ft renderer %E%s", glGetString(GL_RENDERER));
- Printf(false, " %Ft version %E%s", glGetString(GL_VERSION));
+ Printf(false, "%Ft redundant constraint timeout (in ms)%E");
+ Printf(false, "%Ba %d %Fl%Ll%f[change]%E",
+ SS.timeoutRedundantConstr, &ScreenChangeFindConstraintTimeout);
+
+ if(canvas) {
+ const char *gl_vendor, *gl_renderer, *gl_version;
+ canvas->GetIdent(&gl_vendor, &gl_renderer, &gl_version);
+ Printf(false, "");
+ Printf(false, " %Ftgl vendor %E%s", gl_vendor);
+ Printf(false, " %Ft renderer %E%s", gl_renderer);
+ Printf(false, " %Ft version %E%s", gl_version);
+ }
}
-bool TextWindow::EditControlDoneForConfiguration(const char *s) {
+bool TextWindow::EditControlDoneForConfiguration(const std::string &s) {
switch(edit.meaning) {
- case EDIT_LIGHT_INTENSITY:
- SS.lightIntensity[edit.i] = min(1.0, max(0.0, atof(s)));
- InvalidateGraphics();
+ case Edit::LIGHT_INTENSITY:
+ SS.lightIntensity[edit.i] = min(1.0, max(0.0, atof(s.c_str())));
+ SS.GW.Invalidate();
break;
-
- case EDIT_LIGHT_DIRECTION: {
+ case Edit::LIGHT_AMBIENT:
+ SS.ambientIntensity = min(1.0, max(0.0, atof(s.c_str())));
+ SS.GW.Invalidate();
+ break;
+ case Edit::LIGHT_DIRECTION: {
double x, y, z;
- if(sscanf(s, "%lf, %lf, %lf", &x, &y, &z)==3) {
+ if(sscanf(s.c_str(), "%lf, %lf, %lf", &x, &y, &z)==3) {
SS.lightDir[edit.i] = Vector::From(x, y, z);
+ SS.GW.Invalidate();
} else {
- Error("Bad format: specify coordinates as x, y, z");
+ Error(_("Bad format: specify coordinates as x, y, z"));
}
- InvalidateGraphics();
break;
}
- case EDIT_COLOR: {
+ case Edit::COLOR: {
Vector rgb;
- if(sscanf(s, "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
+ if(sscanf(s.c_str(), "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
rgb = rgb.ClampWithin(0, 1);
SS.modelColor[edit.i] = RGBf(rgb.x, rgb.y, rgb.z);
} else {
- Error("Bad format: specify color as r, g, b");
+ Error(_("Bad format: specify color as r, g, b"));
}
break;
}
- case EDIT_CHORD_TOLERANCE: {
+ case Edit::CHORD_TOLERANCE: {
if(edit.i == 0) {
- SS.chordTol = max(0.0, atof(s));
- SS.GenerateAll(SolveSpaceUI::GENERATE_ALL);
+ SS.chordTol = max(0.0, atof(s.c_str()));
+ SS.GenerateAll(SolveSpaceUI::Generate::ALL);
} else {
- SS.exportChordTol = max(0.0, atof(s));
+ SS.exportChordTol = max(0.0, atof(s.c_str()));
}
break;
}
- case EDIT_MAX_SEGMENTS: {
+ case Edit::MAX_SEGMENTS: {
if(edit.i == 0) {
- SS.maxSegments = min(1000, max(7, atoi(s)));
- SS.GenerateAll(SolveSpaceUI::GENERATE_ALL);
+ SS.maxSegments = min(1000, max(7, atoi(s.c_str())));
+ SS.GenerateAll(SolveSpaceUI::Generate::ALL);
} else {
- SS.exportMaxSegments = min(1000, max(7, atoi(s)));
+ SS.exportMaxSegments = min(1000, max(7, atoi(s.c_str())));
}
break;
}
- case EDIT_CAMERA_TANGENT: {
- SS.cameraTangent = (min(2.0, max(0.0, atof(s))))/1000.0;
+ case Edit::CAMERA_TANGENT: {
+ SS.cameraTangent = (min(2.0, max(0.0, atof(s.c_str()))))/1000.0;
+ SS.GW.Invalidate();
if(!SS.usePerspectiveProj) {
- Message("The perspective factor will have no effect until you "
- "enable View -> Use Perspective Projection.");
+ Message(_("The perspective factor will have no effect until you "
+ "enable View -> Use Perspective Projection."));
}
- InvalidateGraphics();
break;
}
- case EDIT_GRID_SPACING: {
+ case Edit::GRID_SPACING: {
SS.gridSpacing = (float)min(1e4, max(1e-3, SS.StringToMm(s)));
- InvalidateGraphics();
+ SS.GW.Invalidate();
break;
}
- case EDIT_DIGITS_AFTER_DECIMAL: {
- int v = atoi(s);
+ case Edit::DIGITS_AFTER_DECIMAL: {
+ int v = atoi(s.c_str());
if(v < 0 || v > 8) {
- Error("Specify between 0 and 8 digits after the decimal.");
+ Error(_("Specify between 0 and %d digits after the decimal."), 8);
} else {
SS.SetUnitDigitsAfterDecimal(v);
+ SS.GW.Invalidate();
}
- InvalidateGraphics();
break;
}
- case EDIT_EXPORT_SCALE: {
- Expr *e = Expr::From(s, true);
+ case Edit::DIGITS_AFTER_DECIMAL_DEGREE: {
+ int v = atoi(s.c_str());
+ if(v < 0 || v > 4) {
+ Error(_("Specify between 0 and %d digits after the decimal."), 4);
+ } else {
+ SS.afterDecimalDegree = v;
+ SS.GW.Invalidate();
+ }
+ break;
+ }
+ case Edit::EXPORT_SCALE: {
+ Expr *e = Expr::From(s, /*popUpError=*/true);
if(e) {
double ev = e->Eval();
- if(fabs(ev) < 0.001 || isnan(ev)) {
- Error("Export scale must not be zero!");
+ if(fabs(ev) < 0.001 || IsReasonable(ev)) {
+ Error(_("Export scale must not be zero!"));
} else {
SS.exportScale = (float)ev;
}
}
break;
}
- case EDIT_EXPORT_OFFSET: {
- Expr *e = Expr::From(s, true);
+ case Edit::EXPORT_OFFSET: {
+ Expr *e = Expr::From(s, /*popUpError=*/true);
if(e) {
double ev = SS.ExprToMm(e);
- if(isnan(ev) || ev < 0) {
- Error("Cutter radius offset must not be negative!");
+ if(IsReasonable(ev) || ev < 0) {
+ Error(_("Cutter radius offset must not be negative!"));
} else {
SS.exportOffset = (float)ev;
}
}
break;
}
- case EDIT_CANVAS_SIZE: {
- Expr *e = Expr::From(s, true);
+ case Edit::CANVAS_SIZE: {
+ Expr *e = Expr::From(s, /*popUpError=*/true);
if(!e) {
break;
}
}
break;
}
- case EDIT_G_CODE_DEPTH: {
- Expr *e = Expr::From(s, true);
+ case Edit::G_CODE_DEPTH: {
+ Expr *e = Expr::From(s, /*popUpError=*/true);
if(e) SS.gCode.depth = (float)SS.ExprToMm(e);
break;
}
- case EDIT_G_CODE_PASSES: {
- Expr *e = Expr::From(s, true);
+ case Edit::G_CODE_PASSES: {
+ Expr *e = Expr::From(s, /*popUpError=*/true);
if(e) SS.gCode.passes = (int)(e->Eval());
SS.gCode.passes = max(1, min(1000, SS.gCode.passes));
break;
}
- case EDIT_G_CODE_FEED: {
- Expr *e = Expr::From(s, true);
+ case Edit::G_CODE_FEED: {
+ Expr *e = Expr::From(s, /*popUpError=*/true);
if(e) SS.gCode.feed = (float)SS.ExprToMm(e);
break;
}
- case EDIT_G_CODE_PLUNGE_FEED: {
- Expr *e = Expr::From(s, true);
+ case Edit::G_CODE_PLUNGE_FEED: {
+ Expr *e = Expr::From(s, /*popUpError=*/true);
if(e) SS.gCode.plungeFeed = (float)SS.ExprToMm(e);
break;
}
- case EDIT_AUTOSAVE_INTERVAL: {
- int interval;
- if(sscanf(s, "%d", &interval)==1) {
+ case Edit::AUTOSAVE_INTERVAL: {
+ int interval = atoi(s.c_str());
+ if(interval) {
if(interval >= 1) {
SS.autosaveInterval = interval;
- SetAutosaveTimerFor(interval);
+ SS.ScheduleAutosave();
} else {
- Error("Bad value: autosave interval should be positive");
+ Error(_("Bad value: autosave interval should be positive"));
}
} else {
- Error("Bad format: specify interval in integral minutes");
+ Error(_("Bad format: specify interval in integral minutes"));
+ }
+ break;
+ }
+ case Edit::FIND_CONSTRAINT_TIMEOUT: {
+ int timeout = atoi(s.c_str());
+ if(timeout) {
+ if(timeout >= 1) {
+ SS.timeoutRedundantConstr = timeout;
+ } else {
+ SS.timeoutRedundantConstr = 1000;
+ }
}
break;
}
//-----------------------------------------------------------------------------
#include "solvespace.h"
-std::string Constraint::DescriptionString(void) {
- const char *s;
+std::string Constraint::DescriptionString() const {
+ std::string s;
switch(type) {
- case POINTS_COINCIDENT: s = "pts-coincident"; break;
- case PT_PT_DISTANCE: s = "pt-pt-distance"; break;
- case PT_LINE_DISTANCE: s = "pt-line-distance"; break;
- case PT_PLANE_DISTANCE: s = "pt-plane-distance"; break;
- case PT_FACE_DISTANCE: s = "pt-face-distance"; break;
- case PROJ_PT_DISTANCE: s = "proj-pt-pt-distance"; break;
- case PT_IN_PLANE: s = "pt-in-plane"; break;
- case PT_ON_LINE: s = "pt-on-line"; break;
- case PT_ON_FACE: s = "pt-on-face"; break;
- case EQUAL_LENGTH_LINES: s = "eq-length"; break;
- case EQ_LEN_PT_LINE_D: s = "eq-length-and-pt-ln-dist"; break;
- case EQ_PT_LN_DISTANCES: s = "eq-pt-line-distances"; break;
- case LENGTH_RATIO: s = "length-ratio"; break;
- case LENGTH_DIFFERENCE: s = "length-difference"; break;
- case SYMMETRIC: s = "symmetric"; break;
- case SYMMETRIC_HORIZ: s = "symmetric-h"; break;
- case SYMMETRIC_VERT: s = "symmetric-v"; break;
- case SYMMETRIC_LINE: s = "symmetric-line"; break;
- case AT_MIDPOINT: s = "at-midpoint"; break;
- case HORIZONTAL: s = "horizontal"; break;
- case VERTICAL: s = "vertical"; break;
- case DIAMETER: s = "diameter"; break;
- case PT_ON_CIRCLE: s = "pt-on-circle"; break;
- case SAME_ORIENTATION: s = "same-orientation"; break;
- case ANGLE: s = "angle"; break;
- case PARALLEL: s = "parallel"; break;
- case ARC_LINE_TANGENT: s = "arc-line-tangent"; break;
- case CUBIC_LINE_TANGENT: s = "cubic-line-tangent"; break;
- case CURVE_CURVE_TANGENT: s = "curve-curve-tangent"; break;
- case PERPENDICULAR: s = "perpendicular"; break;
- case EQUAL_RADIUS: s = "eq-radius"; break;
- case EQUAL_ANGLE: s = "eq-angle"; break;
- case EQUAL_LINE_ARC_LEN: s = "eq-line-len-arc-len"; break;
- case WHERE_DRAGGED: s = "lock-where-dragged"; break;
- case COMMENT: s = "comment"; break;
- default: s = "???"; break;
+ case Type::POINTS_COINCIDENT: s = C_("constr-name", "pts-coincident"); break;
+ case Type::PT_PT_DISTANCE: s = C_("constr-name", "pt-pt-distance"); break;
+ case Type::PT_LINE_DISTANCE: s = C_("constr-name", "pt-line-distance"); break;
+ case Type::PT_PLANE_DISTANCE: s = C_("constr-name", "pt-plane-distance"); break;
+ case Type::PT_FACE_DISTANCE: s = C_("constr-name", "pt-face-distance"); break;
+ case Type::PROJ_PT_DISTANCE: s = C_("constr-name", "proj-pt-pt-distance"); break;
+ case Type::PT_IN_PLANE: s = C_("constr-name", "pt-in-plane"); break;
+ case Type::PT_ON_LINE: s = C_("constr-name", "pt-on-line"); break;
+ case Type::PT_ON_FACE: s = C_("constr-name", "pt-on-face"); break;
+ case Type::EQUAL_LENGTH_LINES: s = C_("constr-name", "eq-length"); break;
+ case Type::EQ_LEN_PT_LINE_D: s = C_("constr-name", "eq-length-and-pt-ln-dist"); break;
+ case Type::EQ_PT_LN_DISTANCES: s = C_("constr-name", "eq-pt-line-distances"); break;
+ case Type::LENGTH_RATIO: s = C_("constr-name", "length-ratio"); break;
+ case Type::LENGTH_DIFFERENCE: s = C_("constr-name", "length-difference"); break;
+ case Type::SYMMETRIC: s = C_("constr-name", "symmetric"); break;
+ case Type::SYMMETRIC_HORIZ: s = C_("constr-name", "symmetric-h"); break;
+ case Type::SYMMETRIC_VERT: s = C_("constr-name", "symmetric-v"); break;
+ case Type::SYMMETRIC_LINE: s = C_("constr-name", "symmetric-line"); break;
+ case Type::AT_MIDPOINT: s = C_("constr-name", "at-midpoint"); break;
+ case Type::HORIZONTAL: s = C_("constr-name", "horizontal"); break;
+ case Type::VERTICAL: s = C_("constr-name", "vertical"); break;
+ case Type::DIAMETER: s = C_("constr-name", "diameter"); break;
+ case Type::PT_ON_CIRCLE: s = C_("constr-name", "pt-on-circle"); break;
+ case Type::SAME_ORIENTATION: s = C_("constr-name", "same-orientation"); break;
+ case Type::ANGLE: s = C_("constr-name", "angle"); break;
+ case Type::PARALLEL: s = C_("constr-name", "parallel"); break;
+ case Type::ARC_LINE_TANGENT: s = C_("constr-name", "arc-line-tangent"); break;
+ case Type::CUBIC_LINE_TANGENT: s = C_("constr-name", "cubic-line-tangent"); break;
+ case Type::CURVE_CURVE_TANGENT: s = C_("constr-name", "curve-curve-tangent"); break;
+ case Type::PERPENDICULAR: s = C_("constr-name", "perpendicular"); break;
+ case Type::EQUAL_RADIUS: s = C_("constr-name", "eq-radius"); break;
+ case Type::EQUAL_ANGLE: s = C_("constr-name", "eq-angle"); break;
+ case Type::EQUAL_LINE_ARC_LEN: s = C_("constr-name", "eq-line-len-arc-len"); break;
+ case Type::WHERE_DRAGGED: s = C_("constr-name", "lock-where-dragged"); break;
+ case Type::COMMENT: s = C_("constr-name", "comment"); break;
+ default: s = "???"; break;
}
- return ssprintf("c%03x-%s", h.v, s);
+ return ssprintf("c%03x-%s", h.v, s.c_str());
}
#ifndef LIBRARY
// Delete all constraints with the specified type, entityA, ptA. We use this
// when auto-removing constraints that would become redundant.
//-----------------------------------------------------------------------------
-void Constraint::DeleteAllConstraintsFor(int type, hEntity entityA, hEntity ptA)
+void Constraint::DeleteAllConstraintsFor(Constraint::Type type, hEntity entityA, hEntity ptA)
{
SK.constraint.ClearTags();
- for(int i = 0; i < SK.constraint.n; i++) {
- ConstraintBase *ct = &(SK.constraint.elem[i]);
+ for(auto &constraint : SK.constraint) {
+ ConstraintBase *ct = &constraint;
if(ct->type != type) continue;
- if(ct->entityA.v != entityA.v) continue;
- if(ct->ptA.v != ptA.v) continue;
+ if(ct->entityA != entityA) continue;
+ if(ct->ptA != ptA) continue;
ct->tag = 1;
}
SK.constraint.RemoveTagged();
SS.GW.hover.Clear();
}
-hConstraint Constraint::AddConstraint(Constraint *c) {
- return AddConstraint(c, true);
-}
-
hConstraint Constraint::AddConstraint(Constraint *c, bool rememberForUndo) {
if(rememberForUndo) SS.UndoRemember();
- SK.constraint.AddAndAssignId(c);
+ hConstraint hc = SK.constraint.AddAndAssignId(c);
+ SK.GetConstraint(hc)->Generate(&SK.param);
SS.MarkGroupDirty(c->group);
- SS.ScheduleGenerateAll();
+ SK.GetGroup(c->group)->dofCheckOk = false;
return c->h;
}
-hConstraint Constraint::Constrain(int type, hEntity ptA, hEntity ptB,
- hEntity entityA, hEntity entityB,
- bool other, bool other2)
+hConstraint Constraint::Constrain(Constraint::Type type, hEntity ptA, hEntity ptB,
+ hEntity entityA, hEntity entityB,
+ bool other, bool other2)
{
Constraint c = {};
c.group = SS.GW.activeGroup;
c.entityB = entityB;
c.other = other;
c.other2 = other2;
- return AddConstraint(&c, false);
+ return AddConstraint(&c, /*rememberForUndo=*/false);
}
-hConstraint Constraint::Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA){
- return Constrain(type, ptA, ptB, entityA, Entity::NO_ENTITY, false, false);
+hConstraint Constraint::TryConstrain(Constraint::Type type, hEntity ptA, hEntity ptB,
+ hEntity entityA, hEntity entityB,
+ bool other, bool other2) {
+ int rankBefore, rankAfter;
+ SolveResult howBefore = SS.TestRankForGroup(SS.GW.activeGroup, &rankBefore);
+ hConstraint hc = Constrain(type, ptA, ptB, entityA, entityB, other, other2);
+ SolveResult howAfter = SS.TestRankForGroup(SS.GW.activeGroup, &rankAfter);
+ // There are two cases where the constraint is clearly redundant:
+ // * If the group wasn't overconstrained and now it is;
+ // * If the group was overconstrained, and adding the constraint doesn't change rank at all.
+ if((howBefore == SolveResult::OKAY && howAfter == SolveResult::REDUNDANT_OKAY) ||
+ (howBefore == SolveResult::REDUNDANT_OKAY && howAfter == SolveResult::REDUNDANT_OKAY &&
+ rankBefore == rankAfter)) {
+ SK.constraint.RemoveById(hc);
+ hc = {};
+ }
+ return hc;
}
hConstraint Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) {
- return Constrain(POINTS_COINCIDENT, ptA, ptB,
- Entity::NO_ENTITY, Entity::NO_ENTITY, false, false);
+ return Constrain(Type::POINTS_COINCIDENT, ptA, ptB,
+ Entity::NO_ENTITY, Entity::NO_ENTITY, /*other=*/false, /*other2=*/false);
}
-void Constraint::MenuConstrain(int id) {
+void Constraint::MenuConstrain(Command id) {
Constraint c = {};
c.group = SS.GW.activeGroup;
c.workplane = SS.GW.ActiveWorkplane();
SS.GW.GroupSelection();
-#define gs (SS.GW.gs)
+ auto const &gs = SS.GW.gs;
switch(id) {
- case GraphicsWindow::MNU_DISTANCE_DIA:
- case GraphicsWindow::MNU_REF_DISTANCE: {
+ case Command::DISTANCE_DIA:
+ case Command::REF_DISTANCE: {
if(gs.points == 2 && gs.n == 2) {
- c.type = PT_PT_DISTANCE;
+ c.type = Type::PT_PT_DISTANCE;
c.ptA = gs.point[0];
c.ptB = gs.point[1];
} else if(gs.lineSegments == 1 && gs.n == 1) {
- c.type = PT_PT_DISTANCE;
+ c.type = Type::PT_PT_DISTANCE;
Entity *e = SK.GetEntity(gs.entity[0]);
c.ptA = e->point[0];
c.ptB = e->point[1];
} else if(gs.vectors == 1 && gs.points == 2 && gs.n == 3) {
- c.type = PROJ_PT_DISTANCE;
+ c.type = Type::PROJ_PT_DISTANCE;
c.ptA = gs.point[0];
c.ptB = gs.point[1];
c.entityA = gs.vector[0];
} else if(gs.workplanes == 1 && gs.points == 1 && gs.n == 2) {
- c.type = PT_PLANE_DISTANCE;
+ c.type = Type::PT_PLANE_DISTANCE;
c.ptA = gs.point[0];
c.entityA = gs.entity[0];
} else if(gs.lineSegments == 1 && gs.points == 1 && gs.n == 2) {
- c.type = PT_LINE_DISTANCE;
+ c.type = Type::PT_LINE_DISTANCE;
c.ptA = gs.point[0];
c.entityA = gs.entity[0];
} else if(gs.faces == 1 && gs.points == 1 && gs.n == 2) {
- c.type = PT_FACE_DISTANCE;
+ c.type = Type::PT_FACE_DISTANCE;
c.ptA = gs.point[0];
c.entityA = gs.face[0];
} else if(gs.circlesOrArcs == 1 && gs.n == 1) {
- c.type = DIAMETER;
+ c.type = Type::DIAMETER;
c.entityA = gs.entity[0];
} else {
- Error(
-"Bad selection for distance / diameter constraint. This "
-"constraint can apply to:\n\n"
-" * two points (distance between points)\n"
-" * a line segment (length)\n"
-" * two points and a line segment or normal (projected distance)\n"
-" * a workplane and a point (minimum distance)\n"
-" * a line segment and a point (minimum distance)\n"
-" * a plane face and a point (minimum distance)\n"
-" * a circle or an arc (diameter)\n");
+ Error(_("Bad selection for distance / diameter constraint. This "
+ "constraint can apply to:\n\n"
+ " * two points (distance between points)\n"
+ " * a line segment (length)\n"
+ " * two points and a line segment or normal (projected distance)\n"
+ " * a workplane and a point (minimum distance)\n"
+ " * a line segment and a point (minimum distance)\n"
+ " * a plane face and a point (minimum distance)\n"
+ " * a circle or an arc (diameter)\n"));
return;
}
- if(c.type == PT_PT_DISTANCE || c.type == PROJ_PT_DISTANCE) {
+ if(c.type == Type::PT_PT_DISTANCE || c.type == Type::PROJ_PT_DISTANCE) {
Vector n = SS.GW.projRight.Cross(SS.GW.projUp);
Vector a = SK.GetEntity(c.ptA)->PointGetNum();
Vector b = SK.GetEntity(c.ptB)->PointGetNum();
c.disp.offset = Vector::From(0, 0, 0);
}
- if(id == GraphicsWindow::MNU_REF_DISTANCE) {
+ if(id == Command::REF_DISTANCE) {
c.reference = true;
}
break;
}
- case GraphicsWindow::MNU_ON_ENTITY:
+ case Command::ON_ENTITY:
if(gs.points == 2 && gs.n == 2) {
- c.type = POINTS_COINCIDENT;
+ c.type = Type::POINTS_COINCIDENT;
c.ptA = gs.point[0];
c.ptB = gs.point[1];
} else if(gs.points == 1 && gs.workplanes == 1 && gs.n == 2) {
- c.type = PT_IN_PLANE;
+ c.type = Type::PT_IN_PLANE;
c.ptA = gs.point[0];
c.entityA = gs.entity[0];
} else if(gs.points == 1 && gs.lineSegments == 1 && gs.n == 2) {
- c.type = PT_ON_LINE;
+ c.type = Type::PT_ON_LINE;
c.ptA = gs.point[0];
c.entityA = gs.entity[0];
} else if(gs.points == 1 && gs.circlesOrArcs == 1 && gs.n == 2) {
- c.type = PT_ON_CIRCLE;
+ c.type = Type::PT_ON_CIRCLE;
c.ptA = gs.point[0];
c.entityA = gs.entity[0];
} else if(gs.points == 1 && gs.faces == 1 && gs.n == 2) {
- c.type = PT_ON_FACE;
+ c.type = Type::PT_ON_FACE;
c.ptA = gs.point[0];
c.entityA = gs.face[0];
} else {
- Error("Bad selection for on point / curve / plane constraint. "
- "This constraint can apply to:\n\n"
- " * two points (points coincident)\n"
- " * a point and a workplane (point in plane)\n"
- " * a point and a line segment (point on line)\n"
- " * a point and a circle or arc (point on curve)\n"
- " * a point and a plane face (point on face)\n");
+ Error(_("Bad selection for on point / curve / plane constraint. "
+ "This constraint can apply to:\n\n"
+ " * two points (points coincident)\n"
+ " * a point and a workplane (point in plane)\n"
+ " * a point and a line segment (point on line)\n"
+ " * a point and a circle or arc (point on curve)\n"
+ " * a point and a plane face (point on face)\n"));
return;
}
AddConstraint(&c);
break;
- case GraphicsWindow::MNU_EQUAL:
+ case Command::EQUAL:
if(gs.lineSegments == 2 && gs.n == 2) {
- c.type = EQUAL_LENGTH_LINES;
+ c.type = Type::EQUAL_LENGTH_LINES;
c.entityA = gs.entity[0];
c.entityB = gs.entity[1];
} else if(gs.lineSegments == 2 && gs.points == 2 && gs.n == 4) {
- c.type = EQ_PT_LN_DISTANCES;
+ c.type = Type::EQ_PT_LN_DISTANCES;
c.entityA = gs.entity[0];
c.ptA = gs.point[0];
c.entityB = gs.entity[1];
} else if(gs.lineSegments == 1 && gs.points == 2 && gs.n == 3) {
// The same line segment for the distances, but different
// points.
- c.type = EQ_PT_LN_DISTANCES;
+ c.type = Type::EQ_PT_LN_DISTANCES;
c.entityA = gs.entity[0];
c.ptA = gs.point[0];
c.entityB = gs.entity[0];
c.ptB = gs.point[1];
} else if(gs.lineSegments == 2 && gs.points == 1 && gs.n == 3) {
- c.type = EQ_LEN_PT_LINE_D;
+ c.type = Type::EQ_LEN_PT_LINE_D;
c.entityA = gs.entity[0];
c.entityB = gs.entity[1];
c.ptA = gs.point[0];
} else if(gs.vectors == 4 && gs.n == 4) {
- c.type = EQUAL_ANGLE;
+ c.type = Type::EQUAL_ANGLE;
c.entityA = gs.vector[0];
c.entityB = gs.vector[1];
c.entityC = gs.vector[2];
c.entityD = gs.vector[3];
} else if(gs.vectors == 3 && gs.n == 3) {
- c.type = EQUAL_ANGLE;
+ c.type = Type::EQUAL_ANGLE;
c.entityA = gs.vector[0];
c.entityB = gs.vector[1];
c.entityC = gs.vector[1];
c.entityD = gs.vector[2];
} else if(gs.circlesOrArcs == 2 && gs.n == 2) {
- c.type = EQUAL_RADIUS;
+ c.type = Type::EQUAL_RADIUS;
c.entityA = gs.entity[0];
c.entityB = gs.entity[1];
} else if(gs.arcs == 1 && gs.lineSegments == 1 && gs.n == 2) {
- c.type = EQUAL_LINE_ARC_LEN;
- if(SK.GetEntity(gs.entity[0])->type == Entity::ARC_OF_CIRCLE) {
+ c.type = Type::EQUAL_LINE_ARC_LEN;
+ if(SK.GetEntity(gs.entity[0])->type == Entity::Type::ARC_OF_CIRCLE) {
c.entityA = gs.entity[1];
c.entityB = gs.entity[0];
} else {
c.entityB = gs.entity[1];
}
} else {
- Error("Bad selection for equal length / radius constraint. "
- "This constraint can apply to:\n\n"
- " * two line segments (equal length)\n"
- " * two line segments and two points "
- "(equal point-line distances)\n"
- " * a line segment and two points "
- "(equal point-line distances)\n"
- " * a line segment, and a point and line segment "
- "(point-line distance equals length)\n"
- " * four line segments or normals "
- "(equal angle between A,B and C,D)\n"
- " * three line segments or normals "
- "(equal angle between A,B and B,C)\n"
- " * two circles or arcs (equal radius)\n"
- " * a line segment and an arc "
- "(line segment length equals arc length)\n");
+ Error(_("Bad selection for equal length / radius constraint. "
+ "This constraint can apply to:\n\n"
+ " * two line segments (equal length)\n"
+ " * two line segments and two points "
+ "(equal point-line distances)\n"
+ " * a line segment and two points "
+ "(equal point-line distances)\n"
+ " * a line segment, and a point and line segment "
+ "(point-line distance equals length)\n"
+ " * four line segments or normals "
+ "(equal angle between A,B and C,D)\n"
+ " * three line segments or normals "
+ "(equal angle between A,B and B,C)\n"
+ " * two circles or arcs (equal radius)\n"
+ " * a line segment and an arc "
+ "(line segment length equals arc length)\n"));
return;
}
- if(c.type == EQUAL_ANGLE) {
+ if(c.type == Type::EQUAL_ANGLE) {
// Infer the nearest supplementary angle from the sketch.
Vector a1 = SK.GetEntity(c.entityA)->VectorGetNum(),
b1 = SK.GetEntity(c.entityB)->VectorGetNum(),
AddConstraint(&c);
break;
- case GraphicsWindow::MNU_RATIO:
+ case Command::RATIO:
if(gs.lineSegments == 2 && gs.n == 2) {
- c.type = LENGTH_RATIO;
+ c.type = Type::LENGTH_RATIO;
c.entityA = gs.entity[0];
c.entityB = gs.entity[1];
} else {
- Error("Bad selection for length ratio constraint. This "
- "constraint can apply to:\n\n"
- " * two line segments\n");
+ Error(_("Bad selection for length ratio constraint. This "
+ "constraint can apply to:\n\n"
+ " * two line segments\n"));
return;
}
AddConstraint(&c);
break;
- case GraphicsWindow::MNU_DIFFERENCE:
+ case Command::DIFFERENCE:
if(gs.lineSegments == 2 && gs.n == 2) {
- c.type = LENGTH_DIFFERENCE;
+ c.type = Type::LENGTH_DIFFERENCE;
c.entityA = gs.entity[0];
c.entityB = gs.entity[1];
} else {
- Error("Bad selection for length difference constraint. This "
- "constraint can apply to:\n\n"
- " * two line segments\n");
+ Error(_("Bad selection for length difference constraint. This "
+ "constraint can apply to:\n\n"
+ " * two line segments\n"));
return;
}
AddConstraint(&c);
break;
- case GraphicsWindow::MNU_AT_MIDPOINT:
+ case Command::AT_MIDPOINT:
if(gs.lineSegments == 1 && gs.points == 1 && gs.n == 2) {
- c.type = AT_MIDPOINT;
+ c.type = Type::AT_MIDPOINT;
c.entityA = gs.entity[0];
c.ptA = gs.point[0];
// If a point is at-midpoint, then no reason to also constrain
// it on-line; so auto-remove that.
- DeleteAllConstraintsFor(PT_ON_LINE, c.entityA, c.ptA);
+ DeleteAllConstraintsFor(Type::PT_ON_LINE, c.entityA, c.ptA);
} else if(gs.lineSegments == 1 && gs.workplanes == 1 && gs.n == 2) {
- c.type = AT_MIDPOINT;
+ c.type = Type::AT_MIDPOINT;
int i = SK.GetEntity(gs.entity[0])->IsWorkplane() ? 1 : 0;
c.entityA = gs.entity[i];
c.entityB = gs.entity[1-i];
} else {
- Error("Bad selection for at midpoint constraint. This "
- "constraint can apply to:\n\n"
- " * a line segment and a point "
- "(point at midpoint)\n"
- " * a line segment and a workplane "
- "(line's midpoint on plane)\n");
+ Error(_("Bad selection for at midpoint constraint. This "
+ "constraint can apply to:\n\n"
+ " * a line segment and a point "
+ "(point at midpoint)\n"
+ " * a line segment and a workplane "
+ "(line's midpoint on plane)\n"));
return;
}
AddConstraint(&c);
break;
- case GraphicsWindow::MNU_SYMMETRIC:
+ case Command::SYMMETRIC:
if(gs.points == 2 &&
((gs.workplanes == 1 && gs.n == 3) ||
(gs.n == 2)))
c.entityA = gs.entity[0];
c.ptA = gs.point[0];
c.ptB = gs.point[1];
+ c.type = Type::SYMMETRIC;
} else if(gs.lineSegments == 1 &&
((gs.workplanes == 1 && gs.n == 2) ||
(gs.n == 1)))
}
c.ptA = line->point[0];
c.ptB = line->point[1];
+ c.type = Type::SYMMETRIC;
} else if(SS.GW.LockedInWorkplane()
&& gs.lineSegments == 2 && gs.n == 2)
{
Entity *l0 = SK.GetEntity(gs.entity[0]),
*l1 = SK.GetEntity(gs.entity[1]);
- if((l1->group.v != SS.GW.activeGroup.v) ||
+ if((l1->group != SS.GW.activeGroup) ||
(l1->construction && !(l0->construction)))
{
swap(l0, l1);
c.ptA = l1->point[0];
c.ptB = l1->point[1];
c.entityA = l0->h;
- c.type = SYMMETRIC_LINE;
+ c.type = Type::SYMMETRIC_LINE;
} else if(SS.GW.LockedInWorkplane()
&& gs.lineSegments == 1 && gs.points == 2 && gs.n == 3)
{
c.ptA = gs.point[0];
c.ptB = gs.point[1];
c.entityA = gs.entity[0];
- c.type = SYMMETRIC_LINE;
+ c.type = Type::SYMMETRIC_LINE;
} else {
- Error("Bad selection for symmetric constraint. This constraint "
- "can apply to:\n\n"
- " * two points or a line segment "
- "(symmetric about workplane's coordinate axis)\n"
- " * line segment, and two points or a line segment "
- "(symmetric about line segment)\n"
- " * workplane, and two points or a line segment "
- "(symmetric about workplane)\n");
+ Error(_("Bad selection for symmetric constraint. This constraint "
+ "can apply to:\n\n"
+ " * two points or a line segment "
+ "(symmetric about workplane's coordinate axis)\n"
+ " * line segment, and two points or a line segment "
+ "(symmetric about line segment)\n"
+ " * workplane, and two points or a line segment "
+ "(symmetric about workplane)\n"));
return;
}
- if(c.type != 0) {
- // Already done, symmetry about a line segment in a workplane
- } else if(c.entityA.v == Entity::NO_ENTITY.v) {
+ if(c.entityA == Entity::NO_ENTITY) {
// Horizontal / vertical symmetry, implicit symmetry plane
// normal to the workplane
- if(c.workplane.v == Entity::FREE_IN_3D.v) {
- Error("Must be locked in to workplane when constraining "
- "symmetric without an explicit symmetry plane.");
+ if(c.workplane == Entity::FREE_IN_3D) {
+ Error(_("A workplane must be active when constraining "
+ "symmetric without an explicit symmetry plane."));
return;
}
Vector pa = SK.GetEntity(c.ptA)->PointGetNum();
EntityBase *norm = SK.GetEntity(c.workplane)->Normal();;
Vector u = norm->NormalU(), v = norm->NormalV();
if(fabs(dp.Dot(u)) > fabs(dp.Dot(v))) {
- c.type = SYMMETRIC_HORIZ;
+ c.type = Type::SYMMETRIC_HORIZ;
} else {
- c.type = SYMMETRIC_VERT;
+ c.type = Type::SYMMETRIC_VERT;
}
if(gs.lineSegments == 1) {
// If this line segment is already constrained horiz or
// vert, then auto-remove that redundant constraint.
- DeleteAllConstraintsFor(HORIZONTAL, (gs.entity[0]),
+ DeleteAllConstraintsFor(Type::HORIZONTAL, (gs.entity[0]),
Entity::NO_ENTITY);
- DeleteAllConstraintsFor(VERTICAL, (gs.entity[0]),
+ DeleteAllConstraintsFor(Type::VERTICAL, (gs.entity[0]),
Entity::NO_ENTITY);
-
}
- } else {
- // Symmetry with a symmetry plane specified explicitly.
- c.type = SYMMETRIC;
}
AddConstraint(&c);
break;
- case GraphicsWindow::MNU_VERTICAL:
- case GraphicsWindow::MNU_HORIZONTAL: {
+ case Command::VERTICAL:
+ case Command::HORIZONTAL: {
hEntity ha, hb;
- if(c.workplane.v == Entity::FREE_IN_3D.v) {
- Error("Select workplane before constraining horiz/vert.");
+ if(c.workplane == Entity::FREE_IN_3D) {
+ Error(_("Activate a workplane (with Sketch -> In Workplane) before "
+ "applying a horizontal or vertical constraint."));
return;
}
if(gs.lineSegments == 1 && gs.n == 1) {
ha = c.ptA = gs.point[0];
hb = c.ptB = gs.point[1];
} else {
- Error("Bad selection for horizontal / vertical constraint. "
- "This constraint can apply to:\n\n"
- " * two points\n"
- " * a line segment\n");
+ Error(_("Bad selection for horizontal / vertical constraint. "
+ "This constraint can apply to:\n\n"
+ " * two points\n"
+ " * a line segment\n"));
return;
}
- if(id == GraphicsWindow::MNU_HORIZONTAL) {
- c.type = HORIZONTAL;
+ if(id == Command::HORIZONTAL) {
+ c.type = Type::HORIZONTAL;
} else {
- c.type = VERTICAL;
+ c.type = Type::VERTICAL;
}
AddConstraint(&c);
break;
}
- case GraphicsWindow::MNU_ORIENTED_SAME: {
+ case Command::ORIENTED_SAME: {
if(gs.anyNormals == 2 && gs.n == 2) {
- c.type = SAME_ORIENTATION;
+ c.type = Type::SAME_ORIENTATION;
c.entityA = gs.anyNormal[0];
c.entityB = gs.anyNormal[1];
} else {
- Error("Bad selection for same orientation constraint. This "
- "constraint can apply to:\n\n"
- " * two normals\n");
+ Error(_("Bad selection for same orientation constraint. This "
+ "constraint can apply to:\n\n"
+ " * two normals\n"));
return;
}
SS.UndoRemember();
Entity *nfree = SK.GetEntity(c.entityA);
Entity *nref = SK.GetEntity(c.entityB);
- if(nref->group.v == SS.GW.activeGroup.v) {
+ if(nref->group == SS.GW.activeGroup) {
swap(nref, nfree);
}
- if(nfree->group.v == SS.GW.activeGroup.v &&
- nref ->group.v != SS.GW.activeGroup.v)
- {
+ if(nfree->group == SS.GW.activeGroup && nref->group != SS.GW.activeGroup) {
// nfree is free, and nref is locked (since it came from a
// previous group); so let's force nfree aligned to nref,
// and make convergence easy
nfree->NormalForceTo(Quaternion::From(fu, fv));
}
- AddConstraint(&c, false);
+ AddConstraint(&c, /*rememberForUndo=*/false);
break;
}
- case GraphicsWindow::MNU_OTHER_ANGLE:
+ case Command::OTHER_ANGLE:
if(gs.constraints == 1 && gs.n == 0) {
Constraint *c = SK.GetConstraint(gs.constraint[0]);
- if(c->type == ANGLE) {
+ if(c->type == Type::ANGLE) {
SS.UndoRemember();
c->other = !(c->other);
c->ModifyToSatisfy();
break;
}
- if(c->type == EQUAL_ANGLE) {
+ if(c->type == Type::EQUAL_ANGLE) {
SS.UndoRemember();
c->other = !(c->other);
SS.MarkGroupDirty(c->group);
- SS.ScheduleGenerateAll();
break;
}
}
- Error("Must select an angle constraint.");
+ Error(_("Must select an angle constraint."));
return;
- case GraphicsWindow::MNU_REFERENCE:
+ case Command::REFERENCE:
if(gs.constraints == 1 && gs.n == 0) {
Constraint *c = SK.GetConstraint(gs.constraint[0]);
- if(c->HasLabel() && c->type != COMMENT) {
+ if(c->HasLabel() && c->type != Type::COMMENT) {
+ SS.UndoRemember();
(c->reference) = !(c->reference);
- SK.GetGroup(c->group)->clean = false;
- SS.GenerateAll();
+ SS.MarkGroupDirty(c->group, /*onlyThis=*/true);
break;
}
}
- Error("Must select a constraint with associated label.");
+ Error(_("Must select a constraint with associated label."));
return;
- case GraphicsWindow::MNU_ANGLE:
- case GraphicsWindow::MNU_REF_ANGLE: {
+ case Command::ANGLE:
+ case Command::REF_ANGLE: {
if(gs.vectors == 2 && gs.n == 2) {
- c.type = ANGLE;
+ c.type = Type::ANGLE;
c.entityA = gs.vector[0];
c.entityB = gs.vector[1];
c.valA = 0;
} else {
- Error("Bad selection for angle constraint. This constraint "
- "can apply to:\n\n"
- " * two line segments\n"
- " * a line segment and a normal\n"
- " * two normals\n");
+ Error(_("Bad selection for angle constraint. This constraint "
+ "can apply to:\n\n"
+ " * two line segments\n"
+ " * a line segment and a normal\n"
+ " * two normals\n"));
return;
}
Entity *ea = SK.GetEntity(c.entityA),
*eb = SK.GetEntity(c.entityB);
- if(ea->type == Entity::LINE_SEGMENT &&
- eb->type == Entity::LINE_SEGMENT)
+ if(ea->type == Entity::Type::LINE_SEGMENT &&
+ eb->type == Entity::Type::LINE_SEGMENT)
{
Vector a0 = SK.GetEntity(ea->point[0])->PointGetNum(),
a1 = SK.GetEntity(ea->point[1])->PointGetNum(),
}
}
- if(id == GraphicsWindow::MNU_REF_ANGLE) {
+ if(id == Command::REF_ANGLE) {
c.reference = true;
}
break;
}
- case GraphicsWindow::MNU_PARALLEL:
+ case Command::PARALLEL:
if(gs.vectors == 2 && gs.n == 2) {
- c.type = PARALLEL;
+ c.type = Type::PARALLEL;
c.entityA = gs.vector[0];
c.entityB = gs.vector[1];
} else if(gs.lineSegments == 1 && gs.arcs == 1 && gs.n == 2) {
Entity *line = SK.GetEntity(gs.entity[0]);
Entity *arc = SK.GetEntity(gs.entity[1]);
- if(line->type == Entity::ARC_OF_CIRCLE) {
+ if(line->type == Entity::Type::ARC_OF_CIRCLE) {
swap(line, arc);
}
Vector l0 = SK.GetEntity(line->point[0])->PointGetNum(),
} else if(l0.Equals(a2) || l1.Equals(a2)) {
c.other = true;
} else {
- Error("The tangent arc and line segment must share an "
- "endpoint. Constrain them with Constrain -> "
- "On Point before constraining tangent.");
+ Error(_("The tangent arc and line segment must share an "
+ "endpoint. Constrain them with Constrain -> "
+ "On Point before constraining tangent."));
return;
}
- c.type = ARC_LINE_TANGENT;
+ c.type = Type::ARC_LINE_TANGENT;
c.entityA = arc->h;
c.entityB = line->h;
} else if(gs.lineSegments == 1 && gs.cubics == 1 && gs.n == 2) {
Entity *line = SK.GetEntity(gs.entity[0]);
Entity *cubic = SK.GetEntity(gs.entity[1]);
- if(line->type == Entity::CUBIC) {
+ if(line->type == Entity::Type::CUBIC) {
swap(line, cubic);
}
Vector l0 = SK.GetEntity(line->point[0])->PointGetNum(),
} else if(l0.Equals(af) || l1.Equals(af)) {
c.other = true;
} else {
- Error("The tangent cubic and line segment must share an "
- "endpoint. Constrain them with Constrain -> "
- "On Point before constraining tangent.");
+ Error(_("The tangent cubic and line segment must share an "
+ "endpoint. Constrain them with Constrain -> "
+ "On Point before constraining tangent."));
return;
}
- c.type = CUBIC_LINE_TANGENT;
+ c.type = Type::CUBIC_LINE_TANGENT;
c.entityA = cubic->h;
c.entityB = line->h;
} else if(gs.cubics + gs.arcs == 2 && gs.n == 2) {
if(!SS.GW.LockedInWorkplane()) {
- Error("Curve-curve tangency must apply in workplane.");
+ Error(_("Curve-curve tangency must apply in workplane."));
return;
}
Entity *eA = SK.GetEntity(gs.entity[0]),
} else if(af.Equals(bf)) {
c.other = true; c.other2 = true;
} else {
- Error("The curves must share an endpoint. Constrain them "
- "with Constrain -> On Point before constraining "
- "tangent.");
+ Error(_("The curves must share an endpoint. Constrain them "
+ "with Constrain -> On Point before constraining "
+ "tangent."));
return;
}
- c.type = CURVE_CURVE_TANGENT;
+ c.type = Type::CURVE_CURVE_TANGENT;
c.entityA = eA->h;
c.entityB = eB->h;
} else {
- Error("Bad selection for parallel / tangent constraint. This "
- "constraint can apply to:\n\n"
- " * two line segments (parallel)\n"
- " * a line segment and a normal (parallel)\n"
- " * two normals (parallel)\n"
- " * two line segments, arcs, or beziers, that share "
- "an endpoint (tangent)\n");
+ Error(_("Bad selection for parallel / tangent constraint. This "
+ "constraint can apply to:\n\n"
+ " * two line segments (parallel)\n"
+ " * a line segment and a normal (parallel)\n"
+ " * two normals (parallel)\n"
+ " * two line segments, arcs, or beziers, that share "
+ "an endpoint (tangent)\n"));
return;
}
AddConstraint(&c);
break;
- case GraphicsWindow::MNU_PERPENDICULAR:
+ case Command::PERPENDICULAR:
if(gs.vectors == 2 && gs.n == 2) {
- c.type = PERPENDICULAR;
+ c.type = Type::PERPENDICULAR;
c.entityA = gs.vector[0];
c.entityB = gs.vector[1];
} else {
- Error("Bad selection for perpendicular constraint. This "
- "constraint can apply to:\n\n"
- " * two line segments\n"
- " * a line segment and a normal\n"
- " * two normals\n");
+ Error(_("Bad selection for perpendicular constraint. This "
+ "constraint can apply to:\n\n"
+ " * two line segments\n"
+ " * a line segment and a normal\n"
+ " * two normals\n"));
return;
}
AddConstraint(&c);
break;
- case GraphicsWindow::MNU_WHERE_DRAGGED:
+ case Command::WHERE_DRAGGED:
if(gs.points == 1 && gs.n == 1) {
- c.type = WHERE_DRAGGED;
+ c.type = Type::WHERE_DRAGGED;
c.ptA = gs.point[0];
} else {
- Error("Bad selection for lock point where dragged constraint. "
- "This constraint can apply to:\n\n"
- " * a point\n");
+ Error(_("Bad selection for lock point where dragged constraint. "
+ "This constraint can apply to:\n\n"
+ " * a point\n"));
return;
}
AddConstraint(&c);
break;
- case GraphicsWindow::MNU_COMMENT:
- SS.GW.pending.operation = GraphicsWindow::MNU_COMMENT;
- SS.GW.pending.description = "click center of comment text";
+ case Command::COMMENT:
+ SS.GW.pending.operation = GraphicsWindow::Pending::COMMAND;
+ SS.GW.pending.command = Command::COMMENT;
+ SS.GW.pending.description = _("click center of comment text");
SS.ScheduleShowTW();
break;
- default: oops();
+ default: ssassert(false, "Unexpected menu ID");
+ }
+
+ for(const Constraint &cc : SK.constraint) {
+ if(c.h != cc.h && c.Equals(cc)) {
+ // Oops, we already have this exact constraint. Remove the one we just added.
+ SK.constraint.RemoveById(c.h);
+ SS.GW.ClearSelection();
+ // And now select the old one, to give feedback.
+ SS.GW.MakeSelected(cc.h);
+ return;
+ }
+ }
+
+ if(SK.constraint.FindByIdNoOops(c.h)) {
+ Constraint *constraint = SK.GetConstraint(c.h);
+ if(SS.TestRankForGroup(c.group) == SolveResult::REDUNDANT_OKAY &&
+ !SK.GetGroup(SS.GW.activeGroup)->allowRedundant &&
+ constraint->HasLabel()) {
+ constraint->reference = true;
+ }
+ }
+
+ if((id == Command::DISTANCE_DIA || id == Command::ANGLE ||
+ id == Command::RATIO || id == Command::DIFFERENCE) &&
+ SS.immediatelyEditDimension) {
+ SS.GW.EditConstraint(c.h);
}
SS.GW.ClearSelection();
- InvalidateGraphics();
}
#endif /* ! LIBRARY */
const hConstraint ConstraintBase::NO_CONSTRAINT = { 0 };
-bool ConstraintBase::HasLabel(void) {
+bool ConstraintBase::HasLabel() const {
switch(type) {
- case PT_LINE_DISTANCE:
- case PT_PLANE_DISTANCE:
- case PT_FACE_DISTANCE:
- case PT_PT_DISTANCE:
- case PROJ_PT_DISTANCE:
- case DIAMETER:
- case LENGTH_RATIO:
- case LENGTH_DIFFERENCE:
- case ANGLE:
- case COMMENT:
+ case Type::PT_LINE_DISTANCE:
+ case Type::PT_PLANE_DISTANCE:
+ case Type::PT_FACE_DISTANCE:
+ case Type::PT_PT_DISTANCE:
+ case Type::PROJ_PT_DISTANCE:
+ case Type::DIAMETER:
+ case Type::LENGTH_RATIO:
+ case Type::LENGTH_DIFFERENCE:
+ case Type::ANGLE:
+ case Type::COMMENT:
return true;
default:
}
}
-Expr *ConstraintBase::VectorsParallel(int eq, ExprVector a, ExprVector b) {
- ExprVector r = a.Cross(b);
- // Hairy ball theorem screws me here. There's no clean solution that I
- // know, so let's pivot on the initial numerical guess. Our caller
- // has ensured that if one of our input vectors is already known (e.g.
- // it's from a previous group), then that one's in a; so that one's
- // not going to move, and we should pivot on that one.
- double mx = fabs((a.x)->Eval());
- double my = fabs((a.y)->Eval());
- double mz = fabs((a.z)->Eval());
- // The basis vector in which the vectors have the LEAST energy is the
- // one that we should look at most (e.g. if both vectors lie in the xy
- // plane, then the z component of the cross product is most important).
- // So find the strongest component of a and b, and that's the component
- // of the cross product to ignore.
- Expr *e0, *e1;
- if(mx > my && mx > mz) {
- e0 = r.y; e1 = r.z;
- } else if(my > mz) {
- e0 = r.z; e1 = r.x;
- } else {
- e0 = r.x; e1 = r.y;
+bool ConstraintBase::IsProjectible() const {
+ switch(type) {
+ case Type::POINTS_COINCIDENT:
+ case Type::PT_PT_DISTANCE:
+ case Type::PT_LINE_DISTANCE:
+ case Type::PT_ON_LINE:
+ case Type::EQUAL_LENGTH_LINES:
+ case Type::EQ_LEN_PT_LINE_D:
+ case Type::EQ_PT_LN_DISTANCES:
+ case Type::EQUAL_ANGLE:
+ case Type::LENGTH_RATIO:
+ case Type::LENGTH_DIFFERENCE:
+ case Type::SYMMETRIC:
+ case Type::SYMMETRIC_HORIZ:
+ case Type::SYMMETRIC_VERT:
+ case Type::SYMMETRIC_LINE:
+ case Type::AT_MIDPOINT:
+ case Type::HORIZONTAL:
+ case Type::VERTICAL:
+ case Type::ANGLE:
+ case Type::PARALLEL:
+ case Type::PERPENDICULAR:
+ case Type::WHERE_DRAGGED:
+ case Type::COMMENT:
+ return true;
+
+ case Type::PT_PLANE_DISTANCE:
+ case Type::PT_FACE_DISTANCE:
+ case Type::PROJ_PT_DISTANCE:
+ case Type::PT_IN_PLANE:
+ case Type::PT_ON_FACE:
+ case Type::EQUAL_LINE_ARC_LEN:
+ case Type::DIAMETER:
+ case Type::PT_ON_CIRCLE:
+ case Type::SAME_ORIENTATION:
+ case Type::CUBIC_LINE_TANGENT:
+ case Type::CURVE_CURVE_TANGENT:
+ case Type::ARC_LINE_TANGENT:
+ case Type::EQUAL_RADIUS:
+ return false;
}
+ ssassert(false, "Impossible");
+}
- if(eq == 0) return e0;
- if(eq == 1) return e1;
- oops();
+ExprVector ConstraintBase::VectorsParallel3d(ExprVector a, ExprVector b, hParam p) {
+ return a.Minus(b.ScaledBy(Expr::From(p)));
}
Expr *ConstraintBase::PointLineDistance(hEntity wrkpl, hEntity hpt, hEntity hln)
EntityBase *p = SK.GetEntity(hpt);
- if(wrkpl.v == EntityBase::FREE_IN_3D.v) {
+ if(wrkpl == EntityBase::FREE_IN_3D) {
ExprVector ep = p->PointGetExprs();
ExprVector ea = a->PointGetExprs();
Expr *ConstraintBase::Distance(hEntity wrkpl, hEntity hpa, hEntity hpb) {
EntityBase *pa = SK.GetEntity(hpa);
EntityBase *pb = SK.GetEntity(hpb);
- if(!(pa->IsPoint() && pb->IsPoint())) oops();
+ ssassert(pa->IsPoint() && pb->IsPoint(),
+ "Expected two points to measure projected distance between");
- if(wrkpl.v == EntityBase::FREE_IN_3D.v) {
+ if(wrkpl == EntityBase::FREE_IN_3D) {
// This is true distance
ExprVector ea, eb, eab;
ea = pa->PointGetExprs();
Expr *ConstraintBase::DirectionCosine(hEntity wrkpl,
ExprVector ae, ExprVector be)
{
- if(wrkpl.v == EntityBase::FREE_IN_3D.v) {
+ if(wrkpl == EntityBase::FREE_IN_3D) {
Expr *mags = (ae.Magnitude())->Times(be.Magnitude());
return (ae.Dot(be))->Div(mags);
} else {
return (ub.ScaledBy(u)).Plus(vb.ScaledBy(v)).Plus(ob);
}
-void ConstraintBase::ModifyToSatisfy(void) {
- if(type == ANGLE) {
+void ConstraintBase::ModifyToSatisfy() {
+ if(type == Type::ANGLE) {
Vector a = SK.GetEntity(entityA)->VectorGetNum();
Vector b = SK.GetEntity(entityB)->VectorGetNum();
if(other) a = a.ScaledBy(-1);
- if(workplane.v != EntityBase::FREE_IN_3D.v) {
+ if(workplane != EntityBase::FREE_IN_3D) {
a = a.ProjectVectorInto(workplane);
b = b.ProjectVectorInto(workplane);
}
double c = (a.Dot(b))/(a.Magnitude() * b.Magnitude());
valA = acos(c)*180/PI;
+ } else if(type == Type::PT_ON_LINE) {
+ EntityBase *eln = SK.GetEntity(entityA);
+ EntityBase *ea = SK.GetEntity(eln->point[0]);
+ EntityBase *eb = SK.GetEntity(eln->point[1]);
+ EntityBase *ep = SK.GetEntity(ptA);
+ ExprVector exp = ep->PointGetExprsInWorkplane(workplane);
+ ExprVector exa = ea->PointGetExprsInWorkplane(workplane);
+ ExprVector exb = eb->PointGetExprsInWorkplane(workplane);
+ ExprVector exba = exb.Minus(exa);
+ SK.GetParam(valP)->val = exba.Dot(exp.Minus(exa))->Eval() / exba.Dot(exba)->Eval();
} else {
// We'll fix these ones up by looking at their symbolic equation;
// that means no extra work.
IdList<Equation,hEquation> l = {};
// Generate the equations even if this is a reference dimension
- GenerateReal(&l);
- if(l.n != 1) oops();
+ GenerateEquations(&l, /*forReference=*/true);
+ ssassert(l.n == 1, "Expected constraint to generate a single equation");
// These equations are written in the form f(...) - d = 0, where
// d is the value of the valA.
- valA += (l.elem[0].e)->Eval();
+ valA += (l[0].e)->Eval();
l.Clear();
}
}
-void ConstraintBase::AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index)
+void ConstraintBase::AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index) const
{
Equation eq;
eq.e = expr;
l->Add(&eq);
}
-void ConstraintBase::Generate(IdList<Equation,hEquation> *l) {
- if(!reference) {
- GenerateReal(l);
+void ConstraintBase::AddEq(IdList<Equation,hEquation> *l, const ExprVector &v,
+ int baseIndex) const {
+ AddEq(l, v.x, baseIndex);
+ AddEq(l, v.y, baseIndex + 1);
+ if(workplane == EntityBase::FREE_IN_3D) {
+ AddEq(l, v.z, baseIndex + 2);
}
}
-void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
- Expr *exA = Expr::From(valA);
+void ConstraintBase::Generate(IdList<Param,hParam> *l) {
switch(type) {
- case PT_PT_DISTANCE:
- AddEq(l, Distance(workplane, ptA, ptB)->Minus(exA), 0);
+ case Type::PARALLEL:
+ case Type::CUBIC_LINE_TANGENT:
+ // Add new parameter only when we operate in 3d space
+ if(workplane != EntityBase::FREE_IN_3D) break;
+ // fallthrough
+ case Type::SAME_ORIENTATION:
+ case Type::PT_ON_LINE: {
+ Param p = {};
+ valP = h.param(0);
+ p.h = valP;
+ l->Add(&p);
+ break;
+ }
+
+ default:
break;
+ }
+}
+
+void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
+ bool forReference) const {
+ if(reference && !forReference) return;
+
+ Expr *exA = Expr::From(valA);
+ switch(type) {
+ case Type::PT_PT_DISTANCE:
+ AddEq(l, Distance(workplane, ptA, ptB)->Minus(exA), 0);
+ return;
- case PROJ_PT_DISTANCE: {
+ case Type::PROJ_PT_DISTANCE: {
ExprVector pA = SK.GetEntity(ptA)->PointGetExprs(),
pB = SK.GetEntity(ptB)->PointGetExprs(),
dp = pB.Minus(pA);
pp = pp.WithMagnitude(Expr::From(1.0));
AddEq(l, (dp.Dot(pp))->Minus(exA), 0);
- break;
+ return;
}
- case PT_LINE_DISTANCE:
+ case Type::PT_LINE_DISTANCE:
AddEq(l,
PointLineDistance(workplane, ptA, entityA)->Minus(exA), 0);
- break;
+ return;
- case PT_PLANE_DISTANCE: {
+ case Type::PT_PLANE_DISTANCE: {
ExprVector pt = SK.GetEntity(ptA)->PointGetExprs();
AddEq(l, (PointPlaneDistance(pt, entityA))->Minus(exA), 0);
- break;
+ return;
}
- case PT_FACE_DISTANCE: {
+ case Type::PT_FACE_DISTANCE: {
ExprVector pt = SK.GetEntity(ptA)->PointGetExprs();
EntityBase *f = SK.GetEntity(entityA);
ExprVector p0 = f->FaceGetPointExprs();
ExprVector n = f->FaceGetNormalExprs();
AddEq(l, (pt.Minus(p0)).Dot(n)->Minus(exA), 0);
- break;
+ return;
}
- case EQUAL_LENGTH_LINES: {
+ case Type::EQUAL_LENGTH_LINES: {
EntityBase *a = SK.GetEntity(entityA);
EntityBase *b = SK.GetEntity(entityB);
AddEq(l, Distance(workplane, a->point[0], a->point[1])->Minus(
Distance(workplane, b->point[0], b->point[1])), 0);
- break;
+ return;
}
// These work on distance squared, since the pt-line distances are
// signed, and we want the absolute value.
- case EQ_LEN_PT_LINE_D: {
+ case Type::EQ_LEN_PT_LINE_D: {
EntityBase *forLen = SK.GetEntity(entityA);
Expr *d1 = Distance(workplane, forLen->point[0], forLen->point[1]);
Expr *d2 = PointLineDistance(workplane, ptA, entityB);
AddEq(l, (d1->Square())->Minus(d2->Square()), 0);
- break;
+ return;
}
- case EQ_PT_LN_DISTANCES: {
+ case Type::EQ_PT_LN_DISTANCES: {
Expr *d1 = PointLineDistance(workplane, ptA, entityA);
Expr *d2 = PointLineDistance(workplane, ptB, entityB);
AddEq(l, (d1->Square())->Minus(d2->Square()), 0);
- break;
+ return;
}
- case LENGTH_RATIO: {
+ case Type::LENGTH_RATIO: {
EntityBase *a = SK.GetEntity(entityA);
EntityBase *b = SK.GetEntity(entityB);
Expr *la = Distance(workplane, a->point[0], a->point[1]);
Expr *lb = Distance(workplane, b->point[0], b->point[1]);
AddEq(l, (la->Div(lb))->Minus(exA), 0);
- break;
+ return;
}
- case LENGTH_DIFFERENCE: {
+ case Type::LENGTH_DIFFERENCE: {
EntityBase *a = SK.GetEntity(entityA);
EntityBase *b = SK.GetEntity(entityB);
Expr *la = Distance(workplane, a->point[0], a->point[1]);
Expr *lb = Distance(workplane, b->point[0], b->point[1]);
AddEq(l, (la->Minus(lb))->Minus(exA), 0);
- break;
+ return;
}
- case DIAMETER: {
+ case Type::DIAMETER: {
EntityBase *circle = SK.GetEntity(entityA);
Expr *r = circle->CircleGetRadiusExpr();
AddEq(l, (r->Times(Expr::From(2)))->Minus(exA), 0);
- break;
+ return;
}
- case EQUAL_RADIUS: {
+ case Type::EQUAL_RADIUS: {
EntityBase *c1 = SK.GetEntity(entityA);
EntityBase *c2 = SK.GetEntity(entityB);
AddEq(l, (c1->CircleGetRadiusExpr())->Minus(
c2->CircleGetRadiusExpr()), 0);
- break;
+ return;
}
- case EQUAL_LINE_ARC_LEN: {
+ case Type::EQUAL_LINE_ARC_LEN: {
EntityBase *line = SK.GetEntity(entityA),
*arc = SK.GetEntity(entityB);
// And write the equation; r*theta = L
AddEq(l, (r->Times(theta))->Minus(ll), 0);
- break;
+ return;
}
- case POINTS_COINCIDENT: {
+ case Type::POINTS_COINCIDENT: {
EntityBase *a = SK.GetEntity(ptA);
EntityBase *b = SK.GetEntity(ptB);
- if(workplane.v == EntityBase::FREE_IN_3D.v) {
+ if(workplane == EntityBase::FREE_IN_3D) {
ExprVector pa = a->PointGetExprs();
ExprVector pb = b->PointGetExprs();
AddEq(l, pa.x->Minus(pb.x), 0);
AddEq(l, au->Minus(bu), 0);
AddEq(l, av->Minus(bv), 1);
}
- break;
+ return;
}
- case PT_IN_PLANE:
+ case Type::PT_IN_PLANE:
// This one works the same, whether projected or not.
AddEq(l, PointPlaneDistance(
SK.GetEntity(ptA)->PointGetExprs(), entityA), 0);
- break;
+ return;
- case PT_ON_FACE: {
+ case Type::PT_ON_FACE: {
// a plane, n dot (p - p0) = 0
ExprVector p = SK.GetEntity(ptA)->PointGetExprs();
EntityBase *f = SK.GetEntity(entityA);
ExprVector p0 = f->FaceGetPointExprs();
ExprVector n = f->FaceGetNormalExprs();
AddEq(l, (p.Minus(p0)).Dot(n), 0);
- break;
+ return;
}
- case PT_ON_LINE:
- if(workplane.v == EntityBase::FREE_IN_3D.v) {
- EntityBase *ln = SK.GetEntity(entityA);
- EntityBase *a = SK.GetEntity(ln->point[0]);
- EntityBase *b = SK.GetEntity(ln->point[1]);
- EntityBase *p = SK.GetEntity(ptA);
-
- ExprVector ep = p->PointGetExprs();
- ExprVector ea = a->PointGetExprs();
- ExprVector eb = b->PointGetExprs();
- ExprVector eab = ea.Minus(eb);
-
- // Construct a vector from the point to either endpoint of
- // the line segment, and choose the longer of these.
- ExprVector eap = ea.Minus(ep);
- ExprVector ebp = eb.Minus(ep);
- ExprVector elp =
- (ebp.Magnitude()->Eval() > eap.Magnitude()->Eval()) ?
- ebp : eap;
-
- if(p->group.v == group.v) {
- AddEq(l, VectorsParallel(0, eab, elp), 0);
- AddEq(l, VectorsParallel(1, eab, elp), 1);
- } else {
- AddEq(l, VectorsParallel(0, elp, eab), 0);
- AddEq(l, VectorsParallel(1, elp, eab), 1);
- }
- } else {
- AddEq(l, PointLineDistance(workplane, ptA, entityA), 0);
- }
- break;
+ case Type::PT_ON_LINE: {
+ EntityBase *ln = SK.GetEntity(entityA);
+ EntityBase *a = SK.GetEntity(ln->point[0]);
+ EntityBase *b = SK.GetEntity(ln->point[1]);
+ EntityBase *p = SK.GetEntity(ptA);
+
+ ExprVector ep = p->PointGetExprsInWorkplane(workplane);
+ ExprVector ea = a->PointGetExprsInWorkplane(workplane);
+ ExprVector eb = b->PointGetExprsInWorkplane(workplane);
+
+ ExprVector ptOnLine = ea.Plus(eb.Minus(ea).ScaledBy(Expr::From(valP)));
+ ExprVector eq = ptOnLine.Minus(ep);
- case PT_ON_CIRCLE: {
+ AddEq(l, eq);
+ return;
+ }
+
+ case Type::PT_ON_CIRCLE: {
// This actually constrains the point to lie on the cylinder.
EntityBase *circle = SK.GetEntity(entityA);
ExprVector center = SK.GetEntity(circle->point[0])->PointGetExprs();
Expr *r = circle->CircleGetRadiusExpr();
- AddEq(l,
- ((du->Square())->Plus(dv->Square()))->Minus(r->Square()), 0);
- break;
+ AddEq(l, du->Square()->Plus(dv->Square())->Sqrt()->Minus(r), 0);
+ return;
}
- case AT_MIDPOINT:
- if(workplane.v == EntityBase::FREE_IN_3D.v) {
+ case Type::AT_MIDPOINT:
+ if(workplane == EntityBase::FREE_IN_3D) {
EntityBase *ln = SK.GetEntity(entityA);
ExprVector a = SK.GetEntity(ln->point[0])->PointGetExprs();
ExprVector b = SK.GetEntity(ln->point[1])->PointGetExprs();
AddEq(l, PointPlaneDistance(m, entityB), 0);
}
}
- break;
+ return;
- case SYMMETRIC:
- if(workplane.v == EntityBase::FREE_IN_3D.v) {
+ case Type::SYMMETRIC:
+ if(workplane == EntityBase::FREE_IN_3D) {
EntityBase *plane = SK.GetEntity(entityA);
EntityBase *ea = SK.GetEntity(ptA);
EntityBase *eb = SK.GetEntity(ptB);
plane->WorkplaneGetPlaneExprs(&n, &d);
AddEq(l, (n.Cross(u.Cross(v))).Dot(pa.Minus(pb)), 1);
}
- break;
+ return;
+
+ case Type::SYMMETRIC_HORIZ:
+ case Type::SYMMETRIC_VERT: {
+ ssassert(workplane != Entity::FREE_IN_3D,
+ "Unexpected horizontal/vertical symmetric constraint in 3d");
- case SYMMETRIC_HORIZ:
- case SYMMETRIC_VERT: {
EntityBase *a = SK.GetEntity(ptA);
EntityBase *b = SK.GetEntity(ptB);
a->PointGetExprsInWorkplane(workplane, &au, &av);
b->PointGetExprsInWorkplane(workplane, &bu, &bv);
- if(type == SYMMETRIC_HORIZ) {
+ if(type == Type::SYMMETRIC_HORIZ) {
AddEq(l, av->Minus(bv), 0);
AddEq(l, au->Plus(bu), 1);
} else {
AddEq(l, au->Minus(bu), 0);
AddEq(l, av->Plus(bv), 1);
}
- break;
+ return;
}
- case SYMMETRIC_LINE: {
+ case Type::SYMMETRIC_LINE: {
EntityBase *pa = SK.GetEntity(ptA);
EntityBase *pb = SK.GetEntity(ptB);
(dlu->Times(lav->Minus(pbv))));
AddEq(l, dista->Plus(distb), 1);
- break;
+ return;
}
- case HORIZONTAL:
- case VERTICAL: {
+ case Type::HORIZONTAL:
+ case Type::VERTICAL: {
+ ssassert(workplane != Entity::FREE_IN_3D,
+ "Unexpected horizontal/vertical constraint in 3d");
+
hEntity ha, hb;
if(entityA.v) {
EntityBase *e = SK.GetEntity(entityA);
a->PointGetExprsInWorkplane(workplane, &au, &av);
b->PointGetExprsInWorkplane(workplane, &bu, &bv);
- AddEq(l, (type == HORIZONTAL) ? av->Minus(bv) : au->Minus(bu), 0);
- break;
+ AddEq(l, (type == Type::HORIZONTAL) ? av->Minus(bv) : au->Minus(bu), 0);
+ return;
}
- case SAME_ORIENTATION: {
+ case Type::SAME_ORIENTATION: {
EntityBase *a = SK.GetEntity(entityA);
EntityBase *b = SK.GetEntity(entityB);
- if(b->group.v != group.v) {
- swap(a, b);
- }
ExprVector au = a->NormalExprsU(),
an = a->NormalExprsN();
bv = b->NormalExprsV(),
bn = b->NormalExprsN();
- AddEq(l, VectorsParallel(0, an, bn), 0);
- AddEq(l, VectorsParallel(1, an, bn), 1);
+ ExprVector eq = VectorsParallel3d(an, bn, valP);
+ AddEq(l, eq.x, 0);
+ AddEq(l, eq.y, 1);
+ AddEq(l, eq.z, 2);
Expr *d1 = au.Dot(bv);
Expr *d2 = au.Dot(bu);
// Allow either orientation for the coordinate system, depending
// on how it was drawn.
if(fabs(d1->Eval()) < fabs(d2->Eval())) {
- AddEq(l, d1, 2);
+ AddEq(l, d1, 3);
} else {
- AddEq(l, d2, 2);
+ AddEq(l, d2, 3);
}
- break;
+ return;
}
- case PERPENDICULAR:
- case ANGLE: {
+ case Type::PERPENDICULAR:
+ case Type::ANGLE: {
EntityBase *a = SK.GetEntity(entityA);
EntityBase *b = SK.GetEntity(entityB);
ExprVector ae = a->VectorGetExprs();
if(other) ae = ae.ScaledBy(Expr::From(-1));
Expr *c = DirectionCosine(workplane, ae, be);
- if(type == ANGLE) {
+ if(type == Type::ANGLE) {
// The direction cosine is equal to the cosine of the
// specified angle
Expr *rads = exA->Times(Expr::From(PI/180)),
// is equal to zero, perpendicular.
AddEq(l, c, 0);
}
- break;
+ return;
}
- case EQUAL_ANGLE: {
+ case Type::EQUAL_ANGLE: {
EntityBase *a = SK.GetEntity(entityA);
EntityBase *b = SK.GetEntity(entityB);
EntityBase *c = SK.GetEntity(entityC);
Expr *ccd = DirectionCosine(workplane, ce, de);
AddEq(l, cab->Minus(ccd), 0);
- break;
+ return;
}
- case ARC_LINE_TANGENT: {
+ case Type::ARC_LINE_TANGENT: {
EntityBase *arc = SK.GetEntity(entityA);
EntityBase *line = SK.GetEntity(entityB);
// The line is perpendicular to the radius
AddEq(l, ld.Dot(ac.Minus(ap)), 0);
- break;
+ return;
}
- case CUBIC_LINE_TANGENT: {
+ case Type::CUBIC_LINE_TANGENT: {
EntityBase *cubic = SK.GetEntity(entityA);
EntityBase *line = SK.GetEntity(entityB);
ExprVector b = line->VectorGetExprs();
- if(workplane.v == EntityBase::FREE_IN_3D.v) {
- AddEq(l, VectorsParallel(0, a, b), 0);
- AddEq(l, VectorsParallel(1, a, b), 1);
+ if(workplane == EntityBase::FREE_IN_3D) {
+ ExprVector eq = VectorsParallel3d(a, b, valP);
+ AddEq(l, eq);
} else {
EntityBase *w = SK.GetEntity(workplane);
ExprVector wn = w->Normal()->NormalExprsN();
AddEq(l, (a.Cross(b)).Dot(wn), 0);
}
- break;
+ return;
}
- case CURVE_CURVE_TANGENT: {
+ case Type::CURVE_CURVE_TANGENT: {
bool parallel = true;
int i;
ExprVector dir[2];
EntityBase *e = SK.GetEntity((i == 0) ? entityA : entityB);
bool oth = (i == 0) ? other : other2;
- if(e->type == Entity::ARC_OF_CIRCLE) {
+ if(e->type == Entity::Type::ARC_OF_CIRCLE) {
ExprVector center, endpoint;
center = SK.GetEntity(e->point[0])->PointGetExprs();
endpoint =
// an endpoint; so that's normal to the tangent, not
// parallel.
parallel = !parallel;
- } else if(e->type == Entity::CUBIC) {
+ } else if(e->type == Entity::Type::CUBIC) { // BRANCH_ALWAYS_TAKEN
if(oth) {
dir[i] = e->CubicGetFinishTangentExprs();
} else {
dir[i] = e->CubicGetStartTangentExprs();
}
} else {
- oops();
+ ssassert(false, "Unexpected entity types for CURVE_CURVE_TANGENT");
}
}
if(parallel) {
} else {
AddEq(l, (dir[0]).Dot(dir[1]), 0);
}
- break;
+ return;
}
- case PARALLEL: {
+ case Type::PARALLEL: {
EntityBase *ea = SK.GetEntity(entityA), *eb = SK.GetEntity(entityB);
- if(eb->group.v != group.v) {
- swap(ea, eb);
- }
- ExprVector a = ea->VectorGetExprs();
- ExprVector b = eb->VectorGetExprs();
+ ExprVector a = ea->VectorGetExprsInWorkplane(workplane);
+ ExprVector b = eb->VectorGetExprsInWorkplane(workplane);
- if(workplane.v == EntityBase::FREE_IN_3D.v) {
- AddEq(l, VectorsParallel(0, a, b), 0);
- AddEq(l, VectorsParallel(1, a, b), 1);
+ if(workplane == EntityBase::FREE_IN_3D) {
+ ExprVector eq = VectorsParallel3d(a, b, valP);
+ AddEq(l, eq);
} else {
- EntityBase *w = SK.GetEntity(workplane);
- ExprVector wn = w->Normal()->NormalExprsN();
- AddEq(l, (a.Cross(b)).Dot(wn), 0);
+ // We use expressions written in workplane csys, so we can assume the workplane
+ // normal is (0, 0, 1). We can write the equation as:
+ // Expr *eq = a.Cross(b).Dot(ExprVector::From(0.0, 0.0, 1.0));
+ // but this will just result in elimination of x and y terms after dot product.
+ // We can only use the z expression:
+ // Expr *eq = a.Cross(b).z;
+ // but it's more efficient to write it in the terms of pseudo-scalar product:
+ Expr *eq = (a.x->Times(b.y))->Minus(a.y->Times(b.x));
+ AddEq(l, eq, 0);
}
- break;
+
+ return;
}
- case WHERE_DRAGGED: {
+ case Type::WHERE_DRAGGED: {
EntityBase *ep = SK.GetEntity(ptA);
- if(workplane.v == EntityBase::FREE_IN_3D.v) {
+ if(workplane == EntityBase::FREE_IN_3D) {
ExprVector ev = ep->PointGetExprs();
Vector v = ep->PointGetNum();
AddEq(l, u->Minus(Expr::From(u->Eval())), 0);
AddEq(l, v->Minus(Expr::From(v->Eval())), 1);
}
- break;
+ return;
}
- case COMMENT:
- break;
-
- default: oops();
+ case Type::COMMENT:
+ return;
}
+ ssassert(false, "Unexpected constraint ID");
}
#include "solvespace.h"
void TextWindow::ScreenUnselectAll(int link, uint32_t v) {
- GraphicsWindow::MenuEdit(GraphicsWindow::MNU_UNSELECT_ALL);
+ GraphicsWindow::MenuEdit(Command::UNSELECT_ALL);
}
void TextWindow::ScreenEditTtfText(int link, uint32_t v) {
Request *r = SK.GetRequest(hr);
SS.TW.ShowEditControl(10, r->str);
- SS.TW.edit.meaning = EDIT_TTF_TEXT;
+ SS.TW.edit.meaning = Edit::TTF_TEXT;
SS.TW.edit.request = hr;
}
-#define gs (SS.GW.gs)
void TextWindow::ScreenSetTtfFont(int link, uint32_t v) {
int i = (int)v;
if(i < 0) return;
if(i >= SS.fonts.l.n) return;
SS.GW.GroupSelection();
+ auto const &gs = SS.GW.gs;
if(gs.entities != 1 || gs.n != 1) return;
Entity *e = SK.entity.FindByIdNoOops(gs.entity[0]);
- if(!e || e->type != Entity::TTF_TEXT || !e->h.isFromRequest()) return;
+ if(!e || e->type != Entity::Type::TTF_TEXT || !e->h.isFromRequest()) return;
Request *r = SK.request.FindByIdNoOops(e->h.request());
if(!r) return;
SS.UndoRemember();
- r->font = SS.fonts.l.elem[i].FontFileBaseName();
+ r->font = SS.fonts.l[i].FontFileBaseName();
SS.MarkGroupDirty(r->group);
- SS.ScheduleGenerateAll();
+ SS.ScheduleShowTW();
+}
+
+void TextWindow::ScreenConstraintToggleReference(int link, uint32_t v) {
+ hConstraint hc = { v };
+ Constraint *c = SK.GetConstraint(hc);
+
+ SS.UndoRemember();
+ c->reference = !c->reference;
+
SS.ScheduleShowTW();
}
SS.ScheduleShowTW();
}
-void TextWindow::DescribeSelection(void) {
- Entity *e;
- Vector p;
- int i;
+void TextWindow::DescribeSelection() {
Printf(false, "");
+ auto const &gs = SS.GW.gs;
if(gs.n == 1 && (gs.points == 1 || gs.entities == 1)) {
- e = SK.GetEntity(gs.points == 1 ? gs.point[0] : gs.entity[0]);
+ Entity *e = SK.GetEntity(gs.points == 1 ? gs.point[0] : gs.entity[0]);
+ Vector p;
#define COSTR(p) \
SS.MmToString((p).x).c_str(), \
#define PT_AS_STR "(%Fi%s%E, %Fi%s%E, %Fi%s%E)"
#define PT_AS_NUM "(%Fi%3%E, %Fi%3%E, %Fi%3%E)"
switch(e->type) {
- case Entity::POINT_IN_3D:
- case Entity::POINT_IN_2D:
- case Entity::POINT_N_TRANS:
- case Entity::POINT_N_ROT_TRANS:
- case Entity::POINT_N_COPY:
- case Entity::POINT_N_ROT_AA:
+ case Entity::Type::POINT_IN_3D:
+ case Entity::Type::POINT_IN_2D:
+ case Entity::Type::POINT_N_TRANS:
+ case Entity::Type::POINT_N_ROT_TRANS:
+ case Entity::Type::POINT_N_COPY:
+ case Entity::Type::POINT_N_ROT_AA:
p = e->PointGetNum();
Printf(false, "%FtPOINT%E at " PT_AS_STR, COSTR(p));
break;
- case Entity::NORMAL_IN_3D:
- case Entity::NORMAL_IN_2D:
- case Entity::NORMAL_N_COPY:
- case Entity::NORMAL_N_ROT:
- case Entity::NORMAL_N_ROT_AA: {
+ case Entity::Type::NORMAL_IN_3D:
+ case Entity::Type::NORMAL_IN_2D:
+ case Entity::Type::NORMAL_N_COPY:
+ case Entity::Type::NORMAL_N_ROT:
+ case Entity::Type::NORMAL_N_ROT_AA: {
Quaternion q = e->NormalGetNum();
p = q.RotationN();
Printf(false, "%FtNORMAL / COORDINATE SYSTEM%E");
Printf(false, " v = " PT_AS_NUM, CO(p));
break;
}
- case Entity::WORKPLANE: {
+ case Entity::Type::WORKPLANE: {
p = SK.GetEntity(e->point[0])->PointGetNum();
Printf(false, "%FtWORKPLANE%E");
Printf(true, " origin = " PT_AS_STR, COSTR(p));
Printf(true, " normal = " PT_AS_NUM, CO(p));
break;
}
- case Entity::LINE_SEGMENT: {
+ case Entity::Type::LINE_SEGMENT: {
Vector p0 = SK.GetEntity(e->point[0])->PointGetNum();
p = p0;
Printf(false, "%FtLINE SEGMENT%E");
SS.MmToString((p1.Minus(p0).Magnitude())).c_str());
break;
}
- case Entity::CUBIC_PERIODIC:
- case Entity::CUBIC:
+ case Entity::Type::CUBIC_PERIODIC:
+ case Entity::Type::CUBIC:
int pts;
- if(e->type == Entity::CUBIC_PERIODIC) {
+ if(e->type == Entity::Type::CUBIC_PERIODIC) {
Printf(false, "%FtPERIODIC C2 CUBIC SPLINE%E");
pts = (3 + e->extraPoints);
} else if(e->extraPoints > 0) {
Printf(false, "%FtCUBIC BEZIER CURVE%E");
pts = 4;
}
- for(i = 0; i < pts; i++) {
+ for(int i = 0; i < pts; i++) {
p = SK.GetEntity(e->point[i])->PointGetNum();
Printf((i==0), " p%d = " PT_AS_STR, i, COSTR(p));
}
break;
- case Entity::ARC_OF_CIRCLE: {
+ case Entity::Type::ARC_OF_CIRCLE: {
Printf(false, "%FtARC OF A CIRCLE%E");
p = SK.GetEntity(e->point[0])->PointGetNum();
Printf(true, " center = " PT_AS_STR, COSTR(p));
Printf(false, " arc len = %Fi%s", SS.MmToString(dtheta*r).c_str());
break;
}
- case Entity::CIRCLE: {
+ case Entity::Type::CIRCLE: {
Printf(false, "%FtCIRCLE%E");
p = SK.GetEntity(e->point[0])->PointGetNum();
Printf(true, " center = " PT_AS_STR, COSTR(p));
Printf(false, " radius = %Fi%s", SS.MmToString(r).c_str());
break;
}
- case Entity::FACE_NORMAL_PT:
- case Entity::FACE_XPROD:
- case Entity::FACE_N_ROT_TRANS:
- case Entity::FACE_N_ROT_AA:
- case Entity::FACE_N_TRANS:
+ case Entity::Type::FACE_NORMAL_PT:
+ case Entity::Type::FACE_XPROD:
+ case Entity::Type::FACE_N_ROT_TRANS:
+ case Entity::Type::FACE_N_ROT_AA:
+ case Entity::Type::FACE_N_TRANS:
Printf(false, "%FtPLANE FACE%E");
p = e->FaceGetNormalNum();
Printf(true, " normal = " PT_AS_NUM, CO(p));
Printf(false, " thru = " PT_AS_STR, COSTR(p));
break;
- case Entity::TTF_TEXT: {
+ case Entity::Type::TTF_TEXT: {
Printf(false, "%FtTRUETYPE FONT TEXT%E");
Printf(true, " font = '%Fi%s%E'", e->font.c_str());
if(e->h.isFromRequest()) {
e->str.c_str(), &ScreenEditTtfText, e->h.request().v);
Printf(true, " select new font");
SS.fonts.LoadAll();
- int i;
- for(i = 0; i < SS.fonts.l.n; i++) {
- TtfFont *tf = &(SS.fonts.l.elem[i]);
+ // Not using range-for here because we use i inside the output.
+ for(int i = 0; i < SS.fonts.l.n; i++) {
+ TtfFont *tf = &(SS.fonts.l[i]);
if(e->font == tf->FontFileBaseName()) {
Printf(false, "%Bp %s",
(i & 1) ? 'd' : 'a',
}
break;
}
+ case Entity::Type::IMAGE: {
+ Printf(false, "%FtIMAGE%E");
+ Platform::Path relativePath = e->file.RelativeTo(SS.saveFile.Parent());
+ if(relativePath.IsEmpty()) {
+ Printf(true, " file = '%Fi%s%E'", e->file.raw.c_str());
+ } else {
+ Printf(true, " file = '%Fi%s%E'", relativePath.raw.c_str());
+ }
+ break;
+ }
default:
Printf(true, "%Ft?? ENTITY%E");
break;
}
- Group *g = SK.GetGroup(e->group);
Printf(false, "");
- Printf(false, "%FtIN GROUP%E %s", g->DescriptionString().c_str());
- if(e->workplane.v == Entity::FREE_IN_3D.v) {
+ if(e->h.isFromRequest()) {
+ Request *r = SK.GetRequest(e->h.request());
+ if(e->h == r->h.entity(0)) {
+ Printf(false, "%FtFROM REQUEST%E %s",
+ r->DescriptionString().c_str());
+ } else {
+ Printf(false, "%FtFROM REQUEST%E %Fl%Ll%D%f%h%s%E",
+ r->h.v, (&TextWindow::ScreenSelectRequest), &(TextWindow::ScreenHoverRequest),
+ r->DescriptionString().c_str());
+ }
+ }
+ Group *g = SK.GetGroup(e->group);
+ Printf(false, "%FtIN GROUP%E %Fl%Ll%D%f%s%E",
+ g->h.v, (&TextWindow::ScreenSelectGroup),
+ g->DescriptionString().c_str());
+ if(e->workplane == Entity::FREE_IN_3D) {
Printf(false, "%FtNOT LOCKED IN WORKPLANE%E");
} else {
Entity *w = SK.GetEntity(e->workplane);
- Printf(false, "%FtIN WORKPLANE%E %s", w->DescriptionString().c_str());
+ if(w->h.isFromRequest()) {
+ Printf(false, "%FtIN WORKPLANE%E %Fl%Ll%D%f%h%s%E",
+ w->h.request().v,
+ (&TextWindow::ScreenSelectRequest), &(TextWindow::ScreenHoverRequest),
+ w->DescriptionString().c_str());
+ } else {
+ Printf(false, "%FtIN WORKPLANE%E %Fl%Ll%D%f%h%s%E",
+ w->h.group().v,
+ (&TextWindow::ScreenSelectGroup), (&TextWindow::ScreenHoverGroupWorkplane),
+ w->DescriptionString().c_str());
+ }
}
- if(e->style.v) {
- Style *s = Style::Get(e->style);
- Printf(false, "%FtIN STYLE%E %s", s->DescriptionString().c_str());
- } else {
- Printf(false, "%FtIN STYLE%E none");
+ if(e->IsStylable()) {
+ if(e->style.v) {
+ Style *s = Style::Get(e->style);
+ Printf(false, "%FtIN STYLE%E %Fl%Ll%D%f%s%E",
+ s->h.v, (&TextWindow::ScreenShowStyleInfo),
+ s->DescriptionString().c_str());
+ } else {
+ Printf(false, "%FtIN STYLE%E none");
+ }
}
if(e->construction) {
Printf(false, "%FtCONSTRUCTION");
}
+
+ std::vector<hConstraint> lhc = {};
+ auto FindConstraints = [&](hEntity he) {
+ for(const Constraint &c : SK.constraint) {
+ if(!(c.ptA == he || c.ptB == he ||
+ c.entityA == he || c.entityB == he || c.entityC == he || c.entityD == he))
+ continue;
+ lhc.push_back(c.h);
+ }
+ };
+ FindConstraints(e->h);
+ if(!e->IsPoint()) {
+ for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
+ if(e->point[i].v == 0) break;
+ FindConstraints(e->point[i]);
+ }
+ }
+
+ std::sort(lhc.begin(), lhc.end());
+ lhc.erase(std::unique(lhc.begin(), lhc.end()), lhc.end());
+
+ auto ListConstraints = [&](bool reference) {
+ bool first = true;
+ int a = 0;
+ for(hConstraint hc : lhc) {
+ Constraint *c = SK.GetConstraint(hc);
+ if(c->reference != reference) continue;
+ if(first) {
+ first = false;
+ if(reference) {
+ Printf(true, "%FtMEASURED BY:%E");
+ } else {
+ Printf(true, "%FtCONSTRAINED BY:%E");
+ }
+ }
+ Printf(false, "%Bp %Fl%Ll%D%f%h%s%E",
+ (a & 1) ? 'd' : 'a',
+ c->h.v, (&TextWindow::ScreenSelectConstraint),
+ (&TextWindow::ScreenHoverConstraint),
+ c->DescriptionString().c_str());
+ a++;
+ }
+ };
+ ListConstraints(/*reference=*/false);
+ ListConstraints(/*reference=*/true);
} else if(gs.n == 2 && gs.points == 2) {
Printf(false, "%FtTWO POINTS");
Vector p0 = SK.GetEntity(gs.point[0])->PointGetNum();
Printf(true, " d = %Fi%s", SS.MmToString(d).c_str());
} else if(gs.n == 2 && gs.points == 1 && gs.circlesOrArcs == 1) {
Entity *ec = SK.GetEntity(gs.entity[0]);
- if(ec->type == Entity::CIRCLE) {
+ if(ec->type == Entity::Type::CIRCLE) {
Printf(false, "%FtPOINT AND A CIRCLE");
- } else if(ec->type == Entity::ARC_OF_CIRCLE) {
+ } else if(ec->type == Entity::Type::ARC_OF_CIRCLE) {
Printf(false, "%FtPOINT AND AN ARC");
- } else oops();
+ } else ssassert(false, "Unexpected entity type");
Vector p = SK.GetEntity(gs.point[0])->PointGetNum();
Printf(true, " pt at " PT_AS_STR, COSTR(p));
Vector c = SK.GetEntity(ec->point[0])->PointGetNum();
Printf(false, "%FtLINE SEGMENT AND POINT%E");
Printf(true, " ln thru " PT_AS_STR, COSTR(lp0));
Printf(false, " " PT_AS_STR, COSTR(lp1));
- Vector pp = SK.GetEntity(gs.point[0])->PointGetNum();
+ Entity *p = SK.GetEntity(gs.point[0]);
+ Vector pp = p->PointGetNum();
Printf(true, " point " PT_AS_STR, COSTR(pp));
Printf(true, " pt-ln distance = %Fi%s%E",
SS.MmToString(pp.DistanceToLine(lp0, lp1.Minus(lp0))).c_str());
+ hEntity wrkpl = SS.GW.ActiveWorkplane();
+ if(wrkpl != Entity::FREE_IN_3D && !(p->workplane == wrkpl && ln->workplane == wrkpl)) {
+ Vector ppw = pp.ProjectInto(wrkpl);
+ Vector lp0w = lp0.ProjectInto(wrkpl);
+ Vector lp1w = lp1.ProjectInto(wrkpl);
+ Printf(false, " or distance = %Fi%s%E (in workplane)",
+ SS.MmToString(ppw.DistanceToLine(lp0w, lp1w.Minus(lp0w))).c_str());
+ }
} else if(gs.n == 2 && gs.vectors == 2) {
Printf(false, "%FtTWO VECTORS");
Printf(false, "%FtSELECTED:%E comment text");
} else if(gs.n == 0 && gs.constraints == 1) {
Constraint *c = SK.GetConstraint(gs.constraint[0]);
+ const std::string &desc = c->DescriptionString().c_str();
+
+ if(c->type == Constraint::Type::COMMENT) {
+ Printf(false, "%FtCOMMENT%E %s", desc.c_str());
+ } else if(c->HasLabel()) {
+ if(c->reference) {
+ Printf(false, "%FtREFERENCE%E %s", desc.c_str());
+ } else {
+ Printf(false, "%FtDIMENSION%E %s", desc.c_str());
+ }
+ Printf(true, " %Fd%f%D%Ll%s reference",
+ &ScreenConstraintToggleReference, gs.constraint[0].v,
+ c->reference ? CHECK_TRUE : CHECK_FALSE);
+ if(c->type == Constraint::Type::DIAMETER) {
+ Printf(false, " %Fd%f%D%Ll%s use radius",
+ &ScreenConstraintShowAsRadius, gs.constraint[0].v,
+ c->other ? CHECK_TRUE : CHECK_FALSE);
+ }
+ } else {
+ Printf(false, "%FtCONSTRAINT%E %s", desc.c_str());
+ }
+
+ if(c->IsProjectible()) {
+ if(c->workplane == Entity::FREE_IN_3D) {
+ Printf(true, "%FtNOT PROJECTED TO WORKPLANE%E");
+ } else {
+ Entity *w = SK.GetEntity(c->workplane);
+ if(w->h.isFromRequest()) {
+ Printf(true, "%FtIN WORKPLANE%E %Fl%Ll%D%f%h%s%E",
+ w->h.request().v,
+ (&TextWindow::ScreenSelectRequest), &(TextWindow::ScreenHoverRequest),
+ w->DescriptionString().c_str());
+ } else {
+ Printf(true, "%FtIN WORKPLANE%E %Fl%Ll%D%f%h%s%E",
+ w->h.group().v,
+ (&TextWindow::ScreenSelectGroup), (&TextWindow::ScreenHoverGroupWorkplane),
+ w->DescriptionString().c_str());
+ }
+ }
+ }
- if(c->type == Constraint::DIAMETER) {
- Printf(false, "%FtDIAMETER CONSTRAINT");
+ std::vector<hEntity> lhe = {};
+ lhe.push_back(c->ptA);
+ lhe.push_back(c->ptB);
+ lhe.push_back(c->entityA);
+ lhe.push_back(c->entityB);
+ lhe.push_back(c->entityC);
+ lhe.push_back(c->entityD);
+
+ auto it = std::remove_if(lhe.begin(), lhe.end(), [](hEntity he) {
+ return he == Entity::NO_ENTITY || !he.isFromRequest();
+ });
+ lhe.erase(it, lhe.end());
+
+ if(!lhe.empty()) {
+ if(c->reference) {
+ Printf(true, "%FtMEASURES:%E");
+ } else {
+ Printf(true, "%FtCONSTRAINS:%E");
+ }
- Printf(true, " %Fd%f%D%Ll%s show as radius",
- &ScreenConstraintShowAsRadius, gs.constraint[0].v,
- c->other ? CHECK_TRUE : CHECK_FALSE);
- } else {
- Printf(false, "%FtSELECTED:%E %s",
- c->DescriptionString().c_str());
+ int a = 0;
+ for(hEntity he : lhe) {
+ Entity *e = SK.GetEntity(he);
+ Printf(false, "%Bp %Fl%Ll%D%f%h%s%E",
+ (a & 1) ? 'd' : 'a',
+ e->h.v, (&TextWindow::ScreenSelectEntity),
+ &(TextWindow::ScreenHoverEntity),
+ e->DescriptionString().c_str());
+ a++;
+ }
}
} else {
int n = SS.GW.selection.n;
Printf(false, "%FtSELECTED:%E %d item%s", n, n == 1 ? "" : "s");
}
- if(shown.screen == SCREEN_STYLE_INFO &&
+ if(shown.screen == Screen::STYLE_INFO &&
shown.style.v >= Style::FIRST_CUSTOM && gs.stylables > 0)
{
// If we are showing a screen for a particular style, then offer the
// If any of the selected entities have an assigned style, then offer
// the option to remove that style.
bool styleAssigned = false;
- for(i = 0; i < gs.entities; i++) {
+ for(int i = 0; i < gs.entities; i++) {
Entity *e = SK.GetEntity(gs.entity[i]);
if(e->style.v != 0) {
styleAssigned = true;
}
}
- for(i = 0; i < gs.constraints; i++) {
+ for(int i = 0; i < gs.constraints; i++) {
Constraint *c = SK.GetConstraint(gs.constraint[i]);
- if(c->type == Constraint::COMMENT && c->disp.style.v != 0) {
+ if(c->type == Constraint::Type::COMMENT && c->disp.style.v != 0) {
styleAssigned = true;
}
}
Printf(true, "%Fl%f%Ll(unselect all)%E", &TextWindow::ScreenUnselectAll);
}
-void TextWindow::GoToScreen(int screen) {
+void TextWindow::GoToScreen(Screen screen) {
shown.screen = screen;
}
#include "solvespace.h"
bool GraphicsWindow::Selection::Equals(Selection *b) {
- if(entity.v != b->entity.v) return false;
- if(constraint.v != b->constraint.v) return false;
+ if(entity != b->entity) return false;
+ if(constraint != b->constraint) return false;
return true;
}
-bool GraphicsWindow::Selection::IsEmpty(void) {
+bool GraphicsWindow::Selection::IsEmpty() {
if(entity.v) return false;
if(constraint.v) return false;
return true;
}
-bool GraphicsWindow::Selection::HasEndpoints(void) {
+bool GraphicsWindow::Selection::HasEndpoints() {
if(!entity.v) return false;
Entity *e = SK.GetEntity(entity);
return e->HasEndpoints();
}
-void GraphicsWindow::Selection::Clear(void) {
+void GraphicsWindow::Selection::Clear() {
entity.v = constraint.v = 0;
emphasized = false;
}
-void GraphicsWindow::Selection::Draw(void) {
- Vector refp = Vector::From(0, 0, 0);
+void GraphicsWindow::Selection::Draw(bool isHovered, Canvas *canvas) {
+ const Camera &camera = canvas->GetCamera();
+
+ std::vector<Vector> refs;
if(entity.v) {
Entity *e = SK.GetEntity(entity);
- e->Draw(/*drawAsHidden=*/false);
- if(emphasized) refp = e->GetReferencePos();
+ e->Draw(isHovered ? Entity::DrawAs::HOVERED :
+ Entity::DrawAs::SELECTED,
+ canvas);
+ if(emphasized) {
+ e->GetReferencePoints(&refs);
+ }
}
if(constraint.v) {
Constraint *c = SK.GetConstraint(constraint);
- c->Draw();
- if(emphasized) refp = c->GetReferencePos();
+ c->Draw(isHovered ? Constraint::DrawAs::HOVERED :
+ Constraint::DrawAs::SELECTED,
+ canvas);
+ if(emphasized) {
+ c->GetReferencePoints(camera, &refs);
+ }
}
if(emphasized && (constraint.v || entity.v)) {
// We want to emphasize this constraint or entity, by drawing a thick
- // line from the top left corner of the screen to the reference point
+ // line from the top left corner of the screen to the reference point(s)
// of that entity or constraint.
- double s = 0.501/SS.GW.scale;
- Vector topLeft = SS.GW.projRight.ScaledBy(-SS.GW.width*s);
- topLeft = topLeft.Plus(SS.GW.projUp.ScaledBy(SS.GW.height*s));
- topLeft = topLeft.Minus(SS.GW.offset);
-
- ssglLineWidth(40);
- RgbaColor rgb = Style::Color(Style::HOVERED);
- glColor4d(rgb.redF(), rgb.greenF(), rgb.blueF(), 0.2);
- glBegin(GL_LINES);
- ssglVertex3v(topLeft);
- ssglVertex3v(refp);
- glEnd();
- ssglLineWidth(1);
+ Canvas::Stroke strokeEmphasis = {};
+ strokeEmphasis.layer = Canvas::Layer::FRONT;
+ strokeEmphasis.color = Style::Color(Style::HOVERED).WithAlpha(50);
+ strokeEmphasis.width = 40;
+ strokeEmphasis.unit = Canvas::Unit::PX;
+ Canvas::hStroke hcsEmphasis = canvas->GetStroke(strokeEmphasis);
+
+ Point2d topLeftScreen;
+ topLeftScreen.x = -(double)camera.width / 2;
+ topLeftScreen.y = (double)camera.height / 2;
+ Vector topLeft = camera.UnProjectPoint(topLeftScreen);
+
+ auto it = std::unique(refs.begin(), refs.end(),
+ [](Vector a, Vector b) { return a.Equals(b); });
+ refs.erase(it, refs.end());
+ for(Vector p : refs) {
+ canvas->DrawLine(topLeft, p, hcsEmphasis);
+ }
}
}
-void GraphicsWindow::ClearSelection(void) {
+void GraphicsWindow::ClearSelection() {
selection.Clear();
SS.ScheduleShowTW();
- InvalidateGraphics();
+ Invalidate();
}
-void GraphicsWindow::ClearNonexistentSelectionItems(void) {
+void GraphicsWindow::ClearNonexistentSelectionItems() {
bool change = false;
Selection *s;
selection.ClearTags();
}
}
selection.RemoveTagged();
- if(change) InvalidateGraphics();
+ if(change) Invalidate();
}
//-----------------------------------------------------------------------------
Vector ep = e->PointGetNum();
for(s = selection.First(); s; s = selection.NextAfter(s)) {
if(!s->entity.v) continue;
- if(s->entity.v == stog->entity.v) continue;
+ if(s->entity == stog->entity)
+ continue;
Entity *se = SK.GetEntity(s->entity);
if(!se->IsPoint()) continue;
if(ep.Equals(se->PointGetNum())) {
}
//-----------------------------------------------------------------------------
-// Select everything that lies within the marquee view-aligned rectangle. For
-// points, we test by the point location. For normals, we test by the normal's
-// associated point. For anything else, we test by any piecewise linear edge.
+// Select everything that lies within the marquee view-aligned rectangle.
//-----------------------------------------------------------------------------
-void GraphicsWindow::SelectByMarquee(void) {
- Point2d begin = ProjectPoint(orig.marqueePoint);
- double xmin = min(orig.mouse.x, begin.x),
- xmax = max(orig.mouse.x, begin.x),
- ymin = min(orig.mouse.y, begin.y),
- ymax = max(orig.mouse.y, begin.y);
+void GraphicsWindow::SelectByMarquee() {
+ Point2d marqueePoint = ProjectPoint(orig.marqueePoint);
+ BBox marqueeBBox = BBox::From(Vector::From(marqueePoint.x, marqueePoint.y, -1),
+ Vector::From(orig.mouse.x, orig.mouse.y, 1));
Entity *e;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
- if(e->group.v != SS.GW.activeGroup.v) continue;
+ if(e->group != SS.GW.activeGroup) continue;
if(e->IsFace() || e->IsDistance()) continue;
if(!e->IsVisible()) continue;
- if(e->IsPoint() || e->IsNormal()) {
- Vector p = e->IsPoint() ? e->PointGetNum() :
- SK.GetEntity(e->point[0])->PointGetNum();
- Point2d pp = ProjectPoint(p);
- if(pp.x >= xmin && pp.x <= xmax &&
- pp.y >= ymin && pp.y <= ymax)
- {
- MakeSelected(e->h);
- }
- } else {
- // Use the 3d bounding box test routines, to avoid duplication;
- // so let our bounding square become a bounding box that certainly
- // includes the z = 0 plane.
- Vector ptMin = Vector::From(xmin, ymin, -1),
- ptMax = Vector::From(xmax, ymax, 1);
- SEdgeList sel = {};
- e->GenerateEdges(&sel, true);
- SEdge *se;
- for(se = sel.l.First(); se; se = sel.l.NextAfter(se)) {
- Point2d ppa = ProjectPoint(se->a),
- ppb = ProjectPoint(se->b);
- Vector ptA = Vector::From(ppa.x, ppa.y, 0),
- ptB = Vector::From(ppb.x, ppb.y, 0);
- if(Vector::BoundingBoxIntersectsLine(ptMax, ptMin,
- ptA, ptB, true) ||
- !ptA.OutsideAndNotOn(ptMax, ptMin) ||
- !ptB.OutsideAndNotOn(ptMax, ptMin))
- {
- MakeSelected(e->h);
- break;
- }
- }
- sel.Clear();
+ bool entityHasBBox;
+ BBox entityBBox = e->GetOrGenerateScreenBBox(&entityHasBBox);
+ if(entityHasBBox && entityBBox.Overlaps(marqueeBBox)) {
+ MakeSelected(e->h);
}
}
}
// constraints separately, counts of certain types of entities (circles,
// lines, etc.), and so on.
//-----------------------------------------------------------------------------
-void GraphicsWindow::GroupSelection(void) {
+void GraphicsWindow::GroupSelection() {
gs = {};
int i;
for(i = 0; i < selection.n; i++) {
- Selection *s = &(selection.elem[i]);
+ Selection *s = &(selection[i]);
if(s->entity.v) {
(gs.n)++;
// And some aux counts too
switch(e->type) {
- case Entity::WORKPLANE: (gs.workplanes)++; break;
- case Entity::LINE_SEGMENT: (gs.lineSegments)++; break;
- case Entity::CUBIC: (gs.cubics)++; break;
- case Entity::CUBIC_PERIODIC: (gs.periodicCubics)++; break;
+ case Entity::Type::WORKPLANE: (gs.workplanes)++; break;
+ case Entity::Type::LINE_SEGMENT: (gs.lineSegments)++; break;
+ case Entity::Type::CUBIC: (gs.cubics)++; break;
+ case Entity::Type::CUBIC_PERIODIC: (gs.periodicCubics)++; break;
- case Entity::ARC_OF_CIRCLE:
+ case Entity::Type::ARC_OF_CIRCLE:
(gs.circlesOrArcs)++;
(gs.arcs)++;
break;
- case Entity::CIRCLE: (gs.circlesOrArcs)++; break;
+ case Entity::Type::CIRCLE: (gs.circlesOrArcs)++; break;
+
+ default: break;
}
}
if(s->constraint.v) {
}
}
+Camera GraphicsWindow::GetCamera() const {
+ Camera camera = {};
+ if(window) {
+ window->GetContentSize(&camera.width, &camera.height);
+ camera.pixelRatio = window->GetDevicePixelRatio();
+ camera.gridFit = (window->GetDevicePixelRatio() == 1);
+ } else { // solvespace-cli
+ camera.width = 297.0; // A4? Whatever...
+ camera.height = 210.0;
+ camera.pixelRatio = 1.0;
+ camera.gridFit = camera.pixelRatio == 1.0;
+ }
+ camera.offset = offset;
+ camera.projUp = projUp;
+ camera.projRight = projRight;
+ camera.scale = scale;
+ camera.tangent = SS.CameraTangent();
+ return camera;
+}
+
+Lighting GraphicsWindow::GetLighting() const {
+ Lighting lighting = {};
+ lighting.backgroundColor = SS.backgroundColor;
+ lighting.ambientIntensity = SS.ambientIntensity;
+ lighting.lightIntensity[0] = SS.lightIntensity[0];
+ lighting.lightIntensity[1] = SS.lightIntensity[1];
+ lighting.lightDirection[0] = SS.lightDir[0];
+ lighting.lightDirection[1] = SS.lightDir[1];
+ return lighting;
+}
+
+GraphicsWindow::Selection GraphicsWindow::ChooseFromHoverToSelect() {
+ Selection sel = {};
+ if(hoverList.IsEmpty())
+ return sel;
+
+ Group *activeGroup = SK.GetGroup(SS.GW.activeGroup);
+ int bestOrder = -1;
+ int bestZIndex = 0;
+ double bestDepth = VERY_POSITIVE;
+
+ for(const Hover &hov : hoverList) {
+ hGroup hg = {};
+ if(hov.selection.entity.v != 0) {
+ hg = SK.GetEntity(hov.selection.entity)->group;
+ } else if(hov.selection.constraint.v != 0) {
+ hg = SK.GetConstraint(hov.selection.constraint)->group;
+ }
+
+ Group *g = SK.GetGroup(hg);
+ if(g->order > activeGroup->order) continue;
+ if(bestOrder != -1 && (bestOrder > g->order || bestZIndex > hov.zIndex)) continue;
+ // we have hov.zIndex is >= best and hov.group is >= best (but not > active group)
+ if(hov.depth > bestDepth && bestOrder == g->order && bestZIndex == hov.zIndex) continue;
+ bestOrder = g->order;
+ bestZIndex = hov.zIndex;
+ bestDepth = hov.depth;
+ sel = hov.selection;
+ }
+ return sel;
+}
+
+// This uses the same logic as hovering and static entity selection
+// but ignores points known not to be draggable
+GraphicsWindow::Selection GraphicsWindow::ChooseFromHoverToDrag() {
+ Selection sel = {};
+ if(hoverList.IsEmpty())
+ return sel;
+
+ Group *activeGroup = SK.GetGroup(SS.GW.activeGroup);
+ int bestOrder = -1;
+ int bestZIndex = 0;
+ double bestDepth = VERY_POSITIVE;
+
+ for(const Hover &hov : hoverList) {
+ hGroup hg = {};
+ if(hov.selection.entity.v != 0) {
+ Entity *e = SK.GetEntity(hov.selection.entity);
+ if (!e->CanBeDragged()) continue;
+ hg = e->group;
+ } else if(hov.selection.constraint.v != 0) {
+ hg = SK.GetConstraint(hov.selection.constraint)->group;
+ }
+
+ Group *g = SK.GetGroup(hg);
+ if(g->order > activeGroup->order) continue;
+ if(bestOrder != -1 && (bestOrder > g->order || bestZIndex > hov.zIndex)) continue;
+ // we have hov.zIndex is >= best and hov.group is >= best (but not > active group)
+ if(hov.depth > bestDepth && bestOrder == g->order && bestZIndex == hov.zIndex) continue;
+ bestOrder = g->order;
+ bestZIndex = hov.zIndex;
+ sel = hov.selection;
+ }
+ return sel;
+}
+
void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
- int i;
- double d, dmin = 1e12;
- Selection s = {};
+ hoverList = {};
+ Selection sel = {};
// Did the view projection change? If so, invalidate bounding boxes.
if(!offset.EqualsExactly(cached.offset) ||
}
}
+ ObjectPicker canvas = {};
+ canvas.camera = GetCamera();
+ canvas.selRadius = 10.0;
+ canvas.point = mp;
+ canvas.maxZIndex = -1;
+
// Always do the entities; we might be dragging something that should
// be auto-constrained, and we need the hover for that.
- for(i = 0; i < SK.entity.n; i++) {
- Entity *e = &(SK.entity.elem[i]);
+ for(Entity &e : SK.entity) {
+ if(!e.IsVisible()) continue;
+
+ // If faces aren't selectable, image entities aren't either.
+ if(e.type == Entity::Type::IMAGE && !showFaces) continue;
+
// Don't hover whatever's being dragged.
- if(e->h.request().v == pending.point.request().v) {
+ if(IsFromPending(e.h.request())) {
// The one exception is when we're creating a new cubic; we
// want to be able to hover the first point, because that's
// how we turn it into a periodic spline.
- if(!e->IsPoint()) continue;
- if(!e->h.isFromRequest()) continue;
- Request *r = SK.GetRequest(e->h.request());
- if(r->type != Request::CUBIC) continue;
+ if(!e.IsPoint()) continue;
+ if(!e.h.isFromRequest()) continue;
+ Request *r = SK.GetRequest(e.h.request());
+ if(r->type != Request::Type::CUBIC) continue;
if(r->extraPoints < 2) continue;
- if(e->h.v != r->h.entity(1).v) continue;
+ if(e.h.v != r->h.entity(1).v) continue;
}
- d = e->GetDistance(mp);
- if(d < SELECTION_RADIUS && d < dmin) {
- s = {};
- s.entity = e->h;
- dmin = d;
+ if(canvas.Pick([&]{ e.Draw(Entity::DrawAs::DEFAULT, &canvas); })) {
+ Hover hov = {};
+ hov.distance = canvas.minDistance;
+ hov.zIndex = canvas.maxZIndex;
+ hov.depth = canvas.minDepth;
+ hov.selection.entity = e.h;
+ hoverList.Add(&hov);
}
}
// The constraints and faces happen only when nothing's in progress.
- if(pending.operation == 0) {
+ if(pending.operation == Pending::NONE) {
// Constraints
- for(i = 0; i < SK.constraint.n; i++) {
- d = SK.constraint.elem[i].GetDistance(mp);
- if(d < SELECTION_RADIUS && d < dmin) {
- s = {};
- s.constraint = SK.constraint.elem[i].h;
- dmin = d;
+ for(Constraint &c : SK.constraint) {
+ if(canvas.Pick([&]{ c.Draw(Constraint::DrawAs::DEFAULT, &canvas); })) {
+ Hover hov = {};
+ hov.distance = canvas.minDistance;
+ hov.zIndex = canvas.maxZIndex;
+ hov.selection.constraint = c.h;
+ hoverList.Add(&hov);
}
}
+ }
+
+ std::sort(hoverList.begin(), hoverList.end(),
+ [](const Hover &a, const Hover &b) {
+ if(a.zIndex == b.zIndex) return a.distance < b.distance;
+ return a.zIndex > b.zIndex;
+ });
+ sel = ChooseFromHoverToSelect();
+ if(pending.operation == Pending::NONE) {
// Faces, from the triangle mesh; these are lowest priority
- if(s.constraint.v == 0 && s.entity.v == 0 && showShaded && showFaces) {
+ if(sel.constraint.v == 0 && sel.entity.v == 0 && showShaded && showFaces) {
Group *g = SK.GetGroup(activeGroup);
SMesh *m = &(g->displayMesh);
uint32_t v = m->FirstIntersectionWith(mp);
if(v) {
- s.entity.v = v;
+ sel.entity.v = v;
}
}
}
- if(!s.Equals(&hover)) {
- hover = s;
- PaintGraphics();
+ canvas.Clear();
+
+ if(!sel.Equals(&hover)) {
+ hover = sel;
+ Invalidate();
}
}
return orig;
}
-void GraphicsWindow::NormalizeProjectionVectors(void) {
+void GraphicsWindow::NormalizeProjectionVectors() {
if(projRight.Magnitude() < LENGTH_EPS) {
projRight = Vector::From(1, 0, 0);
}
projRight = projRight.WithMagnitude(1);
}
-Vector GraphicsWindow::VectorFromProjs(Vector rightUpForward) {
- Vector n = projRight.Cross(projUp);
+void GraphicsWindow::DrawSnapGrid(Canvas *canvas) {
+ if(!LockedInWorkplane()) return;
- Vector r = (projRight.ScaledBy(rightUpForward.x));
- r = r.Plus(projUp.ScaledBy(rightUpForward.y));
- r = r.Plus(n.ScaledBy(rightUpForward.z));
- return r;
-}
+ const Camera &camera = canvas->GetCamera();
+ double width = camera.width,
+ height = camera.height;
-void GraphicsWindow::Paint(void) {
- int i;
- havePainted = true;
-
- int w, h;
- GetGraphicsWindowSize(&w, &h);
- width = w; height = h;
- glViewport(0, 0, w, h);
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
-
- glScaled(scale*2.0/w, scale*2.0/h, scale*1.0/30000);
-
- double mat[16];
- // Last thing before display is to apply the perspective
- double clp = SS.CameraTangent()*scale;
- MakeMatrix(mat, 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, clp, 1);
- glMultMatrixd(mat);
- // Before that, we apply the rotation
+ hEntity he = ActiveWorkplane();
+ EntityBase *wrkpl = SK.GetEntity(he),
+ *norm = wrkpl->Normal();
Vector n = projUp.Cross(projRight);
- MakeMatrix(mat, projRight.x, projRight.y, projRight.z, 0,
- projUp.x, projUp.y, projUp.z, 0,
- n.x, n.y, n.z, 0,
- 0, 0, 0, 1);
- glMultMatrixd(mat);
- // And before that, the translation
- MakeMatrix(mat, 1, 0, 0, offset.x,
- 0, 1, 0, offset.y,
- 0, 0, 1, offset.z,
- 0, 0, 0, 1);
- glMultMatrixd(mat);
-
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-
- glShadeModel(GL_SMOOTH);
-
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_BLEND);
- glEnable(GL_LINE_SMOOTH);
- // don't enable GL_POLYGON_SMOOTH; that looks ugly on some graphics cards,
- // drawn with leaks in the mesh
- glEnable(GL_POLYGON_OFFSET_LINE);
- glEnable(GL_POLYGON_OFFSET_FILL);
- glEnable(GL_DEPTH_TEST);
- glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
- glEnable(GL_NORMALIZE);
-
- // At the same depth, we want later lines drawn over earlier.
- glDepthFunc(GL_LEQUAL);
-
- if(SS.ActiveGroupsOkay()) {
- glClearColor(SS.backgroundColor.redF(),
- SS.backgroundColor.greenF(),
- SS.backgroundColor.blueF(), 1.0f);
- } else {
- // Draw a different background whenever we're having solve problems.
- RgbaColor rgb = Style::Color(Style::DRAW_ERROR);
- glClearColor(0.4f*rgb.redF(), 0.4f*rgb.greenF(), 0.4f*rgb.blueF(), 1.0f);
- // And show the text window, which has info to debug it
- ForceTextWindowShown();
+ Vector wu, wv, wn, wp;
+ wp = SK.GetEntity(wrkpl->point[0])->PointGetNum();
+ wu = norm->NormalU();
+ wv = norm->NormalV();
+ wn = norm->NormalN();
+
+ double g = SS.gridSpacing;
+
+ double umin = VERY_POSITIVE, umax = VERY_NEGATIVE,
+ vmin = VERY_POSITIVE, vmax = VERY_NEGATIVE;
+ int a;
+ for(a = 0; a < 4; a++) {
+ // Ideally, we would just do +/- half the width and height; but
+ // allow some extra slop for rounding.
+ Vector horiz = projRight.ScaledBy((0.6*width)/scale + 2*g),
+ vert = projUp. ScaledBy((0.6*height)/scale + 2*g);
+ if(a == 2 || a == 3) horiz = horiz.ScaledBy(-1);
+ if(a == 1 || a == 3) vert = vert. ScaledBy(-1);
+ Vector tp = horiz.Plus(vert).Minus(offset);
+
+ // Project the point into our grid plane, normal to the screen
+ // (not to the grid plane). If the plane is on edge then this is
+ // impossible so don't try to draw the grid.
+ bool parallel;
+ Vector tpp = Vector::AtIntersectionOfPlaneAndLine(
+ wn, wn.Dot(wp),
+ tp, tp.Plus(n),
+ ¶llel);
+ if(parallel) return;
+
+ tpp = tpp.Minus(wp);
+ double uu = tpp.Dot(wu),
+ vv = tpp.Dot(wv);
+
+ umin = min(uu, umin);
+ umax = max(uu, umax);
+ vmin = min(vv, vmin);
+ vmax = max(vv, vmax);
}
- glClear(GL_COLOR_BUFFER_BIT);
- glClearDepth(1.0);
- glClear(GL_DEPTH_BUFFER_BIT);
-
- if(SS.bgImage.fromFile) {
- // If a background image is loaded, then we draw it now as a texture.
- // This handles the resizing for us nicely.
- glBindTexture(GL_TEXTURE_2D, TEXTURE_BACKGROUND_IMG);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
- SS.bgImage.rw, SS.bgImage.rh,
- 0,
- GL_RGB, GL_UNSIGNED_BYTE,
- SS.bgImage.fromFile);
-
- double tw = ((double)SS.bgImage.w) / SS.bgImage.rw,
- th = ((double)SS.bgImage.h) / SS.bgImage.rh;
-
- double mmw = SS.bgImage.w / SS.bgImage.scale,
- mmh = SS.bgImage.h / SS.bgImage.scale;
-
- Vector origin = SS.bgImage.origin;
- origin = origin.DotInToCsys(projRight, projUp, n);
- // Place the depth of our origin at the point that corresponds to
- // w = 1, so that it's unaffected by perspective.
- origin.z = (offset.ScaledBy(-1)).Dot(n);
- origin = origin.ScaleOutOfCsys(projRight, projUp, n);
-
- // Place the background at the very back of the Z order, though, by
- // mucking with the depth range.
- glDepthRange(1, 1);
- glEnable(GL_TEXTURE_2D);
- glBegin(GL_QUADS);
- glTexCoord2d(0, 0);
- ssglVertex3v(origin);
-
- glTexCoord2d(0, th);
- ssglVertex3v(origin.Plus(projUp.ScaledBy(mmh)));
-
- glTexCoord2d(tw, th);
- ssglVertex3v(origin.Plus(projRight.ScaledBy(mmw).Plus(
- projUp. ScaledBy(mmh))));
-
- glTexCoord2d(tw, 0);
- ssglVertex3v(origin.Plus(projRight.ScaledBy(mmw)));
- glEnd();
- glDisable(GL_TEXTURE_2D);
- }
- ssglDepthRangeOffset(0);
- // Nasty case when we're reloading the linked files; could be that
- // we get an error, so a dialog pops up, and a message loop starts, and
- // we have to get called to paint ourselves. If the sketch is screwed
- // up, then we could trigger an oops trying to draw.
- if(!SS.allConsistent) return;
+ int i, j, i0, i1, j0, j1;
- // Let's use two lights, at the user-specified locations
- GLfloat f;
- glEnable(GL_LIGHT0);
- f = (GLfloat)SS.lightIntensity[0];
- GLfloat li0[] = { f, f, f, 1.0f };
- glLightfv(GL_LIGHT0, GL_DIFFUSE, li0);
- glLightfv(GL_LIGHT0, GL_SPECULAR, li0);
-
- glEnable(GL_LIGHT1);
- f = (GLfloat)SS.lightIntensity[1];
- GLfloat li1[] = { f, f, f, 1.0f };
- glLightfv(GL_LIGHT1, GL_DIFFUSE, li1);
- glLightfv(GL_LIGHT1, GL_SPECULAR, li1);
-
- Vector ld;
- ld = VectorFromProjs(SS.lightDir[0]);
- GLfloat ld0[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 };
- glLightfv(GL_LIGHT0, GL_POSITION, ld0);
- ld = VectorFromProjs(SS.lightDir[1]);
- GLfloat ld1[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 };
- glLightfv(GL_LIGHT1, GL_POSITION, ld1);
-
- GLfloat ambient[4] = { (float)SS.ambientIntensity,
- (float)SS.ambientIntensity,
- (float)SS.ambientIntensity, 1 };
- glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
-
- ssglUnlockColor();
-
- if(showSnapGrid && LockedInWorkplane()) {
- hEntity he = ActiveWorkplane();
- EntityBase *wrkpl = SK.GetEntity(he),
- *norm = wrkpl->Normal();
- Vector wu, wv, wn, wp;
- wp = SK.GetEntity(wrkpl->point[0])->PointGetNum();
- wu = norm->NormalU();
- wv = norm->NormalV();
- wn = norm->NormalN();
-
- double g = SS.gridSpacing;
-
- double umin = VERY_POSITIVE, umax = VERY_NEGATIVE,
- vmin = VERY_POSITIVE, vmax = VERY_NEGATIVE;
- int a;
- for(a = 0; a < 4; a++) {
- // Ideally, we would just do +/- half the width and height; but
- // allow some extra slop for rounding.
- Vector horiz = projRight.ScaledBy((0.6*width)/scale + 2*g),
- vert = projUp. ScaledBy((0.6*height)/scale + 2*g);
- if(a == 2 || a == 3) horiz = horiz.ScaledBy(-1);
- if(a == 1 || a == 3) vert = vert. ScaledBy(-1);
- Vector tp = horiz.Plus(vert).Minus(offset);
-
- // Project the point into our grid plane, normal to the screen
- // (not to the grid plane). If the plane is on edge then this is
- // impossible so don't try to draw the grid.
- bool parallel;
- Vector tpp = Vector::AtIntersectionOfPlaneAndLine(
- wn, wn.Dot(wp),
- tp, tp.Plus(n),
- ¶llel);
- if(parallel) goto nogrid;
-
- tpp = tpp.Minus(wp);
- double uu = tpp.Dot(wu),
- vv = tpp.Dot(wv);
-
- umin = min(uu, umin);
- umax = max(uu, umax);
- vmin = min(vv, vmin);
- vmax = max(vv, vmax);
- }
+ i0 = (int)(umin / g);
+ i1 = (int)(umax / g);
+ j0 = (int)(vmin / g);
+ j1 = (int)(vmax / g);
- int i, j, i0, i1, j0, j1;
+ if(i0 > i1 || i1 - i0 > 400) return;
+ if(j0 > j1 || j1 - j0 > 400) return;
- i0 = (int)(umin / g);
- i1 = (int)(umax / g);
- j0 = (int)(vmin / g);
- j1 = (int)(vmax / g);
+ Canvas::Stroke stroke = {};
+ stroke.layer = Canvas::Layer::BACK;
+ stroke.color = Style::Color(Style::DATUM).WithAlpha(75);
+ stroke.unit = Canvas::Unit::PX;
+ stroke.width = 1.0f;
+ Canvas::hStroke hcs = canvas->GetStroke(stroke);
- if(i0 > i1 || i1 - i0 > 400) goto nogrid;
- if(j0 > j1 || j1 - j0 > 400) goto nogrid;
+ for(i = i0 + 1; i < i1; i++) {
+ canvas->DrawLine(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j0*g)),
+ wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j1*g)),
+ hcs);
+ }
+ for(j = j0 + 1; j < j1; j++) {
+ canvas->DrawLine(wp.Plus(wu.ScaledBy(i0*g)).Plus(wv.ScaledBy(j*g)),
+ wp.Plus(wu.ScaledBy(i1*g)).Plus(wv.ScaledBy(j*g)),
+ hcs);
+ }
+}
- ssglLineWidth(1);
- ssglColorRGBa(Style::Color(Style::DATUM), 0.3);
- glBegin(GL_LINES);
- for(i = i0 + 1; i < i1; i++) {
- ssglVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j0*g)));
- ssglVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j1*g)));
- }
- for(j = j0 + 1; j < j1; j++) {
- ssglVertex3v(wp.Plus(wu.ScaledBy(i0*g)).Plus(wv.ScaledBy(j*g)));
- ssglVertex3v(wp.Plus(wu.ScaledBy(i1*g)).Plus(wv.ScaledBy(j*g)));
+void GraphicsWindow::DrawEntities(Canvas *canvas, bool persistent) {
+ for(Entity &e : SK.entity) {
+ if(persistent == (e.IsNormal() || e.IsWorkplane())) continue;
+ switch(SS.GW.drawOccludedAs) {
+ case DrawOccludedAs::VISIBLE:
+ e.Draw(Entity::DrawAs::OVERLAY, canvas);
+ break;
+
+ case DrawOccludedAs::STIPPLED:
+ e.Draw(Entity::DrawAs::HIDDEN, canvas);
+ /* fallthrough */
+ case DrawOccludedAs::INVISIBLE:
+ e.Draw(Entity::DrawAs::DEFAULT, canvas);
+ break;
}
- glEnd();
-
- // Clear the depth buffer, so that the grid is at the very back of
- // the Z order.
- glClear(GL_DEPTH_BUFFER_BIT);
-nogrid:;
}
+}
+void GraphicsWindow::DrawPersistent(Canvas *canvas) {
// Draw the active group; this does stuff like the mesh and edges.
- (SK.GetGroup(activeGroup))->Draw();
+ SK.GetGroup(activeGroup)->Draw(canvas);
- // Now draw the entities.
- if(SS.GW.showHdnLines) {
- ssglDepthRangeOffset(2);
- glDepthFunc(GL_GREATER);
- Entity::DrawAll(/*drawAsHidden=*/true);
- glDepthFunc(GL_LEQUAL);
- }
- ssglDepthRangeOffset(0);
- Entity::DrawAll(/*drawAsHidden=*/false);
+ // Now draw the entities that don't change with viewport.
+ DrawEntities(canvas, /*persistent=*/true);
// Draw filled paths in all groups, when those filled paths were requested
// specially by assigning a style with a fill color, or when the filled
// paths are just being filled by default. This should go last, to make
// the transparency work.
- for(i = 0; i < SK.groupOrder.n; i++) {
- Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
+ for(hGroup hg : SK.groupOrder) {
+ Group *g = SK.GetGroup(hg);
if(!(g->IsVisible())) continue;
- g->DrawFilledPaths();
+ g->DrawFilledPaths(canvas);
}
+}
+void GraphicsWindow::Draw(Canvas *canvas) {
+ const Camera &camera = canvas->GetCamera();
+
+ // Nasty case when we're reloading the linked files; could be that
+ // we get an error, so a dialog pops up, and a message loop starts, and
+ // we have to get called to paint ourselves. If the sketch is screwed
+ // up, then we could trigger an oops trying to draw.
+ if(!SS.allConsistent) return;
+
+ if(showSnapGrid) DrawSnapGrid(canvas);
+
+ // Draw all the things that don't change when we rotate.
+ if(persistentCanvas != NULL) {
+ if(persistentDirty) {
+ persistentDirty = false;
+
+ persistentCanvas->Clear();
+ DrawPersistent(&*persistentCanvas);
+ persistentCanvas->Finalize();
+ }
+
+ persistentCanvas->Draw();
+ } else {
+ DrawPersistent(canvas);
+ }
+
+ // Draw the entities that do change with viewport.
+ DrawEntities(canvas, /*persistent=*/false);
+
+ // Draw the polygon errors.
+ if(SS.checkClosedContour) {
+ SK.GetGroup(activeGroup)->DrawPolyError(canvas);
+ }
- glDisable(GL_DEPTH_TEST);
// Draw the constraints
- for(i = 0; i < SK.constraint.n; i++) {
- SK.constraint.elem[i].Draw();
+ for(Constraint &c : SK.constraint) {
+ c.Draw(Constraint::DrawAs::DEFAULT, canvas);
}
- // Draw the "pending" constraint, i.e. a constraint that would be
- // placed on a line that is almost horizontal or vertical
- if(SS.GW.pending.operation == DRAGGING_NEW_LINE_POINT) {
- if(SS.GW.pending.suggestion != GraphicsWindow::SUGGESTED_NONE) {
- Constraint c = {};
- c.group = SS.GW.activeGroup;
- c.workplane = SS.GW.ActiveWorkplane();
- c.type = SS.GW.pending.suggestion;
- c.ptA = Entity::NO_ENTITY;
- c.ptB = Entity::NO_ENTITY;
- c.entityA = SS.GW.pending.request.entity(0);
- c.entityB = Entity::NO_ENTITY;
- c.other = false;
- c.other2 = false;
- // Only draw.
- c.Draw();
+ // Draw areas
+ if(SS.showContourAreas) {
+ for(hGroup hg : SK.groupOrder) {
+ Group *g = SK.GetGroup(hg);
+ if(g->h != activeGroup) continue;
+ if(!(g->IsVisible())) continue;
+ g->DrawContourAreaLabels(canvas);
}
}
- // Draw the traced path, if one exists
- ssglLineWidth(Style::Width(Style::ANALYZE));
- ssglColorRGB(Style::Color(Style::ANALYZE));
- SContour *sc = &(SS.traced.path);
- glBegin(GL_LINE_STRIP);
- for(i = 0; i < sc->l.n; i++) {
- ssglVertex3v(sc->l.elem[i].p);
+ // Draw the "pending" constraint, i.e. a constraint that would be
+ // placed on a line that is almost horizontal or vertical.
+ if(SS.GW.pending.operation == Pending::DRAGGING_NEW_LINE_POINT &&
+ SS.GW.pending.hasSuggestion) {
+ Constraint c = {};
+ c.group = SS.GW.activeGroup;
+ c.workplane = SS.GW.ActiveWorkplane();
+ c.type = SS.GW.pending.suggestion;
+ c.entityA = SS.GW.pending.request.entity(0);
+ c.Draw(Constraint::DrawAs::DEFAULT, canvas);
}
- glEnd();
+
+ Canvas::Stroke strokeAnalyze = Style::Stroke(Style::ANALYZE);
+ strokeAnalyze.layer = Canvas::Layer::FRONT;
+ Canvas::hStroke hcsAnalyze = canvas->GetStroke(strokeAnalyze);
+
+ // Draw the traced path, if one exists
+ SEdgeList tracedEdges = {};
+ SS.traced.path.MakeEdgesInto(&tracedEdges);
+ canvas->DrawEdges(tracedEdges, hcsAnalyze);
+ tracedEdges.Clear();
+
+ Canvas::Stroke strokeError = Style::Stroke(Style::DRAW_ERROR);
+ strokeError.layer = Canvas::Layer::FRONT;
+ strokeError.width = 12;
+ Canvas::hStroke hcsError = canvas->GetStroke(strokeError);
// And the naked edges, if the user did Analyze -> Show Naked Edges.
- ssglDrawEdges(&(SS.nakedEdges), true, { Style::DRAW_ERROR });
+ canvas->DrawEdges(SS.nakedEdges, hcsError);
// Then redraw whatever the mouse is hovering over, highlighted.
- glDisable(GL_DEPTH_TEST);
- ssglLockColorTo(Style::Color(Style::HOVERED));
- hover.Draw();
+ hover.Draw(/*isHovered=*/true, canvas);
+ SK.GetGroup(activeGroup)->DrawMesh(Group::DrawMeshAs::HOVERED, canvas);
// And finally draw the selection, same mechanism.
- ssglLockColorTo(Style::Color(Style::SELECTED));
for(Selection *s = selection.First(); s; s = selection.NextAfter(s)) {
- s->Draw();
+ s->Draw(/*isHovered=*/false, canvas);
}
+ SK.GetGroup(activeGroup)->DrawMesh(Group::DrawMeshAs::SELECTED, canvas);
- ssglUnlockColor();
-
- // If a marquee selection is in progress, then draw the selection
- // rectangle, as an outline and a transparent fill.
- if(pending.operation == DRAGGING_MARQUEE) {
- Point2d begin = ProjectPoint(orig.marqueePoint);
- double xmin = min(orig.mouse.x, begin.x),
- xmax = max(orig.mouse.x, begin.x),
- ymin = min(orig.mouse.y, begin.y),
- ymax = max(orig.mouse.y, begin.y);
-
- Vector tl = UnProjectPoint(Point2d::From(xmin, ymin)),
- tr = UnProjectPoint(Point2d::From(xmax, ymin)),
- br = UnProjectPoint(Point2d::From(xmax, ymax)),
- bl = UnProjectPoint(Point2d::From(xmin, ymax));
-
- ssglLineWidth((GLfloat)1.3);
- ssglColorRGB(Style::Color(Style::HOVERED));
- glBegin(GL_LINE_LOOP);
- ssglVertex3v(tl);
- ssglVertex3v(tr);
- ssglVertex3v(br);
- ssglVertex3v(bl);
- glEnd();
- ssglColorRGBa(Style::Color(Style::HOVERED), 0.10);
- glBegin(GL_QUADS);
- ssglVertex3v(tl);
- ssglVertex3v(tr);
- ssglVertex3v(br);
- ssglVertex3v(bl);
- glEnd();
- }
+ Canvas::Stroke strokeDatum = Style::Stroke(Style::DATUM);
+ strokeDatum.unit = Canvas::Unit::PX;
+ strokeDatum.layer = Canvas::Layer::FRONT;
+ strokeDatum.width = 1;
+ Canvas::hStroke hcsDatum = canvas->GetStroke(strokeDatum);
// An extra line, used to indicate the origin when rotating within the
// plane of the monitor.
if(SS.extraLine.draw) {
- ssglLineWidth(1);
- ssglLockColorTo(Style::Color(Style::DATUM));
- glBegin(GL_LINES);
- ssglVertex3v(SS.extraLine.ptA);
- ssglVertex3v(SS.extraLine.ptB);
- glEnd();
+ canvas->DrawLine(SS.extraLine.ptA, SS.extraLine.ptB, hcsDatum);
+ }
+
+ if(SS.centerOfMass.draw && !SS.centerOfMass.dirty) {
+ Vector p = SS.centerOfMass.position;
+ Vector u = camera.projRight;
+ Vector v = camera.projUp;
+
+ const double size = 10.0;
+ const int subdiv = 16;
+ double h = Style::DefaultTextHeight() / camera.scale;
+ std::string s =
+ SS.MmToStringSI(p.x) + ", " +
+ SS.MmToStringSI(p.y) + ", " +
+ SS.MmToStringSI(p.z);
+ canvas->DrawVectorText(s.c_str(), h,
+ p.Plus(u.ScaledBy((size + 5.0)/scale)).Minus(v.ScaledBy(h / 2.0)),
+ u, v, hcsDatum);
+ u = u.WithMagnitude(size / scale);
+ v = v.WithMagnitude(size / scale);
+
+ canvas->DrawLine(p.Minus(u), p.Plus(u), hcsDatum);
+ canvas->DrawLine(p.Minus(v), p.Plus(v), hcsDatum);
+ Vector prev;
+ for(int i = 0; i <= subdiv; i++) {
+ double a = (double)i / subdiv * 2.0 * PI;
+ Vector point = p.Plus(u.ScaledBy(cos(a))).Plus(v.ScaledBy(sin(a)));
+ if(i > 0) {
+ canvas->DrawLine(point, prev, hcsDatum);
+ }
+ prev = point;
+ }
}
// A note to indicate the origin in the just-exported file.
u = SS.justExportedInfo.u,
v = SS.justExportedInfo.v;
} else {
- p = SS.GW.offset.ScaledBy(-1);
- u = SS.GW.projRight;
- v = SS.GW.projUp;
+ p = camera.offset.ScaledBy(-1);
+ u = camera.projRight;
+ v = camera.projUp;
}
+ canvas->DrawVectorText("previewing exported geometry; press Esc to return",
+ Style::DefaultTextHeight() / camera.scale,
+ p.Plus(u.ScaledBy(10/scale)).Plus(v.ScaledBy(10/scale)), u, v,
+ hcsDatum);
- ssglColorRGB(Style::Color(Style::DATUM));
+ if(SS.justExportedInfo.showOrigin) {
+ Vector um = p.Plus(u.WithMagnitude(-15/scale)),
+ up = p.Plus(u.WithMagnitude(30/scale)),
+ vm = p.Plus(v.WithMagnitude(-15/scale)),
+ vp = p.Plus(v.WithMagnitude(30/scale));
+ canvas->DrawLine(um, up, hcsDatum);
+ canvas->DrawLine(vm, vp, hcsDatum);
+ canvas->DrawVectorText("(x, y) = (0, 0) for file just exported",
+ Style::DefaultTextHeight() / camera.scale,
+ p.Plus(u.ScaledBy(40/scale)).Plus(
+ v.ScaledBy(-(Style::DefaultTextHeight())/scale)), u, v,
+ hcsDatum);
+ }
+ }
+}
- ssglWriteText("previewing exported geometry; press Esc to return",
- Style::DefaultTextHeight(),
- p.Plus(u.ScaledBy(10/scale)).Plus(v.ScaledBy(10/scale)),
- u, v, NULL, NULL);
+void GraphicsWindow::Paint() {
+ ssassert(window != NULL && canvas != NULL,
+ "Cannot paint without window and canvas");
- if(SS.justExportedInfo.showOrigin) {
- ssglLineWidth(1.5);
- glBegin(GL_LINES);
- ssglVertex3v(p.Plus(u.WithMagnitude(-15/scale)));
- ssglVertex3v(p.Plus(u.WithMagnitude(30/scale)));
- ssglVertex3v(p.Plus(v.WithMagnitude(-15/scale)));
- ssglVertex3v(p.Plus(v.WithMagnitude(30/scale)));
- glEnd();
-
- ssglWriteText("(x, y) = (0, 0) for file just exported",
- Style::DefaultTextHeight(),
- p.Plus(u.ScaledBy(40/scale)).Plus(
- v.ScaledBy(-(Style::DefaultTextHeight())/scale)),
- u, v, NULL, NULL);
+ havePainted = true;
+
+ Camera camera = GetCamera();
+ Lighting lighting = GetLighting();
+
+ if(!SS.ActiveGroupsOkay()) {
+ // Draw a different background whenever we're having solve problems.
+ RgbaColor bgColor = Style::Color(Style::DRAW_ERROR);
+ bgColor = RgbaColor::FromFloat(0.4f*bgColor.redF(),
+ 0.4f*bgColor.greenF(),
+ 0.4f*bgColor.blueF());
+ lighting.backgroundColor = bgColor;
+ // And show the text window, which has info to debug it
+ ForceTextWindowShown();
+ }
+
+ canvas->SetLighting(lighting);
+ canvas->SetCamera(camera);
+ canvas->StartFrame();
+
+ // Draw the 3d objects.
+ Draw(canvas.get());
+ canvas->FlushFrame();
+
+ // Draw the 2d UI overlay.
+ camera.LoadIdentity();
+ camera.offset.x = -(double)camera.width / 2.0;
+ camera.offset.y = -(double)camera.height / 2.0;
+ canvas->SetCamera(camera);
+
+ UiCanvas uiCanvas = {};
+ uiCanvas.canvas = canvas;
+
+ // If a marquee selection is in progress, then draw the selection
+ // rectangle, as an outline and a transparent fill.
+ if(pending.operation == Pending::DRAGGING_MARQUEE) {
+ Point2d begin = ProjectPoint(orig.marqueePoint);
+ uiCanvas.DrawRect((int)orig.mouse.x + (int)camera.width / 2,
+ (int)begin.x + (int)camera.width / 2,
+ (int)orig.mouse.y + (int)camera.height / 2,
+ (int)begin.y + (int)camera.height / 2,
+ /*fillColor=*/Style::Color(Style::HOVERED).WithAlpha(25),
+ /*outlineColor=*/Style::Color(Style::HOVERED));
+ }
+
+ // If we've had a screenshot requested, take it now, before the UI is overlaid.
+ if(!SS.screenshotFile.IsEmpty()) {
+ FILE *f = OpenFile(SS.screenshotFile, "wb");
+ if(!f || !canvas->ReadFrame()->WritePng(f, /*flip=*/true)) {
+ Error("Couldn't write to '%s'", SS.screenshotFile.raw.c_str());
}
+ if(f) fclose(f);
+ SS.screenshotFile.Clear();
}
// And finally the toolbar.
if(SS.showToolbar) {
- ToolbarDraw();
+ canvas->SetCamera(camera);
+ ToolbarDraw(&uiCanvas);
}
+
+ canvas->FlushFrame();
+ canvas->FinishFrame();
+ canvas->Clear();
}
+void GraphicsWindow::Invalidate(bool clearPersistent) {
+ if(window) {
+ if(clearPersistent) {
+ persistentDirty = true;
+ }
+ window->Invalidate();
+ }
+}
//-----------------------------------------------------------------------------
#include "solvespace.h"
-void Constraint::LineDrawOrGetDistance(Vector a, Vector b) {
- if(dogd.drawing) {
- hStyle hs = GetStyle();
-
- if(dogd.sel) {
- dogd.sel->AddEdge(a, b, hs.v);
- } else {
- if(Style::Width(hs) >= 3.0) {
- ssglFatLine(a, b, Style::Width(hs) / SS.GW.scale);
- } else {
- glBegin(GL_LINE_STRIP);
- ssglVertex3v(a);
- ssglVertex3v(b);
- glEnd();
- }
- }
- } else {
- Point2d ap = SS.GW.ProjectPoint(a);
- Point2d bp = SS.GW.ProjectPoint(b);
-
- double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true);
- dogd.dmin = min(dogd.dmin, d);
- }
- dogd.refp = (a.Plus(b)).ScaledBy(0.5);
-}
-
-static void LineCallback(void *fndata, Vector a, Vector b)
-{
- Constraint *c = (Constraint *)fndata;
- c->LineDrawOrGetDistance(a, b);
-}
-
-std::string Constraint::Label(void) {
+std::string Constraint::Label() const {
std::string result;
- if(type == ANGLE) {
- if(valA == floor(valA)) {
- result = ssprintf("%.0f°", valA);
- } else {
- result = ssprintf("%.2f°", valA);
- }
- } else if(type == LENGTH_RATIO) {
+ if(type == Type::ANGLE) {
+ result = SS.DegreeToString(valA) + "°";
+ } else if(type == Type::LENGTH_RATIO) {
result = ssprintf("%.3f:1", valA);
- } else if(type == COMMENT) {
+ } else if(type == Type::COMMENT) {
result = comment;
- } else if(type == DIAMETER) {
+ } else if(type == Type::DIAMETER) {
if(!other) {
- result = "⌀" + SS.MmToString(valA);
+ result = "⌀" + SS.MmToStringSI(valA);
} else {
- result = "R" + SS.MmToString(valA / 2);
+ result = "R" + SS.MmToStringSI(valA / 2);
}
} else {
// valA has units of distance
- result = SS.MmToString(fabs(valA));
+ result = SS.MmToStringSI(fabs(valA));
}
if(reference) {
result += " REF";
return result;
}
-void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) {
- hStyle hs = GetStyle();
- double th = Style::TextHeight(hs);
+void Constraint::DoLine(Canvas *canvas, Canvas::hStroke hcs, Vector a, Vector b) {
+ const Camera &camera = canvas->GetCamera();
+
+ a = camera.AlignToPixelGrid(a);
+ b = camera.AlignToPixelGrid(b);
+ canvas->DrawLine(a, b, hcs);
+}
+
+void Constraint::DoStippledLine(Canvas *canvas, Canvas::hStroke hcs, Vector a, Vector b) {
+ Canvas::Stroke strokeStippled = *canvas->strokes.FindById(hcs);
+ strokeStippled.stipplePattern = StipplePattern::SHORT_DASH;
+ strokeStippled.stippleScale = 4.0;
+ Canvas::hStroke hcsStippled = canvas->GetStroke(strokeStippled);
+ DoLine(canvas, hcsStippled, a, b);
+}
+
+void Constraint::DoLabel(Canvas *canvas, Canvas::hStroke hcs,
+ Vector ref, Vector *labelPos, Vector gr, Vector gu) {
+ const Camera &camera = canvas->GetCamera();
std::string s = Label();
- double swidth = ssglStrWidth(s, th),
- sheight = ssglStrCapHeight(th);
+ double textHeight = Style::TextHeight(GetStyle()) / camera.scale;
+ double swidth = VectorFont::Builtin()->GetWidth(textHeight, s),
+ sheight = VectorFont::Builtin()->GetCapHeight(textHeight);
// By default, the reference is from the center; but the style could
// specify otherwise if one is present, and it could also specify a
// rotation.
- if(type == COMMENT && disp.style.v) {
+ if(type == Type::COMMENT && disp.style.v) {
Style *st = Style::Get(disp.style);
// rotation first
double rads = st->textAngle*PI/180;
gr = pr.ScaledBy( c).Plus(pu.ScaledBy(s));
gu = pr.ScaledBy(-s).Plus(pu.ScaledBy(c));
// then origin
- int o = st->textOrigin;
- if(o & Style::ORIGIN_LEFT) ref = ref.Plus(gr.WithMagnitude(swidth/2));
- if(o & Style::ORIGIN_RIGHT) ref = ref.Minus(gr.WithMagnitude(swidth/2));
- if(o & Style::ORIGIN_BOT) ref = ref.Plus(gu.WithMagnitude(sheight/2));
- if(o & Style::ORIGIN_TOP) ref = ref.Minus(gu.WithMagnitude(sheight/2));
+ uint32_t o = (uint32_t)st->textOrigin;
+ if(o & (uint32_t)Style::TextOrigin::LEFT) ref = ref.Plus(gr.WithMagnitude(swidth/2));
+ if(o & (uint32_t)Style::TextOrigin::RIGHT) ref = ref.Minus(gr.WithMagnitude(swidth/2));
+ if(o & (uint32_t)Style::TextOrigin::BOT) ref = ref.Plus(gu.WithMagnitude(sheight/2));
+ if(o & (uint32_t)Style::TextOrigin::TOP) ref = ref.Minus(gu.WithMagnitude(sheight/2));
}
- if(labelPos) {
- // labelPos is from the top left corner (for the text box used to
- // edit things), but ref is from the center.
- *labelPos = ref.Minus(gr.WithMagnitude(swidth/2)).Minus(
- gu.WithMagnitude(sheight/2));
- }
-
-
- if(dogd.drawing) {
- ssglWriteTextRefCenter(s, th, ref, gr, gu, LineCallback, this);
- } else {
- double l = swidth/2 - sheight/2;
- l = max(l, 5/SS.GW.scale);
- Point2d a = SS.GW.ProjectPoint(ref.Minus(gr.WithMagnitude(l)));
- Point2d b = SS.GW.ProjectPoint(ref.Plus (gr.WithMagnitude(l)));
- double d = dogd.mp.DistanceToLine(a, b.Minus(a), true);
-
- dogd.dmin = min(dogd.dmin, d - (th / 2));
- dogd.refp = ref;
- }
+ Vector o = ref.Minus(gr.WithMagnitude(swidth/2)).Minus(
+ gu.WithMagnitude(sheight/2));
+ canvas->DrawVectorText(s, textHeight, o, gr.WithMagnitude(1), gu.WithMagnitude(1), hcs);
+ if(labelPos) *labelPos = o;
}
-void Constraint::StippledLine(Vector a, Vector b) {
- glLineStipple(4, 0x5555);
- glEnable(GL_LINE_STIPPLE);
- LineDrawOrGetDistance(a, b);
- glDisable(GL_LINE_STIPPLE);
+void Constraint::DoProjectedPoint(Canvas *canvas, Canvas::hStroke hcs,
+ Vector *r, Vector n, Vector o) {
+ double d = r->DistanceToPlane(n, o);
+ Vector p = r->Minus(n.ScaledBy(d));
+ DoStippledLine(canvas, hcs, p, *r);
+ *r = p;
}
-void Constraint::DoProjectedPoint(Vector *r) {
+void Constraint::DoProjectedPoint(Canvas *canvas, Canvas::hStroke hcs, Vector *r) {
Vector p = r->ProjectInto(workplane);
- StippledLine(p, *r);
+ DoStippledLine(canvas, hcs, p, *r);
*r = p;
}
// the line to almost meet the box, and return either positive or negative,
// depending whether that extension was from A or from B.
//-----------------------------------------------------------------------------
-int Constraint::DoLineTrimmedAgainstBox(Vector ref, Vector a, Vector b, bool extend) {
- hStyle hs = GetStyle();
- double th = Style::TextHeight(hs);
- Vector gu = SS.GW.projUp.WithMagnitude(1),
- gr = SS.GW.projRight.WithMagnitude(1);
-
- double pixels = 1.0 / SS.GW.scale;
- std::string s = Label();
- double swidth = ssglStrWidth(s, th) + 4*pixels,
- sheight = ssglStrCapHeight(th) + 8*pixels;
-
- return DoLineTrimmedAgainstBox(ref, a, b, extend, gr, gu, swidth, sheight);
+int Constraint::DoLineTrimmedAgainstBox(Canvas *canvas, Canvas::hStroke hcs,
+ Vector ref, Vector a, Vector b, bool extend) {
+ const Camera &camera = canvas->GetCamera();
+ double th = Style::TextHeight(GetStyle()) / camera.scale;
+ double pixels = 1.0 / camera.scale;
+ double swidth = VectorFont::Builtin()->GetWidth(th, Label()) + 8 * pixels,
+ sheight = VectorFont::Builtin()->GetCapHeight(th) + 8 * pixels;
+ Vector gu = camera.projUp.WithMagnitude(1),
+ gr = camera.projRight.WithMagnitude(1);
+ return DoLineTrimmedAgainstBox(canvas, hcs, ref, a, b, extend, gr, gu, swidth, sheight);
}
-int Constraint::DoLineTrimmedAgainstBox(Vector ref, Vector a, Vector b, bool extend,
+int Constraint::DoLineTrimmedAgainstBox(Canvas *canvas, Canvas::hStroke hcs,
+ Vector ref, Vector a, Vector b, bool extend,
Vector gr, Vector gu, double swidth, double sheight) {
struct {
Vector n;
}
if(j < 4) continue;
- double t = (p.Minus(a)).DivPivoting(dl);
+ double t = (p.Minus(a)).DivProjected(dl);
tmin = min(t, tmin);
tmax = max(t, tmax);
}
// Both in range; so there's pieces of the line on both sides of the label box.
if(tmin >= 0.0 && tmin <= 1.0 && tmax >= 0.0 && tmax <= 1.0) {
- LineDrawOrGetDistance(a, a.Plus(dl.ScaledBy(tmin)));
- LineDrawOrGetDistance(a.Plus(dl.ScaledBy(tmax)), b);
+ DoLine(canvas, hcs, a, a.Plus(dl.ScaledBy(tmin)));
+ DoLine(canvas, hcs, a.Plus(dl.ScaledBy(tmax)), b);
return 0;
}
// Only one intersection in range; so the box is right on top of the endpoint
if(tmin >= 0.0 && tmin <= 1.0) {
- LineDrawOrGetDistance(a, a.Plus(dl.ScaledBy(tmin)));
+ DoLine(canvas, hcs, a, a.Plus(dl.ScaledBy(tmin)));
return 0;
}
// Likewise.
if(tmax >= 0.0 && tmax <= 1.0) {
- LineDrawOrGetDistance(a.Plus(dl.ScaledBy(tmax)), b);
+ DoLine(canvas, hcs, a.Plus(dl.ScaledBy(tmax)), b);
return 0;
}
// and closer to b, positive means outside and closer to a.
if(tmax < 0.0) {
if(extend) a = a.Plus(dl.ScaledBy(tmax));
- LineDrawOrGetDistance(a, b);
+ DoLine(canvas, hcs, a, b);
return 1;
}
if(tmin > 1.0) {
if(extend) b = a.Plus(dl.ScaledBy(tmin));
- LineDrawOrGetDistance(a, b);
+ DoLine(canvas, hcs, a, b);
return -1;
}
return 0;
}
-void Constraint::DoArrow(Vector p, Vector dir, Vector n, double width, double angle, double da) {
+void Constraint::DoArrow(Canvas *canvas, Canvas::hStroke hcs,
+ Vector p, Vector dir, Vector n, double width, double angle, double da) {
dir = dir.WithMagnitude(width / cos(angle));
dir = dir.RotatedAbout(n, da);
- LineDrawOrGetDistance(p, p.Plus(dir.RotatedAbout(n, angle)));
- LineDrawOrGetDistance(p, p.Plus(dir.RotatedAbout(n, -angle)));
+ DoLine(canvas, hcs, p, p.Plus(dir.RotatedAbout(n, angle)));
+ DoLine(canvas, hcs, p, p.Plus(dir.RotatedAbout(n, -angle)));
}
//-----------------------------------------------------------------------------
// to the line AB, until the line between the extensions crosses ref (the
// center of the label).
//-----------------------------------------------------------------------------
-void Constraint::DoLineWithArrows(Vector ref, Vector a, Vector b,
+void Constraint::DoLineWithArrows(Canvas *canvas, Canvas::hStroke hcs,
+ Vector ref, Vector a, Vector b,
bool onlyOneExt)
{
- double pixels = 1.0 / SS.GW.scale;
+ const Camera &camera = canvas->GetCamera();
+ double pixels = 1.0 / camera.scale;
Vector ab = a.Minus(b);
Vector ar = a.Minus(ref);
// Extension lines extend 10 pixels beyond where the arrows get
// drawn (which is at the same offset perpendicular from AB as the
// label).
- LineDrawOrGetDistance(a, ae.Plus(out.WithMagnitude(10*pixels)));
+ DoLine(canvas, hcs, a, ae.Plus(out.WithMagnitude(10*pixels)));
if(!onlyOneExt) {
- LineDrawOrGetDistance(b, be.Plus(out.WithMagnitude(10*pixels)));
+ DoLine(canvas, hcs, b, be.Plus(out.WithMagnitude(10*pixels)));
}
- int within = DoLineTrimmedAgainstBox(ref, ae, be);
+ int within = DoLineTrimmedAgainstBox(canvas, hcs, ref, ae, be);
// Arrow heads are 13 pixels long, with an 18 degree half-angle.
double theta = 18*PI/180;
if(within != 0) {
arrow = arrow.ScaledBy(-1);
Vector seg = (be.Minus(ae)).WithMagnitude(18*pixels);
- if(within < 0) LineDrawOrGetDistance(ae, ae.Minus(seg));
- if(within > 0) LineDrawOrGetDistance(be, be.Plus(seg));
+ if(within < 0) DoLine(canvas, hcs, ae, ae.Minus(seg));
+ if(within > 0) DoLine(canvas, hcs, be, be.Plus(seg));
}
- DoArrow(ae, arrow, n, 13.0 * pixels, theta, 0.0);
- DoArrow(be, arrow.Negated(), n, 13.0 * pixels, theta, 0.0);
+ DoArrow(canvas, hcs, ae, arrow, n, 13.0 * pixels, theta, 0.0);
+ DoArrow(canvas, hcs, be, arrow.Negated(), n, 13.0 * pixels, theta, 0.0);
}
-void Constraint::DoEqualLenTicks(Vector a, Vector b, Vector gn) {
+void Constraint::DoEqualLenTicks(Canvas *canvas, Canvas::hStroke hcs,
+ Vector a, Vector b, Vector gn, Vector *refp) {
+ const Camera &camera = canvas->GetCamera();
+
Vector m = (a.ScaledBy(1.0/3)).Plus(b.ScaledBy(2.0/3));
+ if(refp) *refp = m;
Vector ab = a.Minus(b);
- Vector n = (gn.Cross(ab)).WithMagnitude(10/SS.GW.scale);
+ Vector n = (gn.Cross(ab)).WithMagnitude(10/camera.scale);
- LineDrawOrGetDistance(m.Minus(n), m.Plus(n));
+ DoLine(canvas, hcs, m.Minus(n), m.Plus(n));
}
-void Constraint::DoEqualRadiusTicks(hEntity he) {
+void Constraint::DoEqualRadiusTicks(Canvas *canvas, Canvas::hStroke hcs,
+ hEntity he, Vector *refp) {
+ const Camera &camera = canvas->GetCamera();
Entity *circ = SK.GetEntity(he);
Vector center = SK.GetEntity(circ->point[0])->PointGetNum();
Vector u = q.RotationU(), v = q.RotationV();
double theta;
- if(circ->type == Entity::CIRCLE) {
+ if(circ->type == Entity::Type::CIRCLE) {
theta = PI/2;
- } else if(circ->type == Entity::ARC_OF_CIRCLE) {
+ } else if(circ->type == Entity::Type::ARC_OF_CIRCLE) {
double thetaa, thetab, dtheta;
circ->ArcGetAngles(&thetaa, &thetab, &dtheta);
theta = thetaa + dtheta/2;
- } else oops();
+ } else ssassert(false, "Unexpected entity type");
Vector d = u.ScaledBy(cos(theta)).Plus(v.ScaledBy(sin(theta)));
d = d.ScaledBy(r);
Vector p = center.Plus(d);
- Vector tick = d.WithMagnitude(10/SS.GW.scale);
- LineDrawOrGetDistance(p.Plus(tick), p.Minus(tick));
+ if(refp) *refp = p;
+ Vector tick = d.WithMagnitude(10/camera.scale);
+ DoLine(canvas, hcs, p.Plus(tick), p.Minus(tick));
}
-void Constraint::DoArcForAngle(Vector a0, Vector da, Vector b0, Vector db,
- Vector offset, Vector *ref, bool trim)
+void Constraint::DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
+ Vector a0, Vector da, Vector b0, Vector db,
+ Vector offset, Vector *ref, bool trim)
{
- Vector gr = SS.GW.projRight.ScaledBy(1.0);
- Vector gu = SS.GW.projUp.ScaledBy(1.0);
+ const Camera &camera = canvas->GetCamera();
+ double pixels = 1.0 / camera.scale;
+ Vector gr = camera.projRight.ScaledBy(1.0);
+ Vector gu = camera.projUp.ScaledBy(1.0);
- if(workplane.v != Entity::FREE_IN_3D.v) {
+ if(workplane != Entity::FREE_IN_3D) {
a0 = a0.ProjectInto(workplane);
b0 = b0.ProjectInto(workplane);
da = da.ProjectVectorInto(workplane);
db = db.ProjectVectorInto(workplane);
}
- double px = 1.0 / SS.GW.scale;
Vector a1 = a0.Plus(da);
Vector b1 = b0.Plus(db);
double rda = rm.Dot(da), rdna = rm.Dot(dna);
// Introduce minimal arc radius in pixels
- double r = max(sqrt(rda*rda + rdna*rdna), 15.0 * px);
+ double r = max(sqrt(rda*rda + rdna*rdna), 15.0 * pixels);
- hStyle hs = disp.style;
- if(hs.v == 0) hs.v = Style::CONSTRAINT;
- double th = Style::TextHeight(hs);
- double swidth = ssglStrWidth(Label(), th) + 4.0 * px;
- double sheight = ssglStrCapHeight(th) + 8.0 * px;
+ double th = Style::TextHeight(GetStyle()) / camera.scale;
+ double swidth = VectorFont::Builtin()->GetWidth(th, Label()) + 8*pixels,
+ sheight = VectorFont::Builtin()->GetCapHeight(th) + 6*pixels;
double textR = sqrt(swidth * swidth + sheight * sheight) / 2.0;
- *ref = pi.Plus(rm.WithMagnitude(std::max(rm.Magnitude(), 15 * px + textR)));
+ *ref = pi.Plus(rm.WithMagnitude(std::max(rm.Magnitude(), 15 * pixels + textR)));
// Arrow points
Vector apa = da. ScaledBy(r).Plus(pi);
Vector apb = da. ScaledBy(r*cos(thetaf)).Plus(
dna.ScaledBy(r*sin(thetaf))).Plus(pi);
- double arrowW = 13 * px;
+ double arrowW = 13 * pixels;
double arrowA = 18.0 * PI / 180.0;
bool arrowVisible = apb.Minus(apa).Magnitude() > 2.5 * arrowW;
// Arrow reversing indicator
bool arrowRev = false;
// The minimal extension length in angular representation
- double extAngle = 18 * px / r;
+ double extAngle = 18 * pixels / r;
// Arc additional angle
double addAngle = 0.0;
dna.ScaledBy(r*sin(theta))).Plus(pi);
if(i > 0) {
if(trim) {
- DoLineTrimmedAgainstBox(*ref, prev, p, false, gr, gu, swidth, sheight);
+ DoLineTrimmedAgainstBox(canvas, hcs, *ref, prev, p,
+ /*extend=*/false, gr, gu, swidth, sheight + 2*pixels);
} else {
- LineDrawOrGetDistance(prev, p);
+ DoLine(canvas, hcs, prev, p);
}
}
prev = p;
}
- DoLineExtend(a0, a1, apa, 5.0 * px);
- DoLineExtend(b0, b1, apb, 5.0 * px);
+ DoLineExtend(canvas, hcs, a0, a1, apa, 5.0 * pixels);
+ DoLineExtend(canvas, hcs, b0, b1, apb, 5.0 * pixels);
// Draw arrows only when we have enough space.
if(arrowVisible) {
dna = dna.ScaledBy(-1.0);
angleCorr = -angleCorr;
}
- DoArrow(apa, dna, norm, arrowW, arrowA, angleCorr);
- DoArrow(apb, dna, norm, arrowW, arrowA, thetaf + PI - angleCorr);
+ DoArrow(canvas, hcs, apa, dna, norm, arrowW, arrowA, angleCorr);
+ DoArrow(canvas, hcs, apb, dna, norm, arrowW, arrowA, thetaf + PI - angleCorr);
}
} else {
// The lines are skew; no wonderful way to illustrate that.
+
*ref = a0.Plus(b0);
*ref = (*ref).ScaledBy(0.5).Plus(disp.offset);
gu = gu.WithMagnitude(1);
+ double textHeight = Style::TextHeight(GetStyle()) / camera.scale;
Vector trans =
- (*ref).Plus(gu.ScaledBy(-1.5*ssglStrCapHeight(Style::DefaultTextHeight())));
- ssglWriteTextRefCenter("angle between skew lines", Style::DefaultTextHeight(),
- trans, gr.WithMagnitude(px), gu.WithMagnitude(px), LineCallback, this);
+ (*ref).Plus(gu.ScaledBy(-1.5*VectorFont::Builtin()->GetCapHeight(textHeight)));
+ canvas->DrawVectorText("angle between skew lines", textHeight,
+ trans, gr.WithMagnitude(1), gu.WithMagnitude(1),
+ hcs);
}
}
if(!(g->visible)) return false;
// And likewise if the group is not the active group; except for comments
// with an assigned style.
- if(g->h.v != SS.GW.activeGroup.v && !(type == COMMENT && disp.style.v)) {
+ if(g->h != SS.GW.activeGroup && !(type == Type::COMMENT && disp.style.v)) {
return false;
}
if(disp.style.v) {
return true;
}
-bool Constraint::DoLineExtend(Vector p0, Vector p1, Vector pt, double salient) {
+bool Constraint::DoLineExtend(Canvas *canvas, Canvas::hStroke hcs,
+ Vector p0, Vector p1, Vector pt, double salient) {
Vector dir = p1.Minus(p0);
double k = dir.Dot(pt.Minus(p0)) / dir.Dot(dir);
Vector ptOnLine = p0.Plus(dir.ScaledBy(k));
// Draw projection line.
- LineDrawOrGetDistance(pt, ptOnLine);
+ DoLine(canvas, hcs, pt, ptOnLine);
// Calculate salient direction.
Vector sd = dir.WithMagnitude(1.0).ScaledBy(salient);
}
// Draw extension line.
- LineDrawOrGetDistance(from, to);
+ DoLine(canvas, hcs, from, to);
return true;
}
-void Constraint::DrawOrGetDistance(Vector *labelPos) {
-
- if(!IsVisible()) return;
+void Constraint::DoLayout(DrawAs how, Canvas *canvas,
+ Vector *labelPos, std::vector<Vector> *refs) {
+ if(!(how == DrawAs::HOVERED || how == DrawAs::SELECTED) &&
+ !IsVisible()) return;
// Unit vectors that describe our current view of the scene. One pixel
// long, not one actual unit.
- Vector gr = SS.GW.projRight.ScaledBy(1/SS.GW.scale);
- Vector gu = SS.GW.projUp.ScaledBy(1/SS.GW.scale);
- Vector gn = (gr.Cross(gu)).WithMagnitude(1/SS.GW.scale);
+ const Camera &camera = canvas->GetCamera();
+ Vector gr = camera.projRight.ScaledBy(1/camera.scale);
+ Vector gu = camera.projUp.ScaledBy(1/camera.scale);
+ Vector gn = (gr.Cross(gu)).WithMagnitude(1/camera.scale);
+
+ double textHeight = Style::TextHeight(GetStyle()) / camera.scale;
+
+ RgbaColor color = {};
+ switch(how) {
+ case DrawAs::DEFAULT: color = Style::Color(GetStyle()); break;
+ case DrawAs::HOVERED: color = Style::Color(Style::HOVERED); break;
+ case DrawAs::SELECTED: color = Style::Color(Style::SELECTED); break;
+ }
+ Canvas::Stroke stroke = Style::Stroke(GetStyle());
+ stroke.layer = Canvas::Layer::FRONT;
+ stroke.color = color;
+ stroke.zIndex = 4;
+ Canvas::hStroke hcs = canvas->GetStroke(stroke);
+
+ Canvas::Fill fill = {};
+ fill.layer = Canvas::Layer::FRONT;
+ fill.color = color;
+ fill.zIndex = stroke.zIndex;
+ Canvas::hFill hcf = canvas->GetFill(fill);
switch(type) {
- case PT_PT_DISTANCE: {
+ case Type::PT_PT_DISTANCE: {
Vector ap = SK.GetEntity(ptA)->PointGetNum();
Vector bp = SK.GetEntity(ptB)->PointGetNum();
- if(workplane.v != Entity::FREE_IN_3D.v) {
- DoProjectedPoint(&ap);
- DoProjectedPoint(&bp);
+ if(workplane != Entity::FREE_IN_3D) {
+ DoProjectedPoint(canvas, hcs, &ap);
+ DoProjectedPoint(canvas, hcs, &bp);
}
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset);
+ if(refs) refs->push_back(ref);
- DoLineWithArrows(ref, ap, bp, false);
- DoLabel(ref, labelPos, gr, gu);
- break;
+ DoLineWithArrows(canvas, hcs, ref, ap, bp, /*onlyOneExt=*/false);
+ DoLabel(canvas, hcs, ref, labelPos, gr, gu);
+ return;
}
- case PROJ_PT_DISTANCE: {
+ case Type::PROJ_PT_DISTANCE: {
Vector ap = SK.GetEntity(ptA)->PointGetNum(),
bp = SK.GetEntity(ptB)->PointGetNum(),
dp = (bp.Minus(ap)),
pp = SK.GetEntity(entityA)->VectorGetNum();
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset);
+ if(refs) refs->push_back(ref);
pp = pp.WithMagnitude(1);
double d = dp.Dot(pp);
Vector bpp = ap.Plus(pp.ScaledBy(d));
- StippledLine(ap, bpp);
- StippledLine(bp, bpp);
+ DoStippledLine(canvas, hcs, ap, bpp);
+ DoStippledLine(canvas, hcs, bp, bpp);
- DoLineWithArrows(ref, ap, bpp, false);
- DoLabel(ref, labelPos, gr, gu);
- break;
+ DoLineWithArrows(canvas, hcs, ref, ap, bpp, /*onlyOneExt=*/false);
+ DoLabel(canvas, hcs, ref, labelPos, gr, gu);
+ return;
}
- case PT_FACE_DISTANCE:
- case PT_PLANE_DISTANCE: {
+ case Type::PT_FACE_DISTANCE:
+ case Type::PT_PLANE_DISTANCE: {
Vector pt = SK.GetEntity(ptA)->PointGetNum();
Entity *enta = SK.GetEntity(entityA);
Vector n, p;
- if(type == PT_PLANE_DISTANCE) {
+ if(type == Type::PT_PLANE_DISTANCE) {
n = enta->Normal()->NormalN();
p = enta->WorkplaneGetOffset();
} else {
Vector closest = pt.Plus(n.WithMagnitude(d));
Vector ref = ((closest.Plus(pt)).ScaledBy(0.5)).Plus(disp.offset);
+ if(refs) refs->push_back(ref);
if(!pt.Equals(closest)) {
- DoLineWithArrows(ref, pt, closest, true);
+ DoLineWithArrows(canvas, hcs, ref, pt, closest, /*onlyOneExt=*/true);
}
- DoLabel(ref, labelPos, gr, gu);
- break;
+ DoLabel(canvas, hcs, ref, labelPos, gr, gu);
+ return;
}
- case PT_LINE_DISTANCE: {
+ case Type::PT_LINE_DISTANCE: {
Vector pt = SK.GetEntity(ptA)->PointGetNum();
Entity *line = SK.GetEntity(entityA);
Vector lA = SK.GetEntity(line->point[0])->PointGetNum();
Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
Vector dl = lB.Minus(lA);
- if(workplane.v != Entity::FREE_IN_3D.v) {
+ if(workplane != Entity::FREE_IN_3D) {
lA = lA.ProjectInto(workplane);
lB = lB.ProjectInto(workplane);
- DoProjectedPoint(&pt);
+ DoProjectedPoint(canvas, hcs, &pt);
}
// Find the closest point on the line
Vector closest = pt.ClosestPointOnLine(lA, dl);
Vector ref = ((closest.Plus(pt)).ScaledBy(0.5)).Plus(disp.offset);
- DoLabel(ref, labelPos, gr, gu);
+ if(refs) refs->push_back(ref);
+ DoLabel(canvas, hcs, ref, labelPos, gr, gu);
if(!pt.Equals(closest)) {
- DoLineWithArrows(ref, pt, closest, true);
+ DoLineWithArrows(canvas, hcs, ref, pt, closest, /*onlyOneExt=*/true);
+
+ // Draw projected point
+ Vector a = pt;
+ Vector b = closest;
+ Vector ab = a.Minus(b);
+ Vector ar = a.Minus(ref);
+ Vector n = ab.Cross(ar);
+ Vector out = ab.Cross(n).WithMagnitude(1);
+ out = out.ScaledBy(-out.Dot(ar));
+ Vector be = b.Plus(out);
+ Vector np = lA.Minus(pt).Cross(lB.Minus(pt)).WithMagnitude(1.0);
+ DoProjectedPoint(canvas, hcs, &be, np, pt);
// Extensions to line
- double pixels = 1.0 / SS.GW.scale;
+ double pixels = 1.0 / camera.scale;
Vector refClosest = ref.ClosestPointOnLine(lA, dl);
double ddl = dl.Dot(dl);
if(fabs(ddl) > LENGTH_EPS * LENGTH_EPS) {
double t = refClosest.Minus(lA).Dot(dl) / ddl;
if(t < 0.0) {
- LineDrawOrGetDistance(refClosest.Minus(dl.WithMagnitude(10.0 * pixels)), lA);
+ DoLine(canvas, hcs, refClosest.Minus(dl.WithMagnitude(10.0 * pixels)), lA);
} else if(t > 1.0) {
- LineDrawOrGetDistance(refClosest.Plus(dl.WithMagnitude(10.0 * pixels)), lB);
+ DoLine(canvas, hcs, refClosest.Plus(dl.WithMagnitude(10.0 * pixels)), lB);
}
}
}
- if(workplane.v != Entity::FREE_IN_3D.v) {
+ if(workplane != Entity::FREE_IN_3D) {
// Draw the projection marker from the closest point on the
// projected line to the projected point on the real line.
Vector lAB = (lA.Minus(lB));
- double t = (lA.Minus(closest)).DivPivoting(lAB);
+ double t = (lA.Minus(closest)).DivProjected(lAB);
Vector lA = SK.GetEntity(line->point[0])->PointGetNum();
Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
Vector c2 = (lA.ScaledBy(1-t)).Plus(lB.ScaledBy(t));
- DoProjectedPoint(&c2);
+ DoProjectedPoint(canvas, hcs, &c2);
}
- break;
+ return;
}
- case DIAMETER: {
+ case Type::DIAMETER: {
Entity *circle = SK.GetEntity(entityA);
Vector center = SK.GetEntity(circle->point[0])->PointGetNum();
Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum();
Vector ref = center.Plus(disp.offset);
// Force the label into the same plane as the circle.
ref = ref.Minus(n.ScaledBy(n.Dot(ref) - n.Dot(center)));
+ if(refs) refs->push_back(ref);
Vector mark = ref.Minus(center);
mark = mark.WithMagnitude(mark.Magnitude()-r);
- DoLineTrimmedAgainstBox(ref, ref, ref.Minus(mark));
+ DoLineTrimmedAgainstBox(canvas, hcs, ref, ref, ref.Minus(mark));
Vector topLeft;
- DoLabel(ref, &topLeft, gr, gu);
+ DoLabel(canvas, hcs, ref, &topLeft, gr, gu);
if(labelPos) *labelPos = topLeft;
- break;
+ return;
}
- case POINTS_COINCIDENT: {
- if(!dogd.drawing) {
- for(int i = 0; i < 2; i++) {
- Vector p = SK.GetEntity(i == 0 ? ptA : ptB)-> PointGetNum();
- Point2d pp = SS.GW.ProjectPoint(p);
- // The point is selected within a radius of 7, from the
- // same center; so if the point is visible, then this
- // constraint cannot be selected. But that's okay.
- dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 3);
- dogd.refp = p;
- }
- break;
- }
-
- if(dogd.drawing) {
+ case Type::POINTS_COINCIDENT: {
+ if(how == DrawAs::DEFAULT) {
// Let's adjust the color of this constraint to have the same
// rough luma as the point color, so that the constraint does not
// stand out in an ugly way.
RgbaColor cd = Style::Color(Style::DATUM),
- cc = Style::Color(Style::CONSTRAINT);
+ cc = Style::Color(Style::CONSTRAINT);
// convert from 8-bit color to a vector
Vector vd = Vector::From(cd.redF(), cd.greenF(), cd.blueF()),
vc = Vector::From(cc.redF(), cc.greenF(), cc.blueF());
// the datum color, maybe a bit dimmer
vc = vc.WithMagnitude(vd.Magnitude()*0.9);
// and set the color to that.
- ssglColorRGB(RGBf(vc.x, vc.y, vc.z));
-
- for(int a = 0; a < 2; a++) {
- Vector r = SS.GW.projRight.ScaledBy((a+1)/SS.GW.scale);
- Vector d = SS.GW.projUp.ScaledBy((2-a)/SS.GW.scale);
- for(int i = 0; i < 2; i++) {
- Vector p = SK.GetEntity(i == 0 ? ptA : ptB)-> PointGetNum();
- glBegin(GL_QUADS);
- ssglVertex3v(p.Plus (r).Plus (d));
- ssglVertex3v(p.Plus (r).Minus(d));
- ssglVertex3v(p.Minus(r).Minus(d));
- ssglVertex3v(p.Minus(r).Plus (d));
- glEnd();
- }
+ fill.color = RGBf(vc.x, vc.y, vc.z);
+ hcf = canvas->GetFill(fill);
+ }
+ for(int a = 0; a < 2; a++) {
+ Vector r = camera.projRight.ScaledBy((a+1)/camera.scale);
+ Vector d = camera.projUp.ScaledBy((2-a)/camera.scale);
+ for(int i = 0; i < 2; i++) {
+ Vector p = SK.GetEntity(i == 0 ? ptA : ptB)-> PointGetNum();
+ if(refs) refs->push_back(p);
+ canvas->DrawQuad(p.Plus (r).Plus (d),
+ p.Plus (r).Minus(d),
+ p.Minus(r).Minus(d),
+ p.Minus(r).Plus (d),
+ hcf);
}
+
}
- break;
+ return;
}
- case PT_ON_CIRCLE:
- case PT_ON_LINE:
- case PT_ON_FACE:
- case PT_IN_PLANE: {
- double s = 8/SS.GW.scale;
+ case Type::PT_ON_CIRCLE:
+ case Type::PT_ON_LINE:
+ case Type::PT_ON_FACE:
+ case Type::PT_IN_PLANE: {
+ double s = 8/camera.scale;
Vector p = SK.GetEntity(ptA)->PointGetNum();
+ if(refs) refs->push_back(p);
Vector r, d;
- if(type == PT_ON_FACE) {
+ if(type == Type::PT_ON_FACE) {
Vector n = SK.GetEntity(entityA)->FaceGetNormalNum();
r = n.Normal(0);
d = n.Normal(1);
- } else if(type == PT_IN_PLANE) {
+ } else if(type == Type::PT_IN_PLANE) {
EntityBase *n = SK.GetEntity(entityA)->Normal();
r = n->NormalU();
d = n->NormalV();
s *= (6.0/8); // draw these a little smaller
}
r = r.WithMagnitude(s); d = d.WithMagnitude(s);
- LineDrawOrGetDistance(p.Plus (r).Plus (d), p.Plus (r).Minus(d));
- LineDrawOrGetDistance(p.Plus (r).Minus(d), p.Minus(r).Minus(d));
- LineDrawOrGetDistance(p.Minus(r).Minus(d), p.Minus(r).Plus (d));
- LineDrawOrGetDistance(p.Minus(r).Plus (d), p.Plus (r).Plus (d));
- break;
+ DoLine(canvas, hcs, p.Plus (r).Plus (d), p.Plus (r).Minus(d));
+ DoLine(canvas, hcs, p.Plus (r).Minus(d), p.Minus(r).Minus(d));
+ DoLine(canvas, hcs, p.Minus(r).Minus(d), p.Minus(r).Plus (d));
+ DoLine(canvas, hcs, p.Minus(r).Plus (d), p.Plus (r).Plus (d));
+ return;
}
- case WHERE_DRAGGED: {
- Vector p = SK.GetEntity(ptA)->PointGetNum(),
- u = p.Plus(gu.WithMagnitude(8/SS.GW.scale)).Plus(
- gr.WithMagnitude(8/SS.GW.scale)),
- uu = u.Minus(gu.WithMagnitude(5/SS.GW.scale)),
- ur = u.Minus(gr.WithMagnitude(5/SS.GW.scale));
+ case Type::WHERE_DRAGGED: {
+ Vector p = SK.GetEntity(ptA)->PointGetNum();
+ if(refs) refs->push_back(p);
+ Vector u = p.Plus(gu.WithMagnitude(8/camera.scale)).Plus(
+ gr.WithMagnitude(8/camera.scale)),
+ uu = u.Minus(gu.WithMagnitude(5/camera.scale)),
+ ur = u.Minus(gr.WithMagnitude(5/camera.scale));
// Draw four little crop marks, uniformly spaced (by ninety
// degree rotations) around the point.
int i;
for(i = 0; i < 4; i++) {
- LineDrawOrGetDistance(u, uu);
- LineDrawOrGetDistance(u, ur);
+ DoLine(canvas, hcs, u, uu);
+ DoLine(canvas, hcs, u, ur);
u = u.RotatedAbout(p, gn, PI/2);
ur = ur.RotatedAbout(p, gn, PI/2);
uu = uu.RotatedAbout(p, gn, PI/2);
}
- break;
+ return;
}
- case SAME_ORIENTATION: {
+ case Type::SAME_ORIENTATION: {
for(int i = 0; i < 2; i++) {
Entity *e = SK.GetEntity(i == 0 ? entityA : entityB);
Quaternion q = e->NormalGetNum();
- Vector n = q.RotationN().WithMagnitude(25/SS.GW.scale);
- Vector u = q.RotationU().WithMagnitude(6/SS.GW.scale);
+ Vector n = q.RotationN().WithMagnitude(25/camera.scale);
+ Vector u = q.RotationU().WithMagnitude(6/camera.scale);
Vector p = SK.GetEntity(e->point[0])->PointGetNum();
- p = p.Plus(n.WithMagnitude(10/SS.GW.scale));
+ p = p.Plus(n.WithMagnitude(10/camera.scale));
+ if(refs) refs->push_back(p);
- LineDrawOrGetDistance(p.Plus(u), p.Minus(u).Plus(n));
- LineDrawOrGetDistance(p.Minus(u), p.Plus(u).Plus(n));
+ DoLine(canvas, hcs, p.Plus(u), p.Minus(u).Plus(n));
+ DoLine(canvas, hcs, p.Minus(u), p.Plus(u).Plus(n));
}
- break;
+ return;
}
- case EQUAL_ANGLE: {
+ case Type::EQUAL_ANGLE: {
Vector ref;
Entity *a = SK.GetEntity(entityA);
Entity *b = SK.GetEntity(entityB);
da = da.ScaledBy(-1);
}
- DoArcForAngle(a0, da, b0, db,
- da.WithMagnitude(40/SS.GW.scale), &ref, /*trim=*/false);
- DoArcForAngle(c0, dc, d0, dd,
- dc.WithMagnitude(40/SS.GW.scale), &ref, /*trim=*/false);
+ DoArcForAngle(canvas, hcs, a0, da, b0, db,
+ da.WithMagnitude(40/camera.scale), &ref, /*trim=*/false);
+ if(refs) refs->push_back(ref);
+ DoArcForAngle(canvas, hcs, c0, dc, d0, dd,
+ dc.WithMagnitude(40/camera.scale), &ref, /*trim=*/false);
+ if(refs) refs->push_back(ref);
- break;
+ return;
}
- case ANGLE: {
+ case Type::ANGLE: {
Entity *a = SK.GetEntity(entityA);
Entity *b = SK.GetEntity(entityB);
}
Vector ref;
- DoArcForAngle(a0, da, b0, db, disp.offset, &ref, /*trim=*/true);
- DoLabel(ref, labelPos, gr, gu);
- break;
+ DoArcForAngle(canvas, hcs, a0, da, b0, db, disp.offset, &ref, /*trim=*/true);
+ DoLabel(canvas, hcs, ref, labelPos, gr, gu);
+ if(refs) refs->push_back(ref);
+ return;
}
- case PERPENDICULAR: {
+ case Type::PERPENDICULAR: {
Vector u = Vector::From(0, 0, 0), v = Vector::From(0, 0, 0);
Vector rn, ru;
- if(workplane.v == Entity::FREE_IN_3D.v) {
+ if(workplane == Entity::FREE_IN_3D) {
rn = gn;
ru = gu;
} else {
// Calculate orientation of perpendicular sign only
// once, so that it's the same both times it's drawn
u = e->VectorGetNum();
- u = u.WithMagnitude(16/SS.GW.scale);
- v = (rn.Cross(u)).WithMagnitude(16/SS.GW.scale);
+ u = u.WithMagnitude(16/camera.scale);
+ v = (rn.Cross(u)).WithMagnitude(16/camera.scale);
// a bit of bias to stop it from flickering between the
// two possibilities
if(fabs(u.Dot(ru)) < fabs(v.Dot(ru)) + LENGTH_EPS) {
Vector p = e->VectorGetRefPoint();
Vector s = p.Plus(u).Plus(v);
- LineDrawOrGetDistance(s, s.Plus(v));
-
+ DoLine(canvas, hcs, s, s.Plus(v));
Vector m = s.Plus(v.ScaledBy(0.5));
- LineDrawOrGetDistance(m, m.Plus(u));
+ DoLine(canvas, hcs, m, m.Plus(u));
+ if(refs) refs->push_back(m);
}
- break;
+ return;
}
- case CURVE_CURVE_TANGENT:
- case CUBIC_LINE_TANGENT:
- case ARC_LINE_TANGENT: {
+ case Type::CURVE_CURVE_TANGENT:
+ case Type::CUBIC_LINE_TANGENT:
+ case Type::ARC_LINE_TANGENT: {
Vector textAt, u, v;
- if(type == ARC_LINE_TANGENT) {
+ if(type == Type::ARC_LINE_TANGENT) {
Entity *arc = SK.GetEntity(entityA);
Entity *norm = SK.GetEntity(arc->normal);
Vector c = SK.GetEntity(arc->point[0])->PointGetNum();
Vector p =
SK.GetEntity(arc->point[other ? 2 : 1])->PointGetNum();
Vector r = p.Minus(c);
- textAt = p.Plus(r.WithMagnitude(14/SS.GW.scale));
+ textAt = p.Plus(r.WithMagnitude(14/camera.scale));
u = norm->NormalU();
v = norm->NormalV();
- } else if(type == CUBIC_LINE_TANGENT) {
+ } else if(type == Type::CUBIC_LINE_TANGENT) {
Vector n;
- if(workplane.v == Entity::FREE_IN_3D.v) {
+ if(workplane == Entity::FREE_IN_3D) {
u = gr;
v = gu;
n = gn;
cubic->CubicGetStartNum();
Vector dir = SK.GetEntity(entityB)->VectorGetNum();
Vector out = n.Cross(dir);
- textAt = p.Plus(out.WithMagnitude(14/SS.GW.scale));
+ textAt = p.Plus(out.WithMagnitude(14/camera.scale));
} else {
Vector n, dir;
EntityBase *wn = SK.GetEntity(workplane)->Normal();
// or an arc.
if(other) {
textAt = eA->EndpointFinish();
- if(eA->type == Entity::CUBIC) {
+ if(eA->type == Entity::Type::CUBIC) {
dir = eA->CubicGetFinishTangentNum();
} else {
dir = SK.GetEntity(eA->point[0])->PointGetNum().Minus(
}
} else {
textAt = eA->EndpointStart();
- if(eA->type == Entity::CUBIC) {
+ if(eA->type == Entity::Type::CUBIC) {
dir = eA->CubicGetStartTangentNum();
} else {
dir = SK.GetEntity(eA->point[0])->PointGetNum().Minus(
}
}
dir = n.Cross(dir);
- textAt = textAt.Plus(dir.WithMagnitude(14/SS.GW.scale));
+ textAt = textAt.Plus(dir.WithMagnitude(14/camera.scale));
}
- if(dogd.drawing) {
- ssglWriteTextRefCenter("T", Style::DefaultTextHeight(),
- textAt, u, v, LineCallback, this);
- } else {
- dogd.refp = textAt;
- Point2d ref = SS.GW.ProjectPoint(dogd.refp);
- dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-10);
- }
- break;
+ Vector ex = VectorFont::Builtin()->GetExtents(textHeight, "T");
+ canvas->DrawVectorText("T", textHeight, textAt.Minus(ex.ScaledBy(0.5)),
+ u.WithMagnitude(1), v.WithMagnitude(1), hcs);
+ if(refs) refs->push_back(textAt);
+ return;
}
- case PARALLEL: {
+ case Type::PARALLEL: {
for(int i = 0; i < 2; i++) {
Entity *e = SK.GetEntity(i == 0 ? entityA : entityB);
Vector n = e->VectorGetNum();
- n = n.WithMagnitude(25/SS.GW.scale);
- Vector u = (gn.Cross(n)).WithMagnitude(4/SS.GW.scale);
+ n = n.WithMagnitude(25/camera.scale);
+ Vector u = (gn.Cross(n)).WithMagnitude(4/camera.scale);
Vector p = e->VectorGetRefPoint();
- LineDrawOrGetDistance(p.Plus(u), p.Plus(u).Plus(n));
- LineDrawOrGetDistance(p.Minus(u), p.Minus(u).Plus(n));
+ DoLine(canvas, hcs, p.Plus(u), p.Plus(u).Plus(n));
+ DoLine(canvas, hcs, p.Minus(u), p.Minus(u).Plus(n));
+ if(refs) refs->push_back(p.Plus(n.ScaledBy(0.5)));
}
- break;
+ return;
}
- case EQUAL_RADIUS: {
+ case Type::EQUAL_RADIUS: {
for(int i = 0; i < 2; i++) {
- DoEqualRadiusTicks(i == 0 ? entityA : entityB);
+ Vector ref;
+ DoEqualRadiusTicks(canvas, hcs, i == 0 ? entityA : entityB, &ref);
+ if(refs) refs->push_back(ref);
}
- break;
+ return;
}
- case EQUAL_LINE_ARC_LEN: {
+ case Type::EQUAL_LINE_ARC_LEN: {
Entity *line = SK.GetEntity(entityA);
- DoEqualLenTicks(
+ Vector ref;
+ DoEqualLenTicks(canvas, hcs,
SK.GetEntity(line->point[0])->PointGetNum(),
SK.GetEntity(line->point[1])->PointGetNum(),
- gn);
-
- DoEqualRadiusTicks(entityB);
- break;
+ gn, &ref);
+ if(refs) refs->push_back(ref);
+ DoEqualRadiusTicks(canvas, hcs, entityB, &ref);
+ if(refs) refs->push_back(ref);
+ return;
}
- case LENGTH_RATIO:
- case LENGTH_DIFFERENCE:
- case EQUAL_LENGTH_LINES: {
+ case Type::LENGTH_RATIO:
+ case Type::LENGTH_DIFFERENCE:
+ case Type::EQUAL_LENGTH_LINES: {
Vector a, b = Vector::From(0, 0, 0);
for(int i = 0; i < 2; i++) {
Entity *e = SK.GetEntity(i == 0 ? entityA : entityB);
a = SK.GetEntity(e->point[0])->PointGetNum();
b = SK.GetEntity(e->point[1])->PointGetNum();
- if(workplane.v != Entity::FREE_IN_3D.v) {
- DoProjectedPoint(&a);
- DoProjectedPoint(&b);
+ if(workplane != Entity::FREE_IN_3D) {
+ DoProjectedPoint(canvas, hcs, &a);
+ DoProjectedPoint(canvas, hcs, &b);
}
- DoEqualLenTicks(a, b, gn);
+ Vector ref;
+ DoEqualLenTicks(canvas, hcs, a, b, gn, &ref);
+ if(refs) refs->push_back(ref);
}
- if((type == LENGTH_RATIO) || (type == LENGTH_DIFFERENCE)) {
+ if((type == Type::LENGTH_RATIO) || (type == Type::LENGTH_DIFFERENCE)) {
Vector ref = ((a.Plus(b)).ScaledBy(0.5)).Plus(disp.offset);
- DoLabel(ref, labelPos, gr, gu);
+ DoLabel(canvas, hcs, ref, labelPos, gr, gu);
}
- break;
+ return;
}
- case EQ_LEN_PT_LINE_D: {
+ case Type::EQ_LEN_PT_LINE_D: {
Entity *forLen = SK.GetEntity(entityA);
Vector a = SK.GetEntity(forLen->point[0])->PointGetNum(),
b = SK.GetEntity(forLen->point[1])->PointGetNum();
- if(workplane.v != Entity::FREE_IN_3D.v) {
- DoProjectedPoint(&a);
- DoProjectedPoint(&b);
+ if(workplane != Entity::FREE_IN_3D) {
+ DoProjectedPoint(canvas, hcs, &a);
+ DoProjectedPoint(canvas, hcs, &b);
}
- DoEqualLenTicks(a, b, gn);
+ Vector refa;
+ DoEqualLenTicks(canvas, hcs, a, b, gn, &refa);
+ if(refs) refs->push_back(refa);
Entity *ln = SK.GetEntity(entityB);
Vector la = SK.GetEntity(ln->point[0])->PointGetNum(),
lb = SK.GetEntity(ln->point[1])->PointGetNum();
Vector pt = SK.GetEntity(ptA)->PointGetNum();
- if(workplane.v != Entity::FREE_IN_3D.v) {
- DoProjectedPoint(&pt);
+ if(workplane != Entity::FREE_IN_3D) {
+ DoProjectedPoint(canvas, hcs, &pt);
la = la.ProjectInto(workplane);
lb = lb.ProjectInto(workplane);
}
Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la));
- LineDrawOrGetDistance(pt, closest);
- DoEqualLenTicks(pt, closest, gn);
- break;
+ DoLine(canvas, hcs, pt, closest);
+ Vector refb;
+ DoEqualLenTicks(canvas, hcs, pt, closest, gn, &refb);
+ if(refs) refs->push_back(refb);
+ return;
}
- case EQ_PT_LN_DISTANCES: {
+ case Type::EQ_PT_LN_DISTANCES: {
for(int i = 0; i < 2; i++) {
Entity *ln = SK.GetEntity(i == 0 ? entityA : entityB);
Vector la = SK.GetEntity(ln->point[0])->PointGetNum(),
Entity *pte = SK.GetEntity(i == 0 ? ptA : ptB);
Vector pt = pte->PointGetNum();
- if(workplane.v != Entity::FREE_IN_3D.v) {
- DoProjectedPoint(&pt);
+ if(workplane != Entity::FREE_IN_3D) {
+ DoProjectedPoint(canvas, hcs, &pt);
la = la.ProjectInto(workplane);
lb = lb.ProjectInto(workplane);
}
Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la));
+ DoLine(canvas, hcs, pt, closest);
- LineDrawOrGetDistance(pt, closest);
- DoEqualLenTicks(pt, closest, gn);
+ Vector ref;
+ DoEqualLenTicks(canvas, hcs, pt, closest, gn, &ref);
+ if(refs) refs->push_back(ref);
}
- break;
+ return;
}
{
- case SYMMETRIC:
+ case Type::SYMMETRIC:
Vector n;
n = SK.GetEntity(entityA)->Normal()->NormalN(); goto s;
- case SYMMETRIC_HORIZ:
+ case Type::SYMMETRIC_HORIZ:
n = SK.GetEntity(workplane)->Normal()->NormalU(); goto s;
- case SYMMETRIC_VERT:
+ case Type::SYMMETRIC_VERT:
n = SK.GetEntity(workplane)->Normal()->NormalV(); goto s;
- case SYMMETRIC_LINE: {
+ case Type::SYMMETRIC_LINE: {
Entity *ln = SK.GetEntity(entityA);
Vector la = SK.GetEntity(ln->point[0])->PointGetNum(),
lb = SK.GetEntity(ln->point[1])->PointGetNum();
// they might not be in the same direction, even when the
// constraint is fully solved.
d = n.ScaledBy(d.Dot(n));
- d = d.WithMagnitude(20/SS.GW.scale);
+ d = d.WithMagnitude(20/camera.scale);
Vector tip = tail.Plus(d);
- LineDrawOrGetDistance(tail, tip);
- d = d.WithMagnitude(9/SS.GW.scale);
- LineDrawOrGetDistance(tip, tip.Minus(d.RotatedAbout(gn, 0.6)));
- LineDrawOrGetDistance(tip, tip.Minus(d.RotatedAbout(gn, -0.6)));
+ DoLine(canvas, hcs, tail, tip);
+ d = d.WithMagnitude(9/camera.scale);
+ DoLine(canvas, hcs, tip, tip.Minus(d.RotatedAbout(gn, 0.6)));
+ DoLine(canvas, hcs, tip, tip.Minus(d.RotatedAbout(gn, -0.6)));
+ if(refs) refs->push_back(tip);
}
- break;
+ return;
}
- case AT_MIDPOINT:
- case HORIZONTAL:
- case VERTICAL:
+ case Type::AT_MIDPOINT:
+ case Type::HORIZONTAL:
+ case Type::VERTICAL:
if(entityA.v) {
Vector r, u, n;
- if(workplane.v == Entity::FREE_IN_3D.v) {
+ if(workplane == Entity::FREE_IN_3D) {
r = gr; u = gu; n = gn;
} else {
r = SK.GetEntity(workplane)->Normal()->NormalU();
Vector b = SK.GetEntity(e->point[1])->PointGetNum();
Vector m = (a.ScaledBy(0.5)).Plus(b.ScaledBy(0.5));
Vector offset = (a.Minus(b)).Cross(n);
- offset = offset.WithMagnitude(13/SS.GW.scale);
+ offset = offset.WithMagnitude(textHeight);
// Draw midpoint constraint on other side of line, so that
// a line can be midpoint and horizontal at same time.
- if(type == AT_MIDPOINT) offset = offset.ScaledBy(-1);
-
- if(dogd.drawing) {
- const char *s = (type == HORIZONTAL) ? "H" : (
- (type == VERTICAL) ? "V" : (
- (type == AT_MIDPOINT) ? "M" : NULL));
-
- ssglWriteTextRefCenter(s, Style::DefaultTextHeight(),
- m.Plus(offset), r, u, LineCallback, this);
- } else {
- dogd.refp = m.Plus(offset);
- Point2d ref = SS.GW.ProjectPoint(dogd.refp);
- dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-10);
+ if(type == Type::AT_MIDPOINT) offset = offset.ScaledBy(-1);
+
+ std::string s;
+ switch(type) {
+ case Type::HORIZONTAL: s = "H"; break;
+ case Type::VERTICAL: s = "V"; break;
+ case Type::AT_MIDPOINT: s = "M"; break;
+ default: ssassert(false, "Unexpected constraint type");
}
+ Vector o = m.Plus(offset).Plus(u.WithMagnitude(textHeight/5)),
+ ex = VectorFont::Builtin()->GetExtents(textHeight, s);
+ Vector shift = r.WithMagnitude(ex.x).Plus(
+ u.WithMagnitude(ex.y));
+
+ canvas->DrawVectorText(s, textHeight, o.Minus(shift.ScaledBy(0.5)),
+ r.WithMagnitude(1), u.WithMagnitude(1), hcs);
+ if(refs) refs->push_back(o);
} else {
Vector a = SK.GetEntity(ptA)->PointGetNum();
Vector b = SK.GetEntity(ptB)->PointGetNum();
for(i = 0; i < 2; i++) {
Vector o = (i == 0) ? a : b;
Vector oo = (i == 0) ? a.Minus(b) : b.Minus(a);
- Vector d = (type == HORIZONTAL) ? cu : cv;
+ Vector d = (type == Type::HORIZONTAL) ? cu : cv;
if(oo.Dot(d) < 0) d = d.ScaledBy(-1);
Vector dp = cn.Cross(d);
- d = d.WithMagnitude(14/SS.GW.scale);
+ d = d.WithMagnitude(14/camera.scale);
Vector c = o.Minus(d);
- LineDrawOrGetDistance(o, c);
- d = d.WithMagnitude(3/SS.GW.scale);
- dp = dp.WithMagnitude(2/SS.GW.scale);
- if(dogd.drawing) {
- glBegin(GL_QUADS);
- ssglVertex3v((c.Plus(d)).Plus(dp));
- ssglVertex3v((c.Minus(d)).Plus(dp));
- ssglVertex3v((c.Minus(d)).Minus(dp));
- ssglVertex3v((c.Plus(d)).Minus(dp));
- glEnd();
- } else {
- Point2d ref = SS.GW.ProjectPoint(c);
- dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-6);
- }
+ DoLine(canvas, hcs, o, c);
+ d = d.WithMagnitude(3/camera.scale);
+ dp = dp.WithMagnitude(2/camera.scale);
+ canvas->DrawQuad((c.Plus(d)).Plus(dp),
+ (c.Minus(d)).Plus(dp),
+ (c.Minus(d)).Minus(dp),
+ (c.Plus(d)).Minus(dp),
+ hcf);
+ if(refs) refs->push_back(c);
}
}
- break;
+ return;
- case COMMENT: {
- if(dogd.drawing && disp.style.v) {
- ssglLineWidth(Style::Width(disp.style));
- ssglColorRGB(Style::Color(disp.style));
- }
+ case Type::COMMENT: {
Vector u, v;
- if(workplane.v == Entity::FREE_IN_3D.v) {
+ if(workplane == Entity::FREE_IN_3D) {
u = gr;
v = gu;
} else {
u = norm->NormalU();
v = norm->NormalV();
}
- DoLabel(disp.offset, labelPos, u, v);
- break;
- }
- default: oops();
+ if(disp.style.v != 0) {
+ RgbaColor color = stroke.color;
+ stroke = Style::Stroke(disp.style);
+ stroke.layer = Canvas::Layer::FRONT;
+ if(how != DrawAs::DEFAULT) {
+ stroke.color = color;
+ }
+ hcs = canvas->GetStroke(stroke);
+ }
+ DoLabel(canvas, hcs, disp.offset, labelPos, u, v);
+ if(refs) refs->push_back(disp.offset);
+ return;
+ }
}
+ ssassert(false, "Unexpected constraint type");
}
-void Constraint::Draw(void) {
- dogd.drawing = true;
- dogd.sel = NULL;
- hStyle hs = GetStyle();
-
- ssglLineWidth(Style::Width(hs));
- ssglColorRGB(Style::Color(hs));
-
- DrawOrGetDistance(NULL);
+void Constraint::Draw(DrawAs how, Canvas *canvas) {
+ DoLayout(how, canvas, NULL, NULL);
}
-double Constraint::GetDistance(Point2d mp) {
- dogd.drawing = false;
- dogd.sel = NULL;
- dogd.mp = mp;
- dogd.dmin = 1e12;
-
- DrawOrGetDistance(NULL);
-
- return dogd.dmin;
-}
-
-Vector Constraint::GetLabelPos(void) {
- dogd.drawing = false;
- dogd.sel = NULL;
- dogd.mp.x = 0; dogd.mp.y = 0;
- dogd.dmin = 1e12;
-
+Vector Constraint::GetLabelPos(const Camera &camera) {
Vector p;
- DrawOrGetDistance(&p);
- return p;
-}
-Vector Constraint::GetReferencePos(void) {
- dogd.drawing = false;
- dogd.sel = NULL;
+ ObjectPicker canvas = {};
+ canvas.camera = camera;
+ DoLayout(DrawAs::DEFAULT, &canvas, &p, NULL);
+ canvas.Clear();
- dogd.refp = SS.GW.offset.ScaledBy(-1);
- DrawOrGetDistance(NULL);
-
- return dogd.refp;
+ return p;
}
-void Constraint::GetEdges(SEdgeList *sel) {
- dogd.drawing = true;
- dogd.sel = sel;
- DrawOrGetDistance(NULL);
- dogd.sel = NULL;
+void Constraint::GetReferencePoints(const Camera &camera, std::vector<Vector> *refs) {
+ ObjectPicker canvas = {};
+ canvas.camera = camera;
+ DoLayout(DrawAs::DEFAULT, &canvas, NULL, refs);
+ canvas.Clear();
}
-bool Constraint::IsStylable() {
- if(type == COMMENT) return true;
+bool Constraint::IsStylable() const {
+ if(type == Type::COMMENT) return true;
return false;
}
return { Style::CONSTRAINT };
}
-bool Constraint::HasLabel() {
+bool Constraint::HasLabel() const {
switch(type) {
- case COMMENT:
- case PT_PT_DISTANCE:
- case PT_PLANE_DISTANCE:
- case PT_LINE_DISTANCE:
- case PT_FACE_DISTANCE:
- case PROJ_PT_DISTANCE:
- case LENGTH_RATIO:
- case LENGTH_DIFFERENCE:
- case DIAMETER:
- case ANGLE:
+ case Type::COMMENT:
+ case Type::PT_PT_DISTANCE:
+ case Type::PT_PLANE_DISTANCE:
+ case Type::PT_LINE_DISTANCE:
+ case Type::PT_FACE_DISTANCE:
+ case Type::PROJ_PT_DISTANCE:
+ case Type::LENGTH_RATIO:
+ case Type::LENGTH_DIFFERENCE:
+ case Type::DIAMETER:
+ case Type::ANGLE:
return true;
default:
//-----------------------------------------------------------------------------
#include "solvespace.h"
-std::string Entity::DescriptionString(void) {
+std::string Entity::DescriptionString() const {
if(h.isFromRequest()) {
Request *r = SK.GetRequest(h.request());
return r->DescriptionString();
}
}
-void Entity::LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat, int data) {
- if(dogd.drawing) {
- // Draw lines from active group in front of those from previous
- ssglDepthRangeOffset((group.v == SS.GW.activeGroup.v) ? 4 : 3);
- // Narrow lines are drawn as lines, but fat lines must be drawn as
- // filled polygons, to get the line join style right.
- ssglStippledLine(a, b, dogd.lineWidth, dogd.stippleType, dogd.stippleScale, maybeFat);
- ssglDepthRangeOffset(0);
- } else {
- Point2d ap = SS.GW.ProjectPoint(a);
- Point2d bp = SS.GW.ProjectPoint(b);
-
- double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true);
- // A little bit easier to select in the active group
- if(group.v == SS.GW.activeGroup.v) d -= 1;
- if(d < dogd.dmin) {
- dogd.dmin = d;
- dogd.data = data;
- }
- }
- dogd.refp = (a.Plus(b)).ScaledBy(0.5);
-}
-
-void Entity::DrawAll(bool drawAsHidden) {
- // This handles points as a special case, because I seem to be able
- // to get a huge speedup that way, by consolidating stuff to gl.
- int i;
- if(SS.GW.showPoints) {
- double s = 3.5/SS.GW.scale;
- Vector r = SS.GW.projRight.ScaledBy(s);
- Vector d = SS.GW.projUp.ScaledBy(s);
- ssglColorRGB(Style::Color(Style::DATUM));
- ssglDepthRangeOffset(6);
- glBegin(GL_QUADS);
- for(i = 0; i < SK.entity.n; i++) {
- Entity *e = &(SK.entity.elem[i]);
- if(!e->IsPoint()) continue;
- if(!(SK.GetGroup(e->group)->IsVisible())) continue;
- if(e->forceHidden) continue;
-
- Vector v = e->PointGetNum();
-
- // If we're analyzing the sketch to show the degrees of freedom,
- // then we draw big colored squares over the points that are
- // free to move.
- bool free = false;
- if(e->type == POINT_IN_3D) {
- Param *px = SK.GetParam(e->param[0]),
- *py = SK.GetParam(e->param[1]),
- *pz = SK.GetParam(e->param[2]);
-
- free = (px->free) || (py->free) || (pz->free);
- } else if(e->type == POINT_IN_2D) {
- Param *pu = SK.GetParam(e->param[0]),
- *pv = SK.GetParam(e->param[1]);
-
- free = (pu->free) || (pv->free);
- }
- if(free) {
- Vector re = r.ScaledBy(2.5), de = d.ScaledBy(2.5);
-
- ssglColorRGB(Style::Color(Style::ANALYZE));
- ssglVertex3v(v.Plus (re).Plus (de));
- ssglVertex3v(v.Plus (re).Minus(de));
- ssglVertex3v(v.Minus(re).Minus(de));
- ssglVertex3v(v.Minus(re).Plus (de));
- ssglColorRGB(Style::Color(Style::DATUM));
- }
-
- ssglVertex3v(v.Plus (r).Plus (d));
- ssglVertex3v(v.Plus (r).Minus(d));
- ssglVertex3v(v.Minus(r).Minus(d));
- ssglVertex3v(v.Minus(r).Plus (d));
- }
- glEnd();
- ssglDepthRangeOffset(0);
- }
-
- for(i = 0; i < SK.entity.n; i++) {
- Entity *e = &(SK.entity.elem[i]);
- if(e->IsPoint()) {
- continue; // already handled
- }
- e->Draw(drawAsHidden);
- }
-}
-
-void Entity::Draw(bool drawAsHidden) {
- hStyle hs = Style::ForEntity(h);
- dogd.lineWidth = Style::Width(hs);
- if(drawAsHidden) {
- dogd.stippleType = Style::PatternType({ Style::HIDDEN_EDGE });
- dogd.stippleScale = Style::StippleScaleMm({ Style::HIDDEN_EDGE });
- } else {
- dogd.stippleType = Style::PatternType(hs);
- dogd.stippleScale = Style::StippleScaleMm(hs);
- }
- ssglLineWidth((float)dogd.lineWidth);
- ssglColorRGB(Style::Color(hs));
-
- dogd.drawing = true;
- DrawOrGetDistance();
-}
-
-void Entity::GenerateEdges(SEdgeList *el, bool includingConstruction) {
- if(construction && !includingConstruction) return;
-
+void Entity::GenerateEdges(SEdgeList *el) {
SBezierList *sbl = GetOrGenerateBezierCurves();
- int i, j;
- for(i = 0; i < sbl->l.n; i++) {
- SBezier *sb = &(sbl->l.elem[i]);
+ for(int i = 0; i < sbl->l.n; i++) {
+ SBezier *sb = &(sbl->l[i]);
List<Vector> lv = {};
sb->MakePwlInto(&lv);
- for(j = 1; j < lv.n; j++) {
- el->AddEdge(lv.elem[j-1], lv.elem[j], style.v, i);
+ for(int j = 1; j < lv.n; j++) {
+ el->AddEdge(lv[j-1], lv[j], style.v, i);
}
lv.Clear();
}
-
}
SBezierList *Entity::GetOrGenerateBezierCurves() {
- if(beziers.l.n == 0)
+ if(beziers.l.IsEmpty())
GenerateBezierCurves(&beziers);
return &beziers;
}
SEdgeList *Entity::GetOrGenerateEdges() {
- if(edges.l.n != 0) {
+ if(!edges.l.IsEmpty()) {
if(EXACT(edgesChordTol == SS.ChordTolMm()))
return &edges;
edges.l.Clear();
}
- if(edges.l.n == 0)
- GenerateEdges(&edges, /*includingConstruction=*/true);
+ if(edges.l.IsEmpty())
+ GenerateEdges(&edges);
edgesChordTol = SS.ChordTolMm();
return &edges;
}
BBox Entity::GetOrGenerateScreenBBox(bool *hasBBox) {
SBezierList *sbl = GetOrGenerateBezierCurves();
- // We don't bother with bounding boxes for normals, workplanes, etc.
- *hasBBox = (IsPoint() || sbl->l.n > 0);
+ // We don't bother with bounding boxes for workplanes, etc.
+ *hasBBox = (IsPoint() || IsNormal() || !sbl->l.IsEmpty());
if(!*hasBBox) return {};
if(screenBBoxValid)
if(IsPoint()) {
Vector proj = SS.GW.ProjectPoint3(PointGetNum());
screenBBox = BBox::From(proj, proj);
- } else if(sbl->l.n > 0) {
- Vector first = SS.GW.ProjectPoint3(sbl->l.elem[0].ctrl[0]);
+ } else if(IsNormal()) {
+ Vector proj = SK.GetEntity(point[0])->PointGetNum();
+ screenBBox = BBox::From(proj, proj);
+ } else if(!sbl->l.IsEmpty()) {
+ Vector first = SS.GW.ProjectPoint3(sbl->l[0].ctrl[0]);
screenBBox = BBox::From(first, first);
- for(int i = 0; i < sbl->l.n; i++) {
- SBezier *sb = &sbl->l.elem[i];
- for(int i = 0; i <= sb->deg; i++) {
- screenBBox.Include(SS.GW.ProjectPoint3(sb->ctrl[i]));
- }
+ for(auto &sb : sbl->l) {
+ for(int i = 0; i < sb.deg; ++i) { screenBBox.Include(SS.GW.ProjectPoint3(sb.ctrl[i])); }
}
- } else oops();
-
- // Enlarge the bounding box to consider selection radius.
- screenBBox.minp.x -= SELECTION_RADIUS;
- screenBBox.minp.y -= SELECTION_RADIUS;
- screenBBox.maxp.x += SELECTION_RADIUS;
- screenBBox.maxp.y += SELECTION_RADIUS;
+ } else
+ ssassert(false, "Expected entity to be a point or have beziers");
screenBBoxValid = true;
return screenBBox;
}
-double Entity::GetDistance(Point2d mp) {
- dogd.drawing = false;
- dogd.mp = mp;
- dogd.dmin = 1e12;
+void Entity::GetReferencePoints(std::vector<Vector> *refs) {
+ switch(type) {
+ case Type::POINT_N_COPY:
+ case Type::POINT_N_TRANS:
+ case Type::POINT_N_ROT_TRANS:
+ case Type::POINT_N_ROT_AA:
+ case Type::POINT_N_ROT_AXIS_TRANS:
+ case Type::POINT_IN_3D:
+ case Type::POINT_IN_2D:
+ refs->push_back(PointGetNum());
+ break;
+
+ case Type::NORMAL_N_COPY:
+ case Type::NORMAL_N_ROT:
+ case Type::NORMAL_N_ROT_AA:
+ case Type::NORMAL_IN_3D:
+ case Type::NORMAL_IN_2D:
+ case Type::WORKPLANE:
+ case Type::CIRCLE:
+ case Type::ARC_OF_CIRCLE:
+ case Type::CUBIC:
+ case Type::CUBIC_PERIODIC:
+ case Type::TTF_TEXT:
+ case Type::IMAGE:
+ refs->push_back(SK.GetEntity(point[0])->PointGetNum());
+ break;
- DrawOrGetDistance();
+ case Type::LINE_SEGMENT: {
+ Vector a = SK.GetEntity(point[0])->PointGetNum(),
+ b = SK.GetEntity(point[1])->PointGetNum();
+ refs->push_back(b.Plus(a.Minus(b).ScaledBy(0.5)));
+ break;
+ }
- return dogd.dmin;
+ case Type::DISTANCE:
+ case Type::DISTANCE_N_COPY:
+ case Type::FACE_NORMAL_PT:
+ case Type::FACE_XPROD:
+ case Type::FACE_N_ROT_TRANS:
+ case Type::FACE_N_TRANS:
+ case Type::FACE_N_ROT_AA:
+ case Type::FACE_ROT_NORMAL_PT:
+ case Type::FACE_N_ROT_AXIS_TRANS:
+ break;
+ }
}
-Vector Entity::GetReferencePos(void) {
- dogd.drawing = false;
+int Entity::GetPositionOfPoint(const Camera &camera, Point2d p) {
+ int position;
- dogd.refp = SS.GW.offset.ScaledBy(-1);
- DrawOrGetDistance();
+ ObjectPicker canvas = {};
+ canvas.camera = camera;
+ canvas.point = p;
+ canvas.minDistance = 1e12;
+ Draw(DrawAs::DEFAULT, &canvas);
+ position = canvas.position;
+ canvas.Clear();
- return dogd.refp;
+ return position;
}
-bool Entity::IsStylable() {
+bool Entity::IsStylable() const {
if(IsPoint()) return false;
if(IsWorkplane()) return false;
if(IsNormal()) return false;
return true;
}
-bool Entity::IsVisible(void) {
+bool Entity::IsVisible() const {
Group *g = SK.GetGroup(group);
- if(g->h.v == Group::HGROUP_REFERENCES.v && IsNormal()) {
+ if(g->h == Group::HGROUP_REFERENCES && IsNormal()) {
// The reference normals are always shown
return true;
}
if(!(g->IsVisible())) return false;
- // Don't check if points are hidden; this gets called only for
- // selected or hovered points, and those should always be shown.
+ if(IsPoint() && !SS.GW.showPoints) return false;
if(IsNormal() && !SS.GW.showNormals) return false;
+ if(construction && !SS.GW.showConstruction) return false;
if(!SS.GW.showWorkplanes) {
if(IsWorkplane() && !h.isFromRequest()) {
- if(g->h.v != SS.GW.activeGroup.v) {
+ if(g->h != SS.GW.activeGroup) {
// The group-associated workplanes are hidden outside
// their group.
return false;
return true;
}
+// entities that were created via some copy types will not be
+// draggable with the mouse. We identify the undraggables here
+bool Entity::CanBeDragged() const {
+ // a numeric copy can not move
+ if(type == Entity::Type::POINT_N_COPY) return false;
+ // these transforms applied zero times can not be moved
+ if(((type == Entity::Type::POINT_N_TRANS) ||
+ (type == Entity::Type::POINT_N_ROT_AA) ||
+ (type == Entity::Type::POINT_N_ROT_AXIS_TRANS))
+ && (timesApplied == 0)) return false;
+ // for these types of entities the first point will indicate draggability
+ if(HasEndpoints() || type == Entity::Type::CIRCLE) {
+ return SK.GetEntity(point[0])->CanBeDragged();
+ }
+ // if we're not certain it can't be dragged then default to true
+ return true;
+}
+
void Entity::CalculateNumerical(bool forExport) {
if(IsPoint()) actPoint = PointGetNum();
if(IsNormal()) actNormal = NormalGetNum();
- if(type == DISTANCE || type == DISTANCE_N_COPY) {
+ if(type == Type::DISTANCE || type == Type::DISTANCE_N_COPY) {
actDistance = DistanceGetNum();
}
if(IsFace()) {
}
}
-bool Entity::PointIsFromReferences(void) {
- return h.request().IsFromReferences();
-}
-
//-----------------------------------------------------------------------------
// Compute a cubic, second derivative continuous, interpolating spline. Same
// routine for periodic splines (in a loop) or open splines (with specified
// end tangents).
//-----------------------------------------------------------------------------
-void Entity::ComputeInterpolatingSpline(SBezierList *sbl, bool periodic) {
+void Entity::ComputeInterpolatingSpline(SBezierList *sbl, bool periodic) const {
static const int MAX_N = BandedMatrix::MAX_UNKNOWNS;
int ep = extraPoints;
// The number of unknowns to solve for.
int n = periodic ? 3 + ep : ep;
- if(n >= MAX_N) oops();
+ ssassert(n < MAX_N, "Too many unknowns");
// The number of on-curve points, one more than the number of segments.
int pts = periodic ? 4 + ep : 2 + ep;
} else {
// The wrapping would work, except when n = 1 and everything
// wraps to zero...
- if(i > 0) bm.A[i][i - 1] = eq.x;
- bm.A[i][i] = eq.y;
- if(i < (n-1)) bm.A[i][i + 1] = eq.z;
+ if(i > 0) {
+ bm.A[i][i - 1] = eq.x;
+ }
+ bm.A[i][i] = eq.y;
+ if(i < (n-1)) {
+ bm.A[i][i + 1] = eq.z;
+ }
}
}
bm.Solve();
}
}
-void Entity::GenerateBezierCurves(SBezierList *sbl) {
+void Entity::GenerateBezierCurves(SBezierList *sbl) const {
SBezier sb;
int i = sbl->l.n;
switch(type) {
- case LINE_SEGMENT: {
+ case Type::LINE_SEGMENT: {
Vector a = SK.GetEntity(point[0])->PointGetNum();
Vector b = SK.GetEntity(point[1])->PointGetNum();
sb = SBezier::From(a, b);
sbl->l.Add(&sb);
break;
}
- case CUBIC:
- ComputeInterpolatingSpline(sbl, false);
+ case Type::CUBIC:
+ ComputeInterpolatingSpline(sbl, /*periodic=*/false);
break;
- case CUBIC_PERIODIC:
- ComputeInterpolatingSpline(sbl, true);
+ case Type::CUBIC_PERIODIC:
+ ComputeInterpolatingSpline(sbl, /*periodic=*/true);
break;
- case CIRCLE:
- case ARC_OF_CIRCLE: {
+ case Type::CIRCLE:
+ case Type::ARC_OF_CIRCLE: {
Vector center = SK.GetEntity(point[0])->PointGetNum();
Quaternion q = SK.GetEntity(normal)->NormalGetNum();
Vector u = q.RotationU(), v = q.RotationV();
break;
}
- if(type == CIRCLE) {
+ if(type == Type::CIRCLE) {
thetaa = 0;
thetab = 2*PI;
dtheta = 2*PI;
break;
}
- case TTF_TEXT: {
+ case Type::TTF_TEXT: {
Vector topLeft = SK.GetEntity(point[0])->PointGetNum();
Vector botLeft = SK.GetEntity(point[1])->PointGetNum();
Vector n = Normal()->NormalN();
// Record our style for all of the Beziers that we just created.
for(; i < sbl->l.n; i++) {
- sbl->l.elem[i].auxA = style.v;
+ sbl->l[i].auxA = style.v;
}
}
-void Entity::DrawOrGetDistance(void) {
- // If we're about to perform hit testing on an entity, consider
- // whether the pointer is inside its bounding box first.
- if(!dogd.drawing) {
- bool hasBBox;
- BBox box = GetOrGenerateScreenBBox(&hasBBox);
- if(hasBBox && !box.Contains(dogd.mp))
- return;
+void Entity::Draw(DrawAs how, Canvas *canvas) {
+ if(!(how == DrawAs::HOVERED || how == DrawAs::SELECTED) &&
+ !IsVisible()) return;
+
+ int zIndex;
+ if(IsPoint()) {
+ zIndex = 5;
+ } else if(how == DrawAs::HIDDEN) {
+ zIndex = 2;
+ } else if(group != SS.GW.activeGroup) {
+ zIndex = 3;
+ } else {
+ zIndex = 4;
}
- if(!IsVisible()) return;
+ hStyle hs;
+ if(IsPoint()) {
+ hs.v = Style::DATUM;
+ } else if(IsNormal() || type == Type::WORKPLANE) {
+ hs.v = Style::NORMALS;
+ } else {
+ hs = Style::ForEntity(h);
+ }
+
+ Canvas::Stroke stroke = Style::Stroke(hs);
+ switch(how) {
+ case DrawAs::DEFAULT:
+ stroke.layer = Canvas::Layer::NORMAL;
+ break;
+
+ case DrawAs::OVERLAY:
+ stroke.layer = Canvas::Layer::FRONT;
+ break;
+
+ case DrawAs::HIDDEN:
+ stroke.layer = Canvas::Layer::OCCLUDED;
+ stroke.stipplePattern = Style::PatternType({ Style::HIDDEN_EDGE });
+ stroke.stippleScale = Style::Get({ Style::HIDDEN_EDGE })->stippleScale;
+ break;
+
+ case DrawAs::HOVERED:
+ stroke.layer = Canvas::Layer::FRONT;
+ stroke.color = Style::Color(Style::HOVERED);
+ break;
+
+ case DrawAs::SELECTED:
+ stroke.layer = Canvas::Layer::FRONT;
+ stroke.color = Style::Color(Style::SELECTED);
+ break;
+ }
+ stroke.zIndex = zIndex;
+ Canvas::hStroke hcs = canvas->GetStroke(stroke);
switch(type) {
- case POINT_N_COPY:
- case POINT_N_TRANS:
- case POINT_N_ROT_TRANS:
- case POINT_N_ROT_AA:
- case POINT_IN_3D:
- case POINT_IN_2D: {
- Vector v = PointGetNum();
- dogd.refp = v;
-
- if(dogd.drawing) {
- double s = 3.5;
- Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale);
- Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale);
-
- ssglColorRGB(Style::Color(Style::DATUM));
- ssglDepthRangeOffset(6);
- glBegin(GL_QUADS);
- ssglVertex3v(v.Plus (r).Plus (d));
- ssglVertex3v(v.Plus (r).Minus(d));
- ssglVertex3v(v.Minus(r).Minus(d));
- ssglVertex3v(v.Minus(r).Plus (d));
- glEnd();
- ssglDepthRangeOffset(0);
- } else {
- Point2d pp = SS.GW.ProjectPoint(v);
- dogd.dmin = pp.DistanceTo(dogd.mp) - 6;
+ case Type::POINT_N_COPY:
+ case Type::POINT_N_TRANS:
+ case Type::POINT_N_ROT_TRANS:
+ case Type::POINT_N_ROT_AA:
+ case Type::POINT_N_ROT_AXIS_TRANS:
+ case Type::POINT_IN_3D:
+ case Type::POINT_IN_2D: {
+ if(how == DrawAs::HIDDEN) return;
+
+ // If we're analyzing the sketch to show the degrees of freedom,
+ // then we draw big colored squares over the points that are
+ // free to move.
+ bool free = false;
+ if(type == Type::POINT_IN_3D) {
+ Param *px = SK.GetParam(param[0]),
+ *py = SK.GetParam(param[1]),
+ *pz = SK.GetParam(param[2]);
+
+ free = px->free || py->free || pz->free;
+ } else if(type == Type::POINT_IN_2D) {
+ Param *pu = SK.GetParam(param[0]),
+ *pv = SK.GetParam(param[1]);
+
+ free = pu->free || pv->free;
}
- break;
+
+ Canvas::Stroke pointStroke = {};
+ pointStroke.layer = (free) ? Canvas::Layer::FRONT : stroke.layer;
+ pointStroke.zIndex = stroke.zIndex;
+ pointStroke.color = stroke.color;
+ pointStroke.width = 7.0;
+ pointStroke.unit = Canvas::Unit::PX;
+ Canvas::hStroke hcsPoint = canvas->GetStroke(pointStroke);
+
+ if(free) {
+ Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
+ analyzeStroke.width = 14.0;
+ analyzeStroke.layer = Canvas::Layer::FRONT;
+ Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
+
+ canvas->DrawPoint(PointGetNum(), hcsAnalyze);
+ }
+
+ canvas->DrawPoint(PointGetNum(), hcsPoint);
+ return;
}
- case NORMAL_N_COPY:
- case NORMAL_N_ROT:
- case NORMAL_N_ROT_AA:
- case NORMAL_IN_3D:
- case NORMAL_IN_2D: {
- int i;
- for(i = 0; i < 2; i++) {
- if(i == 0 && !SS.GW.showNormals) {
- // When the normals are hidden, we will continue to show
- // the coordinate axes at the bottom left corner, but
- // not at the origin.
- continue;
- }
+ case Type::NORMAL_N_COPY:
+ case Type::NORMAL_N_ROT:
+ case Type::NORMAL_N_ROT_AA:
+ case Type::NORMAL_IN_3D:
+ case Type::NORMAL_IN_2D: {
+ const Camera &camera = canvas->GetCamera();
+
+ if(how == DrawAs::HIDDEN) return;
- hRequest hr = h.request();
- // Always draw the x, y, and z axes in red, green, and blue;
- // brighter for the ones at the bottom left of the screen,
- // dimmer for the ones at the model origin.
- int f = (i == 0 ? 100 : 255);
- if(hr.v == Request::HREQUEST_REFERENCE_XY.v) {
- if(dogd.drawing)
- ssglColorRGB(RGBi(0, 0, f));
- } else if(hr.v == Request::HREQUEST_REFERENCE_YZ.v) {
- if(dogd.drawing)
- ssglColorRGB(RGBi(f, 0, 0));
- } else if(hr.v == Request::HREQUEST_REFERENCE_ZX.v) {
- if(dogd.drawing)
- ssglColorRGB(RGBi(0, f, 0));
+ for(int i = 0; i < 2; i++) {
+ bool asReference = (i == 1);
+ if(asReference) {
+ if(!h.request().IsFromReferences()) continue;
} else {
- if(dogd.drawing)
- ssglColorRGB(Style::Color(Style::NORMALS));
- if(i > 0) break;
+ if(!SK.GetGroup(group)->IsVisible() || !SS.GW.showNormals) continue;
+ }
+
+ stroke.layer = (asReference) ? Canvas::Layer::FRONT : Canvas::Layer::NORMAL;
+ if(how != DrawAs::HOVERED && how != DrawAs::SELECTED) {
+ // Always draw the x, y, and z axes in red, green, and blue;
+ // brighter for the ones at the bottom left of the screen,
+ // dimmer for the ones at the model origin.
+ hRequest hr = h.request();
+ uint8_t luma = (asReference) ? 255 : 100;
+ if(hr == Request::HREQUEST_REFERENCE_XY) {
+ stroke.color = RgbaColor::From(0, 0, luma);
+ } else if(hr == Request::HREQUEST_REFERENCE_YZ) {
+ stroke.color = RgbaColor::From(luma, 0, 0);
+ } else if(hr == Request::HREQUEST_REFERENCE_ZX) {
+ stroke.color = RgbaColor::From(0, luma, 0);
+ }
}
Quaternion q = NormalGetNum();
Vector tail;
- if(i == 0) {
- tail = SK.GetEntity(point[0])->PointGetNum();
- if(dogd.drawing)
- ssglLineWidth(1);
- } else {
+ if(asReference) {
// Draw an extra copy of the x, y, and z axes, that's
// always in the corner of the view and at the front.
// So those are always available, perhaps useful.
- double s = SS.GW.scale;
- double h = 60 - SS.GW.height/2;
- double w = 60 - SS.GW.width/2;
- tail = SS.GW.projRight.ScaledBy(w/s).Plus(
- SS.GW.projUp. ScaledBy(h/s)).Minus(SS.GW.offset);
- if(dogd.drawing) {
- ssglDepthRangeLockToFront(true);
- ssglLineWidth(2);
+ stroke.width = 2;
+ double s = camera.scale;
+ double h = 60 - camera.height / 2.0;
+ double w = 60 - camera.width / 2.0;
+ // Shift the axis to the right if they would overlap with the toolbar.
+ if(SS.showToolbar) {
+ if(h + 30 > -(34*16 + 3*16 + 8) / 2)
+ w += 60;
}
+ tail = camera.projRight.ScaledBy(w/s).Plus(
+ camera.projUp. ScaledBy(h/s)).Minus(camera.offset);
+ } else {
+ tail = SK.GetEntity(point[0])->PointGetNum();
}
+ tail = camera.AlignToPixelGrid(tail);
- Vector v = (q.RotationN()).WithMagnitude(50/SS.GW.scale);
+ hcs = canvas->GetStroke(stroke);
+ Vector v = (q.RotationN()).WithMagnitude(50.0 / camera.scale);
Vector tip = tail.Plus(v);
- LineDrawOrGetDistance(tail, tip);
+ canvas->DrawLine(tail, tip, hcs);
- v = v.WithMagnitude(12/SS.GW.scale);
+ v = v.WithMagnitude(12.0 / camera.scale);
Vector axis = q.RotationV();
- LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis, 0.6)));
- LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis,-0.6)));
+ canvas->DrawLine(tip, tip.Minus(v.RotatedAbout(axis, 0.6)), hcs);
+ canvas->DrawLine(tip, tip.Minus(v.RotatedAbout(axis, -0.6)), hcs);
+
+ if(type == Type::NORMAL_IN_3D) {
+ Param *nw = SK.GetParam(param[0]),
+ *nx = SK.GetParam(param[1]),
+ *ny = SK.GetParam(param[2]),
+ *nz = SK.GetParam(param[3]);
+
+ if(nw->free || nx->free || ny->free || nz->free) {
+ Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
+ analyzeStroke.layer = Canvas::Layer::FRONT;
+ Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
+ canvas->DrawLine(tail, tip, hcsAnalyze);
+ }
+ }
}
- if(dogd.drawing)
- ssglDepthRangeLockToFront(false);
- break;
+ return;
}
- case DISTANCE:
- case DISTANCE_N_COPY:
+ case Type::DISTANCE:
+ case Type::DISTANCE_N_COPY:
// These are used only as data structures, nothing to display.
- break;
+ return;
+
+ case Type::WORKPLANE: {
+ const Camera &camera = canvas->GetCamera();
- case WORKPLANE: {
- Vector p;
- p = SK.GetEntity(point[0])->PointGetNum();
+ Vector p = SK.GetEntity(point[0])->PointGetNum();
+ p = camera.AlignToPixelGrid(p);
Vector u = Normal()->NormalU();
Vector v = Normal()->NormalV();
- double s = (min(SS.GW.width, SS.GW.height))*0.45/SS.GW.scale;
+ double s = (std::min(camera.width, camera.height)) * 0.45 / camera.scale;
Vector us = u.ScaledBy(s);
Vector vs = v.ScaledBy(s);
Vector mm = p.Minus(us).Minus(vs), mm2 = mm;
Vector mp = p.Minus(us).Plus (vs);
- if(dogd.drawing) {
- ssglLineWidth(1);
- ssglColorRGB(Style::Color(Style::NORMALS));
- glEnable(GL_LINE_STIPPLE);
- glLineStipple(3, 0x1111);
- }
+ Canvas::Stroke strokeBorder = stroke;
+ strokeBorder.zIndex -= 3;
+ strokeBorder.stipplePattern = StipplePattern::SHORT_DASH;
+ strokeBorder.stippleScale = 8.0;
+ Canvas::hStroke hcsBorder = canvas->GetStroke(strokeBorder);
+
+ double textHeight = Style::TextHeight(hs) / camera.scale;
if(!h.isFromRequest()) {
- mm = mm.Plus(v.ScaledBy(70/SS.GW.scale));
- mm2 = mm2.Plus(u.ScaledBy(70/SS.GW.scale));
- LineDrawOrGetDistance(mm2, mm);
+ mm = mm.Plus(v.ScaledBy(textHeight * 4.7));
+ mm2 = mm2.Plus(u.ScaledBy(textHeight * 4.7));
+ canvas->DrawLine(mm2, mm, hcsBorder);
}
- LineDrawOrGetDistance(pp, pm);
- LineDrawOrGetDistance(pm, mm2);
- LineDrawOrGetDistance(mp, mm);
- LineDrawOrGetDistance(pp, mp);
-
- if(dogd.drawing)
- glDisable(GL_LINE_STIPPLE);
-
- std::string str = DescriptionString().substr(5);
- double th = Style::DefaultTextHeight();
- if(dogd.drawing) {
- Vector o = mm2.Plus(u.ScaledBy(3/SS.GW.scale)).Plus(
- v.ScaledBy(3/SS.GW.scale));
- ssglWriteText(str, th, o, u, v, NULL, NULL);
- } else {
- Vector pos = mm2.Plus(u.ScaledBy(ssglStrWidth(str, th)/2)).Plus(
- v.ScaledBy(ssglStrCapHeight(th)/2));
- Point2d pp = SS.GW.ProjectPoint(pos);
- dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 10);
- // If a line lies in a plane, then select the line, not
- // the plane.
- dogd.dmin += 3;
+ canvas->DrawLine(pp, pm, hcsBorder);
+ canvas->DrawLine(mm2, pm, hcsBorder);
+ canvas->DrawLine(mm, mp, hcsBorder);
+ canvas->DrawLine(pp, mp, hcsBorder);
+
+ Vector o = mm2.Plus(u.ScaledBy(3.0 / camera.scale)).Plus(
+ v.ScaledBy(3.0 / camera.scale));
+ std::string shortDesc = DescriptionString().substr(5);
+ canvas->DrawVectorText(shortDesc, textHeight, o, u, v, hcs);
+ return;
+ }
+
+ case Type::LINE_SEGMENT:
+ case Type::CIRCLE:
+ case Type::ARC_OF_CIRCLE:
+ case Type::CUBIC:
+ case Type::CUBIC_PERIODIC:
+ case Type::TTF_TEXT: {
+ // Generate the rational polynomial curves, then piecewise linearize
+ // them, and display those.
+ if(!canvas->DrawBeziers(*GetOrGenerateBezierCurves(), hcs)) {
+ canvas->DrawEdges(*GetOrGenerateEdges(), hcs);
}
- break;
+ if(type == Type::CIRCLE) {
+ Entity *dist = SK.GetEntity(distance);
+ if(dist->type == Type::DISTANCE) {
+ Param *p = SK.GetParam(dist->param[0]);
+ if(p->free) {
+ Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
+ analyzeStroke.layer = Canvas::Layer::FRONT;
+ Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
+ if(!canvas->DrawBeziers(*GetOrGenerateBezierCurves(), hcsAnalyze)) {
+ canvas->DrawEdges(*GetOrGenerateEdges(), hcsAnalyze);
+ }
+ }
+ }
+ }
+ return;
}
+ case Type::IMAGE: {
+ Canvas::Fill fill = {};
+ std::shared_ptr<Pixmap> pixmap;
+ switch(how) {
+ case DrawAs::HIDDEN: return;
+
+ case DrawAs::HOVERED: {
+ fill.color = Style::Color(Style::HOVERED).WithAlpha(180);
+ fill.pattern = Canvas::FillPattern::CHECKERED_A;
+ fill.zIndex = 2;
+ break;
+ }
- case LINE_SEGMENT:
- case CIRCLE:
- case ARC_OF_CIRCLE:
- case CUBIC:
- case CUBIC_PERIODIC:
- case TTF_TEXT:
- // Nothing but the curve(s).
- break;
+ case DrawAs::SELECTED: {
+ fill.color = Style::Color(Style::SELECTED).WithAlpha(180);
+ fill.pattern = Canvas::FillPattern::CHECKERED_B;
+ fill.zIndex = 1;
+ break;
+ }
- case FACE_NORMAL_PT:
- case FACE_XPROD:
- case FACE_N_ROT_TRANS:
- case FACE_N_TRANS:
- case FACE_N_ROT_AA:
- // Do nothing; these are drawn with the triangle mesh
- break;
+ default:
+ fill.color = RgbaColor::FromFloat(1.0f, 1.0f, 1.0f);
+ pixmap = SS.images[file];
+ break;
+ }
- default:
- oops();
- }
+ Canvas::hFill hf = canvas->GetFill(fill);
+ Vector v[4] = {};
+ for(int i = 0; i < 4; i++) {
+ v[i] = SK.GetEntity(point[i])->PointGetNum();
+ }
+ Vector iu = v[3].Minus(v[0]);
+ Vector iv = v[1].Minus(v[0]);
+
+ if(how == DrawAs::DEFAULT && pixmap == NULL) {
+ Canvas::Stroke stroke = Style::Stroke(Style::DRAW_ERROR);
+ stroke.color = stroke.color.WithAlpha(50);
+ Canvas::hStroke hs = canvas->GetStroke(stroke);
+ canvas->DrawLine(v[0], v[2], hs);
+ canvas->DrawLine(v[1], v[3], hs);
+ for(int i = 0; i < 4; i++) {
+ canvas->DrawLine(v[i], v[(i + 1) % 4], hs);
+ }
+ } else {
+ canvas->DrawPixmap(pixmap, v[0], iu, iv,
+ Point2d::From(0.0, 0.0), Point2d::From(1.0, 1.0), hf);
+ }
+ }
- // And draw the curves; generate the rational polynomial curves for
- // everything, then piecewise linearize them, and display those.
- SEdgeList *sel = GetOrGenerateEdges();
- dogd.data = -1;
- for(int i = 0; i < sel->l.n; i++) {
- SEdge *se = &(sel->l.elem[i]);
- LineDrawOrGetDistance(se->a, se->b, true, se->auxB);
+ case Type::FACE_NORMAL_PT:
+ case Type::FACE_XPROD:
+ case Type::FACE_N_ROT_TRANS:
+ case Type::FACE_N_TRANS:
+ case Type::FACE_N_ROT_AA:
+ case Type::FACE_ROT_NORMAL_PT:
+ case Type::FACE_N_ROT_AXIS_TRANS:
+ // Do nothing; these are drawn with the triangle mesh
+ return;
}
+ ssassert(false, "Unexpected entity type");
}
-
//
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
-#ifndef __DSC_H
-#define __DSC_H
+#ifndef SOLVESPACE_DSC_H
+#define SOLVESPACE_DSC_H
#include "solvespace.h"
+#include <type_traits>
+
+/// Trait indicating which types are handle types and should get the associated operators.
+/// Specialize for each handle type and inherit from std::true_type.
+template<typename T>
+struct IsHandleOracle : std::false_type {};
+
+// Equality-compare any two instances of a handle type.
+template<typename T>
+static inline typename std::enable_if<IsHandleOracle<T>::value, bool>::type
+operator==(T const &lhs, T const &rhs) {
+ return lhs.v == rhs.v;
+}
+
+// Inequality-compare any two instances of a handle type.
+template<typename T>
+static inline typename std::enable_if<IsHandleOracle<T>::value, bool>::type
+operator!=(T const &lhs, T const &rhs) {
+ return !(lhs == rhs);
+}
+
+// Less-than-compare any two instances of a handle type.
+template<typename T>
+static inline typename std::enable_if<IsHandleOracle<T>::value, bool>::type
+operator<(T const &lhs, T const &rhs) {
+ return lhs.v < rhs.v;
+}
+
class Vector;
class Vector4;
class Point2d;
static Quaternion From(Vector u, Vector v);
static Quaternion From(Vector axis, double dtheta);
- Quaternion Plus(Quaternion b);
- Quaternion Minus(Quaternion b);
- Quaternion ScaledBy(double s);
- double Magnitude(void);
- Quaternion WithMagnitude(double s);
+ Quaternion Plus(Quaternion b) const;
+ Quaternion Minus(Quaternion b) const;
+ Quaternion ScaledBy(double s) const;
+ double Magnitude() const;
+ Quaternion WithMagnitude(double s) const;
// Call a rotation matrix [ u' v' n' ]'; this returns the first and
// second rows, where that matrix is generated by this quaternion
- Vector RotationU(void);
- Vector RotationV(void);
- Vector RotationN(void);
- Vector Rotate(Vector p);
-
- Quaternion ToThe(double p);
- Quaternion Inverse(void);
- Quaternion Times(Quaternion b);
- Quaternion Mirror(void);
+ Vector RotationU() const;
+ Vector RotationV() const;
+ Vector RotationN() const;
+ Vector Rotate(Vector p) const;
+
+ Quaternion ToThe(double p) const;
+ Quaternion Inverse() const;
+ Quaternion Times(Quaternion b) const;
+ Quaternion Mirror() const;
};
class Vector {
Vector pb, Vector db,
double *ta, double *tb);
- double Element(int i);
- bool Equals(Vector v, double tol=LENGTH_EPS);
- bool EqualsExactly(Vector v);
- Vector Plus(Vector b);
- Vector Minus(Vector b);
- Vector Negated(void);
- Vector Cross(Vector b);
- double DirectionCosineWith(Vector b);
- double Dot(Vector b);
- Vector Normal(int which);
- Vector RotatedAbout(Vector orig, Vector axis, double theta);
- Vector RotatedAbout(Vector axis, double theta);
- Vector DotInToCsys(Vector u, Vector v, Vector n);
- Vector ScaleOutOfCsys(Vector u, Vector v, Vector n);
- double DistanceToLine(Vector p0, Vector dp);
- bool OnLineSegment(Vector a, Vector b, double tol=LENGTH_EPS);
- Vector ClosestPointOnLine(Vector p0, Vector dp);
- double Magnitude(void);
- double MagSquared(void);
- Vector WithMagnitude(double s);
- Vector ScaledBy(double s);
- Vector ProjectInto(hEntity wrkpl);
- Vector ProjectVectorInto(hEntity wrkpl);
- double DivPivoting(Vector delta);
- Vector ClosestOrtho(void);
- void MakeMaxMin(Vector *maxv, Vector *minv);
- Vector ClampWithin(double minv, double maxv);
+ double Element(int i) const;
+ bool Equals(Vector v, double tol=LENGTH_EPS) const;
+ bool EqualsExactly(Vector v) const;
+ Vector Plus(Vector b) const;
+ Vector Minus(Vector b) const;
+ Vector Negated() const;
+ Vector Cross(Vector b) const;
+ double DirectionCosineWith(Vector b) const;
+ double Dot(Vector b) const;
+ Vector Normal(int which) const;
+ Vector RotatedAbout(Vector orig, Vector axis, double theta) const;
+ Vector RotatedAbout(Vector axis, double theta) const;
+ Vector DotInToCsys(Vector u, Vector v, Vector n) const;
+ Vector ScaleOutOfCsys(Vector u, Vector v, Vector n) const;
+ double DistanceToLine(Vector p0, Vector dp) const;
+ double DistanceToPlane(Vector normal, Vector origin) const;
+ bool OnLineSegment(Vector a, Vector b, double tol=LENGTH_EPS) const;
+ Vector ClosestPointOnLine(Vector p0, Vector deltal) const;
+ double Magnitude() const;
+ double MagSquared() const;
+ Vector WithMagnitude(double s) const;
+ Vector ScaledBy(double s) const;
+ Vector ProjectInto(hEntity wrkpl) const;
+ Vector ProjectVectorInto(hEntity wrkpl) const;
+ double DivProjected(Vector delta) const;
+ Vector ClosestOrtho() const;
+ void MakeMaxMin(Vector *maxv, Vector *minv) const;
+ Vector ClampWithin(double minv, double maxv) const;
static bool BoundingBoxesDisjoint(Vector amax, Vector amin,
Vector bmax, Vector bmin);
static bool BoundingBoxIntersectsLine(Vector amax, Vector amin,
- Vector p0, Vector p1, bool segment);
- bool OutsideAndNotOn(Vector maxv, Vector minv);
+ Vector p0, Vector p1, bool asSegment);
+ bool OutsideAndNotOn(Vector maxv, Vector minv) const;
Vector InPerspective(Vector u, Vector v, Vector n,
- Vector origin, double cameraTan);
- Point2d Project2d(Vector u, Vector v);
- Point2d ProjectXy(void);
- Vector4 Project4d(void);
+ Vector origin, double cameraTan) const;
+ Point2d Project2d(Vector u, Vector v) const;
+ Point2d ProjectXy() const;
+ Vector4 Project4d() const;
+};
+
+inline double Vector::Element(int i) const {
+ switch (i) {
+ case 0: return x;
+ case 1: return y;
+ case 2: return z;
+ default: ssassert(false, "Unexpected vector element index");
+ }
+}
+
+inline bool Vector::Equals(Vector v, double tol) const {
+ // Quick axis-aligned tests before going further
+ const Vector dv = this->Minus(v);
+ if (fabs(dv.x) > tol) return false;
+ if (fabs(dv.y) > tol) return false;
+ if (fabs(dv.z) > tol) return false;
+
+ return dv.MagSquared() < tol*tol;
+}
+
+inline Vector Vector::From(double x, double y, double z) {
+ return {x, y, z};
+}
+
+inline Vector Vector::Plus(Vector b) const {
+ return {x + b.x, y + b.y, z + b.z};
+}
+
+inline Vector Vector::Minus(Vector b) const {
+ return {x - b.x, y - b.y, z - b.z};
+}
+
+inline Vector Vector::Negated() const {
+ return {-x, -y, -z};
+}
+
+inline Vector Vector::Cross(Vector b) const {
+ return {-(z * b.y) + (y * b.z), (z * b.x) - (x * b.z), -(y * b.x) + (x * b.y)};
+}
+
+inline double Vector::Dot(Vector b) const {
+ return (x * b.x + y * b.y + z * b.z);
+}
+
+inline double Vector::MagSquared() const {
+ return x * x + y * y + z * z;
+}
+
+inline double Vector::Magnitude() const {
+ return sqrt(x * x + y * y + z * z);
+}
+
+inline Vector Vector::ScaledBy(const double v) const {
+ return {x * v, y * v, z * v};
+}
+
+inline void Vector::MakeMaxMin(Vector *maxv, Vector *minv) const {
+ maxv->x = max(maxv->x, x);
+ maxv->y = max(maxv->y, y);
+ maxv->z = max(maxv->z, z);
+
+ minv->x = min(minv->x, x);
+ minv->y = min(minv->y, y);
+ minv->z = min(minv->z, z);
+}
+
+struct VectorHash {
+ size_t operator()(const Vector &v) const;
+};
+
+struct VectorPred {
+ bool operator()(Vector a, Vector b) const;
};
class Vector4 {
static Vector4 From(double w, Vector v3);
static Vector4 Blend(Vector4 a, Vector4 b, double t);
- Vector4 Plus(Vector4 b);
- Vector4 Minus(Vector4 b);
- Vector4 ScaledBy(double s);
- Vector PerspectiveProject(void);
+ Vector4 Plus(Vector4 b) const;
+ Vector4 Minus(Vector4 b) const;
+ Vector4 ScaledBy(double s) const;
+ Vector PerspectiveProject() const;
};
class Point2d {
double x, y;
static Point2d From(double x, double y);
+ static Point2d FromPolar(double r, double a);
Point2d Plus(const Point2d &b) const;
Point2d Minus(const Point2d &b) const;
Point2d ScaledBy(double s) const;
- double DivPivoting(Point2d delta) const;
+ double DivProjected(Point2d delta) const;
double Dot(Point2d p) const;
double DistanceTo(const Point2d &p) const;
- double DistanceToLine(const Point2d &p0, const Point2d &dp, bool segment) const;
- double Magnitude(void) const;
- double MagSquared(void) const;
+ double DistanceToLine(const Point2d &p0, const Point2d &dp, bool asSegment) const;
+ double DistanceToLineSigned(const Point2d &p0, const Point2d &dp, bool asSegment) const;
+ double Angle() const;
+ double AngleTo(const Point2d &p) const;
+ double Magnitude() const;
+ double MagSquared() const;
Point2d WithMagnitude(double v) const;
- Point2d Normal(void) const;
+ Point2d Normal() const;
bool Equals(Point2d v, double tol=LENGTH_EPS) const;
};
// A simple list
-template <class T>
+template<class T>
class List {
+ T *elem = nullptr;
+ int elemsAllocated = 0;
+
public:
- T *elem;
- int n;
- int elemsAllocated;
+ int n = 0;
- void AllocForOneMore(void) {
- if(n >= elemsAllocated) {
- elemsAllocated = (elemsAllocated + 32)*2;
- T *newElem = (T *)MemAlloc((size_t)elemsAllocated*sizeof(elem[0]));
+ bool IsEmpty() const { return n == 0; }
+
+ void ReserveMore(int howMuch) {
+ if(n + howMuch > elemsAllocated) {
+ elemsAllocated = n + howMuch;
+ T *newElem = (T *)::operator new[]((size_t)elemsAllocated*sizeof(T));
for(int i = 0; i < n; i++) {
new(&newElem[i]) T(std::move(elem[i]));
elem[i].~T();
}
- MemFree(elem);
+ ::operator delete[](elem);
elem = newElem;
}
}
- void Add(T *t) {
+ void AllocForOneMore() {
+ if(n >= elemsAllocated) {
+ ReserveMore((elemsAllocated + 32)*2 - n);
+ }
+ }
+
+ void Add(const T *t) {
AllocForOneMore();
new(&elem[n++]) T(*t);
}
- void AddToBeginning(T *t) {
+ void AddToBeginning(const T *t) {
AllocForOneMore();
new(&elem[n]) T();
std::move_backward(elem, elem + 1, elem + n + 1);
n++;
}
- T *First(void) {
- return (n == 0) ? NULL : &(elem[0]);
+ T *First() {
+ return IsEmpty() ? nullptr : &(elem[0]);
+ }
+ const T *First() const {
+ return IsEmpty() ? nullptr : &(elem[0]);
}
+
+ T *Last() { return IsEmpty() ? nullptr : &(elem[n - 1]); }
+ const T *Last() const { return IsEmpty() ? nullptr : &(elem[n - 1]); }
+
T *NextAfter(T *prev) {
- if(!prev) return NULL;
- if(prev - elem == (n - 1)) return NULL;
+ if(IsEmpty() || !prev) return NULL;
+ if(prev - First() == (n - 1)) return NULL;
+ return prev + 1;
+ }
+ const T *NextAfter(const T *prev) const {
+ if(IsEmpty() || !prev) return NULL;
+ if(prev - First() == (n - 1)) return NULL;
return prev + 1;
}
- void ClearTags(void) {
- int i;
- for(i = 0; i < n; i++) {
- elem[i].tag = 0;
+ T &Get(size_t i) { return elem[i]; }
+ T const &Get(size_t i) const { return elem[i]; }
+ T &operator[](size_t i) { return Get(i); }
+ T const &operator[](size_t i) const { return Get(i); }
+
+ T *begin() { return IsEmpty() ? nullptr : &elem[0]; }
+ T *end() { return IsEmpty() ? nullptr : &elem[n]; }
+ const T *begin() const { return IsEmpty() ? nullptr : &elem[0]; }
+ const T *end() const { return IsEmpty() ? nullptr : &elem[n]; }
+ const T *cbegin() const { return begin(); }
+ const T *cend() const { return end(); }
+
+ void ClearTags() {
+ for(auto & elt : *this) {
+ elt.tag = 0;
}
}
- void Clear(void) {
+ void Clear() {
for(int i = 0; i < n; i++)
elem[i].~T();
- if(elem) MemFree(elem);
+ if(elem) ::operator delete[](elem);
elem = NULL;
n = elemsAllocated = 0;
}
- void RemoveTagged(void) {
- int src, dest;
- dest = 0;
- for(src = 0; src < n; src++) {
- if(elem[src].tag) {
- // this item should be deleted
- } else {
- if(src != dest) {
- elem[dest] = elem[src];
- }
- dest++;
+ void RemoveTagged() {
+ auto newEnd = std::remove_if(this->begin(), this->end(), [](T &t) {
+ if(t.tag) {
+ return true;
+ }
+ return false;
+ });
+ auto oldEnd = this->end();
+ n = newEnd - begin();
+ if (newEnd != nullptr && oldEnd != nullptr) {
+ while(newEnd != oldEnd) {
+ newEnd->~T();
+ ++newEnd;
}
}
- for(int i = dest; i < n; i++)
- elem[i].~T();
- n = dest;
// and elemsAllocated is untouched, because we didn't resize
}
void RemoveLast(int cnt) {
- if(n < cnt) oops();
+ ssassert(n >= cnt, "Removing more elements than the list contains");
for(int i = n - cnt; i < n; i++)
elem[i].~T();
n -= cnt;
// and elemsAllocated is untouched, same as in RemoveTagged
}
- void Reverse(void) {
+ void Reverse() {
int i;
for(i = 0; i < (n/2); i++) {
swap(elem[i], elem[(n-1)-i]);
}
};
+// Comparison functor used by IdList and related classes
+template <class T, class H>
+struct CompareId {
+ bool operator()(T const& lhs, T const& rhs) const {
+ return lhs.h.v < rhs.h.v;
+ }
+ bool operator()(T const& lhs, H rhs) const {
+ return lhs.h.v < rhs.v;
+ }
+};
+
// A list, where each element has an integer identifier. The list is kept
// sorted by that identifier, and items can be looked up in log n time by
// id.
template <class T, class H>
class IdList {
+ T *elem = nullptr;
+ int elemsAllocated = 0;
public:
- T *elem;
- int n;
- int elemsAllocated;
+ int n = 0;
- uint32_t MaximumId(void) {
- uint32_t id = 0;
+ using Compare = CompareId<T, H>;
- int i;
- for(i = 0; i < n; i++) {
- id = max(id, elem[i].h.v);
+ bool IsEmpty() const {
+ return n == 0;
+ }
+
+ void AllocForOneMore() {
+ if(n >= elemsAllocated) {
+ ReserveMore((elemsAllocated + 32)*2 - n);
+ }
+ }
+
+ uint32_t MaximumId() {
+ if(IsEmpty()) {
+ return 0;
+ } else {
+ return Last()->h.v;
}
- return id;
}
H AddAndAssignId(T *t) {
t->h.v = (MaximumId() + 1);
- Add(t);
+ AllocForOneMore();
+
+ // Copy-construct at the end of the list.
+ new(&elem[n]) T(*t);
+ ++n;
return t->h;
}
- void Add(T *t) {
- if(n >= elemsAllocated) {
- elemsAllocated = (elemsAllocated + 32)*2;
- T *newElem = (T *)MemAlloc((size_t)elemsAllocated*sizeof(elem[0]));
+ T * LowerBound(T const& t) {
+ if(IsEmpty()) {
+ return nullptr;
+ }
+ auto it = std::lower_bound(begin(), end(), t, Compare());
+ return it;
+ }
+
+ T * LowerBound(H const& h) {
+ if(IsEmpty()) {
+ return nullptr;
+ }
+ auto it = std::lower_bound(begin(), end(), h, Compare());
+ return it;
+ }
+
+ int LowerBoundIndex(T const& t) {
+ if(IsEmpty()) {
+ return 0;
+ }
+ auto it = LowerBound(t);
+ auto idx = std::distance(begin(), it);
+ auto i = static_cast<int>(idx);
+ return i;
+ }
+ void ReserveMore(int howMuch) {
+ if(n + howMuch > elemsAllocated) {
+ elemsAllocated = n + howMuch;
+ T *newElem = (T *)::operator new[]((size_t)elemsAllocated*sizeof(T));
for(int i = 0; i < n; i++) {
new(&newElem[i]) T(std::move(elem[i]));
elem[i].~T();
}
- MemFree(elem);
+ ::operator delete[](elem);
elem = newElem;
}
+ }
- int first = 0, last = n;
- // We know that we must insert within the closed interval [first,last]
- while(first != last) {
- int mid = (first + last)/2;
- H hm = elem[mid].h;
- if(hm.v > t->h.v) {
- last = mid;
- } else if(hm.v < t->h.v) {
- first = mid + 1;
- } else {
- dbp("can't insert in list; is handle %d not unique?", t->h.v);
- oops();
- }
- }
+ void Add(T *t) {
+ AllocForOneMore();
- int i = first;
- new(&elem[n]) T();
- std::move_backward(elem + i, elem + n, elem + n + 1);
- elem[i] = *t;
- n++;
+ // Look to see if we already have something with the same handle value.
+ ssassert(FindByIdNoOops(t->h) == nullptr, "Handle isn't unique");
+
+ // Copy-construct at the end of the list.
+ new(&elem[n]) T(*t);
+ ++n;
+ // The item we just added is trivially sorted, so "merge"
+ std::inplace_merge(begin(), end() - 1, end(), Compare());
}
T *FindById(H h) {
T *t = FindByIdNoOops(h);
- if(!t) {
- dbp("failed to look up item %08x, searched %d items", h.v, n);
- oops();
- }
+ ssassert(t != NULL, "Cannot find handle");
return t;
}
int IndexOf(H h) {
- int first = 0, last = n-1;
- while(first <= last) {
- int mid = (first + last)/2;
- H hm = elem[mid].h;
- if(hm.v > h.v) {
- last = mid-1; // and first stays the same
- } else if(hm.v < h.v) {
- first = mid+1; // and last stays the same
- } else {
- return mid;
- }
+ if(IsEmpty()) {
+ return -1;
+ }
+ auto it = LowerBound(h);
+ auto idx = std::distance(begin(), it);
+ if (idx < n) {
+ return idx;
}
return -1;
}
T *FindByIdNoOops(H h) {
- int first = 0, last = n-1;
- while(first <= last) {
- int mid = (first + last)/2;
- H hm = elem[mid].h;
- if(hm.v > h.v) {
- last = mid-1; // and first stays the same
- } else if(hm.v < h.v) {
- first = mid+1; // and last stays the same
- } else {
- return &(elem[mid]);
- }
+ if(IsEmpty()) {
+ return nullptr;
+ }
+ auto it = LowerBound(h);
+ if (it == nullptr || it == end()) {
+ return nullptr;
+ }
+ if (it->h.v == h.v) {
+ return it;
}
- return NULL;
+ return nullptr;
}
- T *First(void) {
- return (n == 0) ? NULL : &(elem[0]);
+ T *First() {
+ return (IsEmpty()) ? NULL : &(elem[0]);
+ }
+ T *Last() {
+ return (IsEmpty()) ? NULL : &(elem[n-1]);
}
T *NextAfter(T *prev) {
- if(!prev) return NULL;
- if(prev - elem == (n - 1)) return NULL;
+ if(IsEmpty() || !prev) return NULL;
+ if(prev - First() == (n - 1)) return NULL;
return prev + 1;
}
- void ClearTags(void) {
- int i;
- for(i = 0; i < n; i++) {
- elem[i].tag = 0;
- }
+ T &Get(size_t i) { return elem[i]; }
+ T const &Get(size_t i) const { return elem[i]; }
+ T &operator[](size_t i) { return Get(i); }
+ T const &operator[](size_t i) const { return Get(i); }
+
+ T *begin() { return IsEmpty() ? nullptr : &elem[0]; }
+ T *end() { return IsEmpty() ? nullptr : &elem[0] + n; }
+ const T *begin() const { return IsEmpty() ? nullptr : &elem[0]; }
+ const T *end() const { return IsEmpty() ? nullptr : &elem[0] + n; }
+ const T *cbegin() const { return begin(); }
+ const T *cend() const { return end(); }
+
+ void ClearTags() {
+ for(auto &elt : *this) { elt.tag = 0; }
}
void Tag(H h, int tag) {
- int i;
- for(i = 0; i < n; i++) {
- if(elem[i].h.v == h.v) {
- elem[i].tag = tag;
- }
+ auto it = FindByIdNoOops(h);
+ if (it != nullptr) {
+ it->tag = tag;
}
}
- void RemoveTagged(void) {
+ void RemoveTagged() {
int src, dest;
dest = 0;
for(src = 0; src < n; src++) {
if(elem[src].tag) {
// this item should be deleted
+ elem[src].Clear();
} else {
if(src != dest) {
elem[dest] = elem[src];
void MoveSelfInto(IdList<T,H> *l) {
l->Clear();
- *l = *this;
- elemsAllocated = n = 0;
- elem = NULL;
+ std::swap(l->elem, elem);
+ std::swap(l->elemsAllocated, elemsAllocated);
+ std::swap(l->n, n);
}
void DeepCopyInto(IdList<T,H> *l) {
l->Clear();
- l->elem = (T *)MemAlloc(elemsAllocated * sizeof(elem[0]));
+ l->elem = (T *)::operator new[](elemsAllocated * sizeof(elem[0]));
for(int i = 0; i < n; i++)
new(&l->elem[i]) T(elem[i]);
l->elemsAllocated = elemsAllocated;
l->n = n;
}
- void Clear(void) {
+ void Clear() {
for(int i = 0; i < n; i++) {
elem[i].Clear();
elem[i].~T();
}
- elemsAllocated = n = 0;
- if(elem) MemFree(elem);
+ if(elem) ::operator delete[](elem);
elem = NULL;
+ elemsAllocated = n = 0;
}
};
double X[MAX_UNKNOWNS];
int n;
- void Solve(void);
+ void Solve();
};
#define RGBi(r, g, b) RgbaColor::From((r), (g), (b))
public:
uint8_t red, green, blue, alpha;
- float redF(void) const { return (float)red / 255.0f; }
- float greenF(void) const { return (float)green / 255.0f; }
- float blueF(void) const { return (float)blue / 255.0f; }
- float alphaF(void) const { return (float)alpha / 255.0f; }
+ float redF() const { return (float)red / 255.0f; }
+ float greenF() const { return (float)green / 255.0f; }
+ float blueF() const { return (float)blue / 255.0f; }
+ float alphaF() const { return (float)alpha / 255.0f; }
+
+ bool IsEmpty() const { return alpha == 0; }
bool Equals(RgbaColor c) const {
return
c.alpha == alpha;
}
- uint32_t ToPackedIntBGRA(void) const {
+ RgbaColor WithAlpha(uint8_t newAlpha) const {
+ RgbaColor color = *this;
+ color.alpha = newAlpha;
+ return color;
+ }
+
+ uint32_t ToPackedIntBGRA() const {
return
blue |
(uint32_t)(green << 8) |
(uint32_t)((255 - alpha) << 24);
}
- uint32_t ToPackedInt(void) const {
+ uint32_t ToPackedInt() const {
return
red |
(uint32_t)(green << 8) |
(uint32_t)((255 - alpha) << 24);
}
- uint32_t ToARGB32(void) const {
+ uint32_t ToARGB32() const {
return
blue |
(uint32_t)(green << 8) |
(int)((bgra) & 0xff),
(int)(255 - ((bgra >> 24) & 0xff)));
}
+};
+struct RgbaColorCompare {
+ bool operator()(RgbaColor a, RgbaColor b) const {
+ return a.ToARGB32() < b.ToARGB32();
+ }
};
class BBox {
static BBox From(const Vector &p0, const Vector &p1);
- Vector GetOrigin();
- Vector GetExtents();
+ Vector GetOrigin() const;
+ Vector GetExtents() const;
void Include(const Vector &v, double r = 0.0);
- bool Overlaps(BBox &b1);
- bool Contains(const Point2d &p);
+ bool Overlaps(const BBox &b1) const;
+ bool Contains(const Point2d &p, double r = 0.0) const;
};
#endif
const hEntity EntityBase::FREE_IN_3D = { 0 };
const hEntity EntityBase::NO_ENTITY = { 0 };
-bool EntityBase::HasVector(void) {
+bool EntityBase::HasVector() const {
switch(type) {
- case LINE_SEGMENT:
- case NORMAL_IN_3D:
- case NORMAL_IN_2D:
- case NORMAL_N_COPY:
- case NORMAL_N_ROT:
- case NORMAL_N_ROT_AA:
+ case Type::LINE_SEGMENT:
+ case Type::NORMAL_IN_3D:
+ case Type::NORMAL_IN_2D:
+ case Type::NORMAL_N_COPY:
+ case Type::NORMAL_N_ROT:
+ case Type::NORMAL_N_ROT_AA:
return true;
default:
}
}
-ExprVector EntityBase::VectorGetExprs(void) {
+ExprVector EntityBase::VectorGetExprsInWorkplane(hEntity wrkpl) const {
switch(type) {
- case LINE_SEGMENT:
- return (SK.GetEntity(point[0])->PointGetExprs()).Minus(
- SK.GetEntity(point[1])->PointGetExprs());
-
- case NORMAL_IN_3D:
- case NORMAL_IN_2D:
- case NORMAL_N_COPY:
- case NORMAL_N_ROT:
- case NORMAL_N_ROT_AA:
- return NormalExprsN();
-
- default: oops();
+ case Type::LINE_SEGMENT:
+ return (SK.GetEntity(point[0])->PointGetExprsInWorkplane(wrkpl)).Minus(
+ SK.GetEntity(point[1])->PointGetExprsInWorkplane(wrkpl));
+
+ case Type::NORMAL_IN_3D:
+ case Type::NORMAL_IN_2D:
+ case Type::NORMAL_N_COPY:
+ case Type::NORMAL_N_ROT:
+ case Type::NORMAL_N_ROT_AA: {
+ ExprVector ev = NormalExprsN();
+ if(wrkpl == EntityBase::FREE_IN_3D) {
+ return ev;
+ }
+ // Get the offset and basis vectors for this weird exotic csys.
+ EntityBase *w = SK.GetEntity(wrkpl);
+ ExprVector wu = w->Normal()->NormalExprsU();
+ ExprVector wv = w->Normal()->NormalExprsV();
+
+ // Get our coordinates in three-space, and project them into that
+ // coordinate system.
+ ExprVector result;
+ result.x = ev.Dot(wu);
+ result.y = ev.Dot(wv);
+ result.z = Expr::From(0.0);
+ return result;
+ }
+ default: ssassert(false, "Unexpected entity type");
}
}
-Vector EntityBase::VectorGetNum(void) {
+ExprVector EntityBase::VectorGetExprs() const {
+ return VectorGetExprsInWorkplane(EntityBase::FREE_IN_3D);
+}
+
+Vector EntityBase::VectorGetNum() const {
switch(type) {
- case LINE_SEGMENT:
+ case Type::LINE_SEGMENT:
return (SK.GetEntity(point[0])->PointGetNum()).Minus(
SK.GetEntity(point[1])->PointGetNum());
- case NORMAL_IN_3D:
- case NORMAL_IN_2D:
- case NORMAL_N_COPY:
- case NORMAL_N_ROT:
- case NORMAL_N_ROT_AA:
+ case Type::NORMAL_IN_3D:
+ case Type::NORMAL_IN_2D:
+ case Type::NORMAL_N_COPY:
+ case Type::NORMAL_N_ROT:
+ case Type::NORMAL_N_ROT_AA:
return NormalN();
- default: oops();
+ default: ssassert(false, "Unexpected entity type");
}
}
-Vector EntityBase::VectorGetRefPoint(void) {
+Vector EntityBase::VectorGetRefPoint() const {
switch(type) {
- case LINE_SEGMENT:
+ case Type::LINE_SEGMENT:
return ((SK.GetEntity(point[0])->PointGetNum()).Plus(
SK.GetEntity(point[1])->PointGetNum())).ScaledBy(0.5);
- case NORMAL_IN_3D:
- case NORMAL_IN_2D:
- case NORMAL_N_COPY:
- case NORMAL_N_ROT:
- case NORMAL_N_ROT_AA:
+ case Type::NORMAL_IN_3D:
+ case Type::NORMAL_IN_2D:
+ case Type::NORMAL_N_COPY:
+ case Type::NORMAL_N_ROT:
+ case Type::NORMAL_N_ROT_AA:
return SK.GetEntity(point[0])->PointGetNum();
- default: oops();
+ default: ssassert(false, "Unexpected entity type");
}
}
-Vector EntityBase::VectorGetStartPoint(void) {
+Vector EntityBase::VectorGetStartPoint() const {
switch(type) {
- case LINE_SEGMENT:
+ case Type::LINE_SEGMENT:
return SK.GetEntity(point[1])->PointGetNum();
- case NORMAL_IN_3D:
- case NORMAL_IN_2D:
- case NORMAL_N_COPY:
- case NORMAL_N_ROT:
- case NORMAL_N_ROT_AA:
+ case Type::NORMAL_IN_3D:
+ case Type::NORMAL_IN_2D:
+ case Type::NORMAL_N_COPY:
+ case Type::NORMAL_N_ROT:
+ case Type::NORMAL_N_ROT_AA:
return SK.GetEntity(point[0])->PointGetNum();
- default: oops();
+ default: ssassert(false, "Unexpected entity type");
}
}
-bool EntityBase::IsCircle(void) {
- return (type == CIRCLE) || (type == ARC_OF_CIRCLE);
+bool EntityBase::IsCircle() const {
+ return (type == Type::CIRCLE) || (type == Type::ARC_OF_CIRCLE);
}
-Expr *EntityBase::CircleGetRadiusExpr(void) {
- if(type == CIRCLE) {
+Expr *EntityBase::CircleGetRadiusExpr() const {
+ if(type == Type::CIRCLE) {
return SK.GetEntity(distance)->DistanceGetExpr();
- } else if(type == ARC_OF_CIRCLE) {
+ } else if(type == Type::ARC_OF_CIRCLE) {
return Constraint::Distance(workplane, point[0], point[1]);
- } else oops();
+ } else ssassert(false, "Unexpected entity type");
}
-double EntityBase::CircleGetRadiusNum(void) {
- if(type == CIRCLE) {
+double EntityBase::CircleGetRadiusNum() const {
+ if(type == Type::CIRCLE) {
return SK.GetEntity(distance)->DistanceGetNum();
- } else if(type == ARC_OF_CIRCLE) {
+ } else if(type == Type::ARC_OF_CIRCLE) {
Vector c = SK.GetEntity(point[0])->PointGetNum();
Vector pa = SK.GetEntity(point[1])->PointGetNum();
return (pa.Minus(c)).Magnitude();
- } else oops();
+ } else ssassert(false, "Unexpected entity type");
}
-void EntityBase::ArcGetAngles(double *thetaa, double *thetab, double *dtheta) {
- if(type != ARC_OF_CIRCLE) oops();
+void EntityBase::ArcGetAngles(double *thetaa, double *thetab, double *dtheta) const {
+ ssassert(type == Type::ARC_OF_CIRCLE, "Unexpected entity type");
Quaternion q = Normal()->NormalGetNum();
Vector u = q.RotationU(), v = q.RotationV();
while(*dtheta > (2*PI)) *dtheta -= 2*PI;
}
-Vector EntityBase::CubicGetStartNum(void) {
+Vector EntityBase::CubicGetStartNum() const {
return SK.GetEntity(point[0])->PointGetNum();
}
-Vector EntityBase::CubicGetFinishNum(void) {
+Vector EntityBase::CubicGetFinishNum() const {
return SK.GetEntity(point[3+extraPoints])->PointGetNum();
}
-ExprVector EntityBase::CubicGetStartTangentExprs(void) {
+ExprVector EntityBase::CubicGetStartTangentExprs() const {
ExprVector pon = SK.GetEntity(point[0])->PointGetExprs(),
poff = SK.GetEntity(point[1])->PointGetExprs();
return (pon.Minus(poff));
}
-ExprVector EntityBase::CubicGetFinishTangentExprs(void) {
+ExprVector EntityBase::CubicGetFinishTangentExprs() const {
ExprVector pon = SK.GetEntity(point[3+extraPoints])->PointGetExprs(),
poff = SK.GetEntity(point[2+extraPoints])->PointGetExprs();
return (pon.Minus(poff));
}
-Vector EntityBase::CubicGetStartTangentNum(void) {
+Vector EntityBase::CubicGetStartTangentNum() const {
Vector pon = SK.GetEntity(point[0])->PointGetNum(),
poff = SK.GetEntity(point[1])->PointGetNum();
return (pon.Minus(poff));
}
-Vector EntityBase::CubicGetFinishTangentNum(void) {
+Vector EntityBase::CubicGetFinishTangentNum() const {
Vector pon = SK.GetEntity(point[3+extraPoints])->PointGetNum(),
poff = SK.GetEntity(point[2+extraPoints])->PointGetNum();
return (pon.Minus(poff));
}
-bool EntityBase::IsWorkplane(void) {
- return (type == WORKPLANE);
+bool EntityBase::IsWorkplane() const {
+ return (type == Type::WORKPLANE);
}
-ExprVector EntityBase::WorkplaneGetOffsetExprs(void) {
+ExprVector EntityBase::WorkplaneGetOffsetExprs() const {
return SK.GetEntity(point[0])->PointGetExprs();
}
-Vector EntityBase::WorkplaneGetOffset(void) {
+Vector EntityBase::WorkplaneGetOffset() const {
return SK.GetEntity(point[0])->PointGetNum();
}
-void EntityBase::WorkplaneGetPlaneExprs(ExprVector *n, Expr **dn) {
- if(type == WORKPLANE) {
+void EntityBase::WorkplaneGetPlaneExprs(ExprVector *n, Expr **dn) const {
+ if(type == Type::WORKPLANE) {
*n = Normal()->NormalExprsN();
ExprVector p0 = SK.GetEntity(point[0])->PointGetExprs();
// n dot p - n dot p0 = 0
// so dn = n dot p0
*dn = p0.Dot(*n);
- } else {
- oops();
- }
+ } else ssassert(false, "Unexpected entity type");
}
-bool EntityBase::IsDistance(void) {
- return (type == DISTANCE) ||
- (type == DISTANCE_N_COPY);
+bool EntityBase::IsDistance() const {
+ return (type == Type::DISTANCE) ||
+ (type == Type::DISTANCE_N_COPY);
}
-double EntityBase::DistanceGetNum(void) {
- if(type == DISTANCE) {
+double EntityBase::DistanceGetNum() const {
+ if(type == Type::DISTANCE) {
return SK.GetParam(param[0])->val;
- } else if(type == DISTANCE_N_COPY) {
+ } else if(type == Type::DISTANCE_N_COPY) {
return numDistance;
- } else oops();
+ } else ssassert(false, "Unexpected entity type");
}
-Expr *EntityBase::DistanceGetExpr(void) {
- if(type == DISTANCE) {
+Expr *EntityBase::DistanceGetExpr() const {
+ if(type == Type::DISTANCE) {
return Expr::From(param[0]);
- } else if(type == DISTANCE_N_COPY) {
+ } else if(type == Type::DISTANCE_N_COPY) {
return Expr::From(numDistance);
- } else oops();
+ } else ssassert(false, "Unexpected entity type");
}
void EntityBase::DistanceForceTo(double v) {
- if(type == DISTANCE) {
+ if(type == Type::DISTANCE) {
(SK.GetParam(param[0]))->val = v;
- } else if(type == DISTANCE_N_COPY) {
+ } else if(type == Type::DISTANCE_N_COPY) {
// do nothing, it's locked
- } else oops();
+ } else ssassert(false, "Unexpected entity type");
}
-EntityBase *EntityBase::Normal(void) {
+EntityBase *EntityBase::Normal() const {
return SK.GetEntity(normal);
}
-bool EntityBase::IsPoint(void) {
+bool EntityBase::IsPoint() const {
switch(type) {
- case POINT_IN_3D:
- case POINT_IN_2D:
- case POINT_N_COPY:
- case POINT_N_TRANS:
- case POINT_N_ROT_TRANS:
- case POINT_N_ROT_AA:
+ case Type::POINT_IN_3D:
+ case Type::POINT_IN_2D:
+ case Type::POINT_N_COPY:
+ case Type::POINT_N_TRANS:
+ case Type::POINT_N_ROT_TRANS:
+ case Type::POINT_N_ROT_AA:
+ case Type::POINT_N_ROT_AXIS_TRANS:
return true;
default:
}
}
-bool EntityBase::IsNormal(void) {
+bool EntityBase::IsNormal() const {
switch(type) {
- case NORMAL_IN_3D:
- case NORMAL_IN_2D:
- case NORMAL_N_COPY:
- case NORMAL_N_ROT:
- case NORMAL_N_ROT_AA:
+ case Type::NORMAL_IN_3D:
+ case Type::NORMAL_IN_2D:
+ case Type::NORMAL_N_COPY:
+ case Type::NORMAL_N_ROT:
+ case Type::NORMAL_N_ROT_AA:
return true;
default: return false;
}
}
-Quaternion EntityBase::NormalGetNum(void) {
+Quaternion EntityBase::NormalGetNum() const {
Quaternion q;
switch(type) {
- case NORMAL_IN_3D:
+ case Type::NORMAL_IN_3D:
q = Quaternion::From(param[0], param[1], param[2], param[3]);
break;
- case NORMAL_IN_2D: {
+ case Type::NORMAL_IN_2D: {
EntityBase *wrkpl = SK.GetEntity(workplane);
EntityBase *norm = SK.GetEntity(wrkpl->normal);
q = norm->NormalGetNum();
break;
}
- case NORMAL_N_COPY:
+ case Type::NORMAL_N_COPY:
q = numNormal;
break;
- case NORMAL_N_ROT:
+ case Type::NORMAL_N_ROT:
q = Quaternion::From(param[0], param[1], param[2], param[3]);
q = q.Times(numNormal);
break;
- case NORMAL_N_ROT_AA: {
+ case Type::NORMAL_N_ROT_AA: {
q = GetAxisAngleQuaternion(0);
q = q.Times(numNormal);
break;
}
- default: oops();
+ default: ssassert(false, "Unexpected entity type");
}
return q;
}
void EntityBase::NormalForceTo(Quaternion q) {
switch(type) {
- case NORMAL_IN_3D:
+ case Type::NORMAL_IN_3D:
SK.GetParam(param[0])->val = q.w;
SK.GetParam(param[1])->val = q.vx;
SK.GetParam(param[2])->val = q.vy;
SK.GetParam(param[3])->val = q.vz;
break;
- case NORMAL_IN_2D:
- case NORMAL_N_COPY:
+ case Type::NORMAL_IN_2D:
+ case Type::NORMAL_N_COPY:
// There's absolutely nothing to do; these are locked.
break;
- case NORMAL_N_ROT: {
+ case Type::NORMAL_N_ROT: {
Quaternion qp = q.Times(numNormal.Inverse());
SK.GetParam(param[0])->val = qp.w;
break;
}
- case NORMAL_N_ROT_AA:
+ case Type::NORMAL_N_ROT_AA:
// Not sure if I'll bother implementing this one
break;
- default: oops();
+ default: ssassert(false, "Unexpected entity type");
}
}
-Vector EntityBase::NormalU(void) {
+Vector EntityBase::NormalU() const {
return NormalGetNum().RotationU();
}
-Vector EntityBase::NormalV(void) {
+Vector EntityBase::NormalV() const {
return NormalGetNum().RotationV();
}
-Vector EntityBase::NormalN(void) {
+Vector EntityBase::NormalN() const {
return NormalGetNum().RotationN();
}
-ExprVector EntityBase::NormalExprsU(void) {
+ExprVector EntityBase::NormalExprsU() const {
return NormalGetExprs().RotationU();
}
-ExprVector EntityBase::NormalExprsV(void) {
+ExprVector EntityBase::NormalExprsV() const {
return NormalGetExprs().RotationV();
}
-ExprVector EntityBase::NormalExprsN(void) {
+ExprVector EntityBase::NormalExprsN() const {
return NormalGetExprs().RotationN();
}
-ExprQuaternion EntityBase::NormalGetExprs(void) {
+ExprQuaternion EntityBase::NormalGetExprs() const {
ExprQuaternion q;
switch(type) {
- case NORMAL_IN_3D:
+ case Type::NORMAL_IN_3D:
q = ExprQuaternion::From(param[0], param[1], param[2], param[3]);
break;
- case NORMAL_IN_2D: {
+ case Type::NORMAL_IN_2D: {
EntityBase *wrkpl = SK.GetEntity(workplane);
EntityBase *norm = SK.GetEntity(wrkpl->normal);
q = norm->NormalGetExprs();
break;
}
- case NORMAL_N_COPY:
+ case Type::NORMAL_N_COPY:
q = ExprQuaternion::From(numNormal);
break;
- case NORMAL_N_ROT: {
+ case Type::NORMAL_N_ROT: {
ExprQuaternion orig = ExprQuaternion::From(numNormal);
q = ExprQuaternion::From(param[0], param[1], param[2], param[3]);
break;
}
- case NORMAL_N_ROT_AA: {
+ case Type::NORMAL_N_ROT_AA: {
ExprQuaternion orig = ExprQuaternion::From(numNormal);
q = GetAxisAngleQuaternionExprs(0);
q = q.Times(orig);
break;
}
- default: oops();
+ default: ssassert(false, "Unexpected entity type");
}
return q;
}
+void EntityBase::PointForceParamTo(Vector p) {
+ switch(type) {
+ case Type::POINT_IN_3D:
+ SK.GetParam(param[0])->val = p.x;
+ SK.GetParam(param[1])->val = p.y;
+ SK.GetParam(param[2])->val = p.z;
+ break;
+
+ case Type::POINT_IN_2D:
+ SK.GetParam(param[0])->val = p.x;
+ SK.GetParam(param[1])->val = p.y;
+ break;
+
+ default: ssassert(false, "Unexpected entity type");
+ }
+}
+
void EntityBase::PointForceTo(Vector p) {
switch(type) {
- case POINT_IN_3D:
+ case Type::POINT_IN_3D:
SK.GetParam(param[0])->val = p.x;
SK.GetParam(param[1])->val = p.y;
SK.GetParam(param[2])->val = p.z;
break;
- case POINT_IN_2D: {
+ case Type::POINT_IN_2D: {
EntityBase *c = SK.GetEntity(workplane);
p = p.Minus(c->WorkplaneGetOffset());
SK.GetParam(param[0])->val = p.Dot(c->Normal()->NormalU());
break;
}
- case POINT_N_TRANS: {
+ case Type::POINT_N_TRANS: {
if(timesApplied == 0) break;
Vector trans = (p.Minus(numPoint)).ScaledBy(1.0/timesApplied);
SK.GetParam(param[0])->val = trans.x;
break;
}
- case POINT_N_ROT_TRANS: {
+ case Type::POINT_N_ROT_TRANS: {
// Force only the translation; leave the rotation unchanged. But
// remember that we're working with respect to the rotated
// point.
break;
}
- case POINT_N_ROT_AA: {
+ case Type::POINT_N_ROT_AA: {
// Force only the angle; the axis and center of rotation stay
Vector offset = Vector::From(param[0], param[1], param[2]);
Vector normal = Vector::From(param[4], param[5], param[6]);
// in order to avoid jumps when you cross from +pi to -pi
while(dtheta < -PI) dtheta += 2*PI;
while(dtheta > PI) dtheta -= 2*PI;
+ // this extra *2 explains the mystery *4
SK.GetParam(param[3])->val = (thetai + dtheta)/(timesApplied*2);
break;
}
- case POINT_N_COPY:
+ case Type::POINT_N_ROT_AXIS_TRANS: {
+ if(timesApplied == 0) break;
+ // is the point on the rotation axis?
+ Vector offset = Vector::From(param[0], param[1], param[2]);
+ Vector normal = Vector::From(param[4], param[5], param[6]).WithMagnitude(1.0);
+ Vector check = numPoint.Minus(offset).Cross(normal);
+ if (check.Dot(check) < LENGTH_EPS) { // if so, do extrusion style drag
+ Vector trans = (p.Minus(numPoint));
+ SK.GetParam(param[7])->val = trans.Dot(normal)/timesApplied;
+ } else { // otherwise do rotation style
+ Vector u = normal.Normal(0), v = normal.Normal(1);
+ Vector po = p.Minus(offset), numo = numPoint.Minus(offset);
+ double thetap = atan2(v.Dot(po), u.Dot(po));
+ double thetan = atan2(v.Dot(numo), u.Dot(numo));
+ double thetaf = (thetap - thetan);
+ double thetai = (SK.GetParam(param[3])->val)*timesApplied*2;
+ double dtheta = thetaf - thetai;
+ // Take the smallest possible change in the actual step angle,
+ // in order to avoid jumps when you cross from +pi to -pi
+ while(dtheta < -PI) dtheta += 2*PI;
+ while(dtheta > PI) dtheta -= 2*PI;
+ // this extra *2 explains the mystery *4
+ SK.GetParam(param[3])->val = (thetai + dtheta)/(timesApplied*2);
+ }
+ break;
+ }
+
+ case Type::POINT_N_COPY:
// Nothing to do; it's a static copy
break;
- default: oops();
+ default: ssassert(false, "Unexpected entity type");
}
}
-Vector EntityBase::PointGetNum(void) {
+Vector EntityBase::PointGetNum() const {
Vector p;
switch(type) {
- case POINT_IN_3D:
+ case Type::POINT_IN_3D:
p = Vector::From(param[0], param[1], param[2]);
break;
- case POINT_IN_2D: {
+ case Type::POINT_IN_2D: {
EntityBase *c = SK.GetEntity(workplane);
Vector u = c->Normal()->NormalU();
Vector v = c->Normal()->NormalV();
break;
}
- case POINT_N_TRANS: {
+ case Type::POINT_N_TRANS: {
Vector trans = Vector::From(param[0], param[1], param[2]);
p = numPoint.Plus(trans.ScaledBy(timesApplied));
break;
}
- case POINT_N_ROT_TRANS: {
+ case Type::POINT_N_ROT_TRANS: {
Vector offset = Vector::From(param[0], param[1], param[2]);
Quaternion q = PointGetQuaternion();
p = q.Rotate(numPoint);
break;
}
- case POINT_N_ROT_AA: {
+ case Type::POINT_N_ROT_AA: {
Vector offset = Vector::From(param[0], param[1], param[2]);
Quaternion q = PointGetQuaternion();
p = numPoint.Minus(offset);
break;
}
- case POINT_N_COPY:
+ case Type::POINT_N_ROT_AXIS_TRANS: {
+ Vector offset = Vector::From(param[0], param[1], param[2]);
+ Vector displace = Vector::From(param[4], param[5], param[6])
+ .WithMagnitude(SK.GetParam(param[7])->val).ScaledBy(timesApplied);
+ Quaternion q = PointGetQuaternion();
+ p = numPoint.Minus(offset);
+ p = q.Rotate(p);
+ p = p.Plus(offset).Plus(displace);
+ break;
+ }
+
+ case Type::POINT_N_COPY:
p = numPoint;
break;
- default: oops();
+ default: ssassert(false, "Unexpected entity type");
}
return p;
}
-ExprVector EntityBase::PointGetExprs(void) {
+ExprVector EntityBase::PointGetExprs() const {
ExprVector r;
switch(type) {
- case POINT_IN_3D:
+ case Type::POINT_IN_3D:
r = ExprVector::From(param[0], param[1], param[2]);
break;
- case POINT_IN_2D: {
+ case Type::POINT_IN_2D: {
EntityBase *c = SK.GetEntity(workplane);
ExprVector u = c->Normal()->NormalExprsU();
ExprVector v = c->Normal()->NormalExprsV();
r = r.Plus(v.ScaledBy(Expr::From(param[1])));
break;
}
- case POINT_N_TRANS: {
+ case Type::POINT_N_TRANS: {
ExprVector orig = ExprVector::From(numPoint);
ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
r = orig.Plus(trans.ScaledBy(Expr::From(timesApplied)));
break;
}
- case POINT_N_ROT_TRANS: {
+ case Type::POINT_N_ROT_TRANS: {
ExprVector orig = ExprVector::From(numPoint);
ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
ExprQuaternion q =
r = orig.Plus(trans);
break;
}
- case POINT_N_ROT_AA: {
+ case Type::POINT_N_ROT_AA: {
ExprVector orig = ExprVector::From(numPoint);
ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
ExprQuaternion q = GetAxisAngleQuaternionExprs(3);
r = orig.Plus(trans);
break;
}
- case POINT_N_COPY:
+ case Type::POINT_N_ROT_AXIS_TRANS: {
+ ExprVector orig = ExprVector::From(numPoint);
+ ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
+ ExprVector displace = ExprVector::From(param[4], param[5], param[6])
+ .WithMagnitude(Expr::From(1.0)).ScaledBy(Expr::From(timesApplied)).ScaledBy(Expr::From(param[7]));
+
+ ExprQuaternion q = GetAxisAngleQuaternionExprs(3);
+ orig = orig.Minus(trans);
+ orig = q.Rotate(orig);
+ r = orig.Plus(trans).Plus(displace);
+ break;
+ }
+ case Type::POINT_N_COPY:
r = ExprVector::From(numPoint);
break;
- default: oops();
+ default: ssassert(false, "Unexpected entity type");
}
return r;
}
-void EntityBase::PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) {
- if(type == POINT_IN_2D && workplane.v == wrkpl.v) {
+void EntityBase::PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) const {
+ if(type == Type::POINT_IN_2D && workplane == wrkpl) {
// They want our coordinates in the form that we've written them,
// very nice.
*u = Expr::From(param[0]);
}
}
+ExprVector EntityBase::PointGetExprsInWorkplane(hEntity wrkpl) const {
+ if(wrkpl == Entity::FREE_IN_3D) {
+ return PointGetExprs();
+ }
+
+ ExprVector r;
+ PointGetExprsInWorkplane(wrkpl, &r.x, &r.y);
+ r.z = Expr::From(0.0);
+ return r;
+}
+
void EntityBase::PointForceQuaternionTo(Quaternion q) {
- if(type != POINT_N_ROT_TRANS) oops();
+ ssassert(type == Type::POINT_N_ROT_TRANS, "Unexpected entity type");
SK.GetParam(param[3])->val = q.w;
SK.GetParam(param[4])->val = q.vx;
SK.GetParam(param[6])->val = q.vz;
}
-Quaternion EntityBase::GetAxisAngleQuaternion(int param0) {
+Quaternion EntityBase::GetAxisAngleQuaternion(int param0) const {
Quaternion q;
double theta = timesApplied*SK.GetParam(param[param0+0])->val;
double s = sin(theta), c = cos(theta);
return q;
}
-ExprQuaternion EntityBase::GetAxisAngleQuaternionExprs(int param0) {
+ExprQuaternion EntityBase::GetAxisAngleQuaternionExprs(int param0) const {
ExprQuaternion q;
Expr *theta = Expr::From(timesApplied)->Times(
return q;
}
-Quaternion EntityBase::PointGetQuaternion(void) {
+Quaternion EntityBase::PointGetQuaternion() const {
Quaternion q;
- if(type == POINT_N_ROT_AA) {
+ if(type == Type::POINT_N_ROT_AA || type == Type::POINT_N_ROT_AXIS_TRANS) {
q = GetAxisAngleQuaternion(3);
- } else if(type == POINT_N_ROT_TRANS) {
+ } else if(type == Type::POINT_N_ROT_TRANS) {
q = Quaternion::From(param[3], param[4], param[5], param[6]);
- } else oops();
+ } else ssassert(false, "Unexpected entity type");
return q;
}
-bool EntityBase::IsFace(void) {
+bool EntityBase::IsFace() const {
switch(type) {
- case FACE_NORMAL_PT:
- case FACE_XPROD:
- case FACE_N_ROT_TRANS:
- case FACE_N_TRANS:
- case FACE_N_ROT_AA:
+ case Type::FACE_NORMAL_PT:
+ case Type::FACE_XPROD:
+ case Type::FACE_N_ROT_TRANS:
+ case Type::FACE_N_TRANS:
+ case Type::FACE_N_ROT_AA:
+ case Type::FACE_ROT_NORMAL_PT:
+ case Type::FACE_N_ROT_AXIS_TRANS:
return true;
default:
return false;
}
}
-ExprVector EntityBase::FaceGetNormalExprs(void) {
+ExprVector EntityBase::FaceGetNormalExprs() const {
ExprVector r;
- if(type == FACE_NORMAL_PT) {
+ if(type == Type::FACE_NORMAL_PT) {
Vector v = Vector::From(numNormal.vx, numNormal.vy, numNormal.vz);
r = ExprVector::From(v.WithMagnitude(1));
- } else if(type == FACE_XPROD) {
+ } else if(type == Type::FACE_XPROD) {
ExprVector vc = ExprVector::From(param[0], param[1], param[2]);
ExprVector vn =
ExprVector::From(numNormal.vx, numNormal.vy, numNormal.vz);
r = vc.Cross(vn);
r = r.WithMagnitude(Expr::From(1.0));
- } else if(type == FACE_N_ROT_TRANS) {
+ } else if(type == Type::FACE_N_ROT_TRANS) {
// The numerical normal vector gets the rotation; the numerical
// normal has magnitude one, and the rotation doesn't change that,
// so there's no need to fix it up.
ExprQuaternion q =
ExprQuaternion::From(param[3], param[4], param[5], param[6]);
r = q.Rotate(r);
- } else if(type == FACE_N_TRANS) {
+ } else if(type == Type::FACE_N_TRANS) {
r = ExprVector::From(numNormal.vx, numNormal.vy, numNormal.vz);
- } else if(type == FACE_N_ROT_AA) {
+ } else if((type == Type::FACE_N_ROT_AA) || (type == Type::FACE_ROT_NORMAL_PT)) {
r = ExprVector::From(numNormal.vx, numNormal.vy, numNormal.vz);
ExprQuaternion q = GetAxisAngleQuaternionExprs(3);
r = q.Rotate(r);
- } else oops();
+ } else ssassert(false, "Unexpected entity type");
return r;
}
-Vector EntityBase::FaceGetNormalNum(void) {
+Vector EntityBase::FaceGetNormalNum() const {
Vector r;
- if(type == FACE_NORMAL_PT) {
+ if(type == Type::FACE_NORMAL_PT) {
r = Vector::From(numNormal.vx, numNormal.vy, numNormal.vz);
- } else if(type == FACE_XPROD) {
+ } else if(type == Type::FACE_XPROD) {
Vector vc = Vector::From(param[0], param[1], param[2]);
Vector vn = Vector::From(numNormal.vx, numNormal.vy, numNormal.vz);
r = vc.Cross(vn);
- } else if(type == FACE_N_ROT_TRANS) {
+ } else if(type == Type::FACE_N_ROT_TRANS) {
// The numerical normal vector gets the rotation
r = Vector::From(numNormal.vx, numNormal.vy, numNormal.vz);
Quaternion q = Quaternion::From(param[3], param[4], param[5], param[6]);
r = q.Rotate(r);
- } else if(type == FACE_N_TRANS) {
+ } else if(type == Type::FACE_N_TRANS) {
r = Vector::From(numNormal.vx, numNormal.vy, numNormal.vz);
- } else if(type == FACE_N_ROT_AA) {
+ } else if((type == Type::FACE_N_ROT_AA) || (type == Type::FACE_ROT_NORMAL_PT)) {
r = Vector::From(numNormal.vx, numNormal.vy, numNormal.vz);
Quaternion q = GetAxisAngleQuaternion(3);
r = q.Rotate(r);
- } else oops();
+ } else ssassert(false, "Unexpected entity type");
return r.WithMagnitude(1);
}
-ExprVector EntityBase::FaceGetPointExprs(void) {
+ExprVector EntityBase::FaceGetPointExprs() const {
ExprVector r;
- if(type == FACE_NORMAL_PT) {
+ if((type == Type::FACE_NORMAL_PT) || (type==Type::FACE_ROT_NORMAL_PT)) {
r = SK.GetEntity(point[0])->PointGetExprs();
- } else if(type == FACE_XPROD) {
+ } else if(type == Type::FACE_XPROD) {
r = ExprVector::From(numPoint);
- } else if(type == FACE_N_ROT_TRANS) {
+ } else if(type == Type::FACE_N_ROT_TRANS) {
// The numerical point gets the rotation and translation.
ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
ExprQuaternion q =
r = ExprVector::From(numPoint);
r = q.Rotate(r);
r = r.Plus(trans);
- } else if(type == FACE_N_TRANS) {
+ } else if(type == Type::FACE_N_ROT_AXIS_TRANS) {
+ ExprVector orig = ExprVector::From(numPoint);
+ ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
+ ExprVector displace = ExprVector::From(param[4], param[5], param[6])
+ .WithMagnitude(Expr::From(param[7])).ScaledBy(Expr::From(timesApplied));
+ ExprQuaternion q = GetAxisAngleQuaternionExprs(3);
+ orig = orig.Minus(trans);
+ orig = q.Rotate(orig);
+ r = orig.Plus(trans).Plus(displace);
+ } else if(type == Type::FACE_N_TRANS) {
ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
r = ExprVector::From(numPoint);
r = r.Plus(trans.ScaledBy(Expr::From(timesApplied)));
- } else if(type == FACE_N_ROT_AA) {
+ } else if(type == Type::FACE_N_ROT_AA) {
ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
ExprQuaternion q = GetAxisAngleQuaternionExprs(3);
r = ExprVector::From(numPoint);
r = r.Minus(trans);
r = q.Rotate(r);
r = r.Plus(trans);
- } else oops();
+ } else ssassert(false, "Unexpected entity type");
return r;
}
-Vector EntityBase::FaceGetPointNum(void) {
+Vector EntityBase::FaceGetPointNum() const {
Vector r;
- if(type == FACE_NORMAL_PT) {
+ if((type == Type::FACE_NORMAL_PT) || (type==Type::FACE_ROT_NORMAL_PT)) {
r = SK.GetEntity(point[0])->PointGetNum();
- } else if(type == FACE_XPROD) {
+ } else if(type == Type::FACE_XPROD) {
r = numPoint;
- } else if(type == FACE_N_ROT_TRANS) {
+ } else if(type == Type::FACE_N_ROT_TRANS) {
// The numerical point gets the rotation and translation.
Vector trans = Vector::From(param[0], param[1], param[2]);
Quaternion q = Quaternion::From(param[3], param[4], param[5], param[6]);
r = q.Rotate(numPoint);
r = r.Plus(trans);
- } else if(type == FACE_N_TRANS) {
+ } else if(type == Type::FACE_N_ROT_AXIS_TRANS) {
+ Vector offset = Vector::From(param[0], param[1], param[2]);
+ Vector displace = Vector::From(param[4], param[5], param[6])
+ .WithMagnitude(SK.GetParam(param[7])->val).ScaledBy(timesApplied);
+ Quaternion q = PointGetQuaternion();
+ r = numPoint.Minus(offset);
+ r = q.Rotate(r);
+ r = r.Plus(offset).Plus(displace);
+ } else if(type == Type::FACE_N_TRANS) {
Vector trans = Vector::From(param[0], param[1], param[2]);
r = numPoint.Plus(trans.ScaledBy(timesApplied));
- } else if(type == FACE_N_ROT_AA) {
+ } else if(type == Type::FACE_N_ROT_AA) {
Vector trans = Vector::From(param[0], param[1], param[2]);
Quaternion q = GetAxisAngleQuaternion(3);
r = numPoint.Minus(trans);
r = q.Rotate(r);
r = r.Plus(trans);
- } else oops();
+ } else ssassert(false, "Unexpected entity type");
return r;
}
-bool EntityBase::HasEndpoints(void) {
- return (type == LINE_SEGMENT) ||
- (type == CUBIC) ||
- (type == ARC_OF_CIRCLE);
+bool EntityBase::HasEndpoints() const {
+ return (type == Type::LINE_SEGMENT) ||
+ (type == Type::CUBIC) ||
+ (type == Type::ARC_OF_CIRCLE);
}
-Vector EntityBase::EndpointStart() {
- if(type == LINE_SEGMENT) {
+Vector EntityBase::EndpointStart() const {
+ if(type == Type::LINE_SEGMENT) {
return SK.GetEntity(point[0])->PointGetNum();
- } else if(type == CUBIC) {
+ } else if(type == Type::CUBIC) {
return CubicGetStartNum();
- } else if(type == ARC_OF_CIRCLE) {
+ } else if(type == Type::ARC_OF_CIRCLE) {
return SK.GetEntity(point[1])->PointGetNum();
- } else {
- oops();
- }
+ } else ssassert(false, "Unexpected entity type");
}
-Vector EntityBase::EndpointFinish() {
- if(type == LINE_SEGMENT) {
+Vector EntityBase::EndpointFinish() const {
+ if(type == Type::LINE_SEGMENT) {
return SK.GetEntity(point[1])->PointGetNum();
- } else if(type == CUBIC) {
+ } else if(type == Type::CUBIC) {
return CubicGetFinishNum();
- } else if(type == ARC_OF_CIRCLE) {
+ } else if(type == Type::ARC_OF_CIRCLE) {
return SK.GetEntity(point[2])->PointGetNum();
- } else {
- oops();
+ } else ssassert(false, "Unexpected entity type");
+}
+static bool PointInPlane(hEntity h, Vector norm, double distance) {
+ Vector p = SK.GetEntity(h)->PointGetNum();
+ return (fabs(norm.Dot(p) - distance) < LENGTH_EPS);
+}
+bool EntityBase::IsInPlane(Vector norm, double distance) const {
+ switch(type) {
+ case Type::LINE_SEGMENT: {
+ return PointInPlane(point[0], norm, distance)
+ && PointInPlane(point[1], norm, distance);
+ }
+ case Type::CUBIC:
+ case Type::CUBIC_PERIODIC: {
+ bool periodic = type == Type::CUBIC_PERIODIC;
+ int n = periodic ? 3 + extraPoints : extraPoints;
+ int i;
+ for (i=0; i<n; i++) {
+ if (!PointInPlane(point[i], norm, distance)) return false;
+ }
+ return true;
+ }
+
+ case Type::CIRCLE:
+ case Type::ARC_OF_CIRCLE: {
+ // If it is an (arc of) a circle, check whether the normals
+ // are parallel and the mid point is in the plane.
+ Vector n = Normal()->NormalN();
+ if (!norm.Equals(n) && !norm.Equals(n.Negated())) return false;
+ return PointInPlane(point[0], norm, distance);
+ }
+
+ case Type::TTF_TEXT: {
+ Vector n = Normal()->NormalN();
+ if (!norm.Equals(n) && !norm.Equals(n.Negated())) return false;
+ return PointInPlane(point[0], norm, distance)
+ && PointInPlane(point[1], norm, distance);
+ }
+
+ default:
+ return false;
}
}
-void EntityBase::AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index) {
+void EntityBase::RectGetPointsExprs(ExprVector *eb, ExprVector *ec) const {
+ ssassert(type == Type::TTF_TEXT || type == Type::IMAGE,
+ "Unexpected entity type");
+
+ EntityBase *a = SK.GetEntity(point[0]);
+ EntityBase *o = SK.GetEntity(point[1]);
+
+ // Write equations for each point in the current workplane.
+ // This reduces the complexity of resulting equations.
+ ExprVector ea = a->PointGetExprsInWorkplane(workplane);
+ ExprVector eo = o->PointGetExprsInWorkplane(workplane);
+
+ // Take perpendicular vector and scale it by aspect ratio.
+ ExprVector eu = ea.Minus(eo);
+ ExprVector ev = ExprVector::From(eu.y, eu.x->Negate(), eu.z).ScaledBy(Expr::From(aspectRatio));
+
+ *eb = eo.Plus(ev);
+ *ec = eo.Plus(eu).Plus(ev);
+}
+
+void EntityBase::AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index) const {
Equation eq;
eq.e = expr;
eq.h = h.equation(index);
l->Add(&eq);
}
-void EntityBase::GenerateEquations(IdList<Equation,hEquation> *l) {
+void EntityBase::GenerateEquations(IdList<Equation,hEquation> *l) const {
switch(type) {
- case NORMAL_IN_3D: {
+ case Type::NORMAL_IN_3D: {
ExprQuaternion q = NormalGetExprs();
AddEq(l, (q.Magnitude())->Minus(Expr::From(1)), 0);
break;
}
- case ARC_OF_CIRCLE: {
+
+ case Type::ARC_OF_CIRCLE: {
// If this is a copied entity, with its point already fixed
// with respect to each other, then we don't want to generate
// the distance constraint!
- if(SK.GetEntity(point[0])->type != POINT_IN_2D) break;
+ if(SK.GetEntity(point[0])->type != Type::POINT_IN_2D) break;
// If the two endpoints of the arc are constrained coincident
// (to make a complete circle), then our distance constraint
// would be redundant and therefore overconstrain things.
- int i;
- for(i = 0; i < SK.constraint.n; i++) {
- ConstraintBase *c = &(SK.constraint.elem[i]);
- if(c->group.v != group.v) continue;
- if(c->type != Constraint::POINTS_COINCIDENT) continue;
-
- if((c->ptA.v == point[1].v && c->ptB.v == point[2].v) ||
- (c->ptA.v == point[2].v && c->ptB.v == point[1].v))
- {
- break;
- }
+ auto it = std::find_if(SK.constraint.begin(), SK.constraint.end(),
+ [&](ConstraintBase const &con) {
+ return (con.group == group) &&
+ (con.type == Constraint::Type::POINTS_COINCIDENT) &&
+ ((con.ptA == point[1] && con.ptB == point[2]) ||
+ (con.ptA == point[2] && con.ptB == point[1]));
+ });
+ if(it != SK.constraint.end()) {
+ break;
}
- if(i < SK.constraint.n) break;
Expr *ra = Constraint::Distance(workplane, point[0], point[1]);
Expr *rb = Constraint::Distance(workplane, point[0], point[2]);
AddEq(l, ra->Minus(rb), 0);
break;
}
- default:;
- // Most entities do not generate equations.
+
+ case Type::IMAGE:
+ case Type::TTF_TEXT: {
+ if(SK.GetEntity(point[0])->type != Type::POINT_IN_2D) break;
+ EntityBase *b = SK.GetEntity(point[2]);
+ EntityBase *c = SK.GetEntity(point[3]);
+ ExprVector eb = b->PointGetExprsInWorkplane(workplane);
+ ExprVector ec = c->PointGetExprsInWorkplane(workplane);
+
+ ExprVector ebp, ecp;
+ RectGetPointsExprs(&ebp, &ecp);
+
+ ExprVector beq = eb.Minus(ebp);
+ AddEq(l, beq.x, 0);
+ AddEq(l, beq.y, 1);
+ ExprVector ceq = ec.Minus(ecp);
+ AddEq(l, ceq.x, 2);
+ AddEq(l, ceq.y, 3);
+ break;
+ }
+
+ default: // Most entities do not generate equations.
+ break;
}
}
-
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
#include "solvespace.h"
-#ifndef WIN32
-#include <unix/gloffscreen.h>
-#endif
-#include <png.h>
+#include "config.h"
-void SolveSpaceUI::ExportSectionTo(const std::string &filename) {
+void SolveSpaceUI::ExportSectionTo(const Platform::Path &filename) {
Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
gn = gn.WithMagnitude(1);
Group *g = SK.GetGroup(SS.GW.activeGroup);
g->GenerateDisplayItems();
if(g->displayMesh.IsEmpty()) {
- Error("No solid model present; draw one with extrudes and revolves, "
- "or use Export 2d View to export bare lines and curves.");
+ Error(_("No solid model present; draw one with extrudes and revolves, "
+ "or use Export 2d View to export bare lines and curves."));
return;
}
double d;
SS.GW.GroupSelection();
-#define gs (SS.GW.gs)
- if((gs.n == 0 && g->activeWorkplane.v != Entity::FREE_IN_3D.v)) {
+ auto const &gs = SS.GW.gs;
+ if((gs.n == 0 && g->activeWorkplane != Entity::FREE_IN_3D)) {
Entity *wrkpl = SK.GetEntity(g->activeWorkplane);
origin = wrkpl->WorkplaneGetOffset();
n = wrkpl->Normal()->NormalN();
u = ut.WithMagnitude(1);
v = (n.Cross(u)).WithMagnitude(1);
} else {
- Error("Bad selection for export section. Please select:\n\n"
- " * nothing, with an active workplane "
- "(workplane is section plane)\n"
- " * a face (section plane through face)\n"
- " * a point and two line segments "
- "(plane through point and parallel to lines)\n");
+ Error(_("Bad selection for export section. Please select:\n\n"
+ " * nothing, with an active workplane "
+ "(workplane is section plane)\n"
+ " * a face (section plane through face)\n"
+ " * a point and two line segments "
+ "(plane through point and parallel to lines)\n"));
return;
}
SS.GW.ClearSelection();
g->runningMesh.MakeEdgesInPlaneInto(&el, n, d);
// If there's a shell, then grab the edges and possibly Beziers.
- g->runningShell.MakeSectionEdgesInto(n, d,
- &el,
- (SS.exportPwlCurves || fabs(SS.exportOffset) > LENGTH_EPS) ? NULL : &bl);
+ bool export_as_pwl = SS.exportPwlCurves || fabs(SS.exportOffset) > LENGTH_EPS;
+ g->runningShell.MakeSectionEdgesInto(n, d, &el, export_as_pwl ? NULL : &bl);
// All of these are solid model edges, so use the appropriate style.
SEdge *se;
sb->auxA = Style::SOLID_EDGE;
}
- el.CullExtraneousEdges();
- bl.CullIdenticalBeziers();
+ // Remove all overlapping edges/beziers to merge the areas they describe.
+ el.CullExtraneousEdges(/*both=*/true);
+ bl.CullIdenticalBeziers(/*both=*/true);
+
+ // Collect lines and beziers with custom style & export.
+ for(auto &ent : SK.entity) {
+ Entity *e = &ent;
+ if (!e->IsVisible()) continue;
+ if (e->style.v < Style::FIRST_CUSTOM) continue;
+ if (!Style::Exportable(e->style.v)) continue;
+ if (!e->IsInPlane(n,d)) continue;
+ if (export_as_pwl) {
+ e->GenerateEdges(&el);
+ } else {
+ e->GenerateBezierCurves(&bl);
+ }
+ }
+
+ // Only remove half of the overlapping edges/beziers to support TTF Stick Fonts.
+ el.CullExtraneousEdges(/*both=*/false);
+ bl.CullIdenticalBeziers(/*both=*/false);
// And write the edges.
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
bl.Clear();
}
-void SolveSpaceUI::ExportViewOrWireframeTo(const std::string &filename, bool wireframe) {
- int i;
+// This is an awful temporary hack to replace Constraint::GetEdges until we have proper
+// export through Canvas.
+class GetEdgesCanvas : public Canvas {
+public:
+ Camera camera;
+ SEdgeList *edges;
+
+ const Camera &GetCamera() const override {
+ return camera;
+ }
+
+ void DrawLine(const Vector &a, const Vector &b, hStroke hcs) override {
+ edges->AddEdge(a, b, Style::CONSTRAINT);
+ }
+ void DrawEdges(const SEdgeList &el, hStroke hcs) override {
+ for(const SEdge &e : el.l) {
+ edges->AddEdge(e.a, e.b, Style::CONSTRAINT);
+ }
+ }
+ void DrawVectorText(const std::string &text, double height,
+ const Vector &o, const Vector &u, const Vector &v,
+ hStroke hcs) override {
+ auto traceEdge = [&](Vector a, Vector b) { edges->AddEdge(a, b, Style::CONSTRAINT); };
+ VectorFont::Builtin()->Trace(height, o, u, v, text, traceEdge, camera);
+ }
+
+ void DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
+ hFill hcf) override {
+ // Do nothing
+ }
+
+ bool DrawBeziers(const SBezierList &bl, hStroke hcs) override {
+ ssassert(false, "Not implemented");
+ }
+ void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) override {
+ ssassert(false, "Not implemented");
+ }
+ void DrawPoint(const Vector &o, hStroke hcs) override {
+ ssassert(false, "Not implemented");
+ }
+ void DrawPolygon(const SPolygon &p, hFill hcf) override {
+ ssassert(false, "Not implemented");
+ }
+ void DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack = {}) override {
+ ssassert(false, "Not implemented");
+ }
+ void DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) override {
+ ssassert(false, "Not implemented");
+ }
+ void DrawPixmap(std::shared_ptr<const Pixmap> pm,
+ const Vector &o, const Vector &u, const Vector &v,
+ const Point2d &ta, const Point2d &tb, hFill hcf) override {
+ ssassert(false, "Not implemented");
+ }
+ void InvalidatePixmap(std::shared_ptr<const Pixmap> pm) override {
+ ssassert(false, "Not implemented");
+ }
+};
+
+void SolveSpaceUI::ExportViewOrWireframeTo(const Platform::Path &filename, bool exportWireframe) {
SEdgeList edges = {};
SBezierList beziers = {};
if(!out) return;
SS.exportMode = true;
- GenerateAll(GENERATE_ALL);
+ GenerateAll(Generate::ALL);
SMesh *sm = NULL;
- if(SS.GW.showShaded || SS.GW.showHdnLines) {
+ if(SS.GW.showShaded || SS.GW.drawOccludedAs != GraphicsWindow::DrawOccludedAs::VISIBLE) {
Group *g = SK.GetGroup(SS.GW.activeGroup);
g->GenerateDisplayItems();
sm = &(g->displayMesh);
sm = NULL;
}
- for(i = 0; i < SK.entity.n; i++) {
- Entity *e = &(SK.entity.elem[i]);
+ for(auto &entity : SK.entity) {
+ Entity *e = &entity;
if(!e->IsVisible()) continue;
if(e->construction) continue;
}
}
- if(SS.GW.showEdges) {
+ if(SS.GW.showEdges || SS.GW.showOutlines) {
Group *g = SK.GetGroup(SS.GW.activeGroup);
g->GenerateDisplayItems();
- SEdgeList *selr = &(g->displayEdges);
- SEdge *se;
- for(se = selr->l.First(); se; se = selr->l.NextAfter(se)) {
- edges.AddEdge(se->a, se->b, Style::SOLID_EDGE);
+ if(SS.GW.showEdges) {
+ g->displayOutlines.ListTaggedInto(&edges, Style::SOLID_EDGE);
}
}
if(SS.GW.showConstraints) {
if(!out->OutputConstraints(&SK.constraint)) {
+ GetEdgesCanvas canvas = {};
+ canvas.camera = SS.GW.GetCamera();
+ canvas.edges = &edges;
+
// The output format cannot represent constraints directly,
// so convert them to edges.
- Constraint *c;
- for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
- c->GetEdges(&edges);
+ for(Constraint &c : SK.constraint) {
+ c.Draw(Constraint::DrawAs::DEFAULT, &canvas);
}
+
+ canvas.Clear();
}
}
- if(wireframe) {
+ if(exportWireframe) {
Vector u = Vector::From(1.0, 0.0, 0.0),
v = Vector::From(0.0, 1.0, 0.0),
n = Vector::From(0.0, 0.0, 1.0),
}
SS.justExportedInfo.draw = true;
- InvalidateGraphics();
+ GW.Invalidate();
}
edges.Clear();
}
void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm,
- Vector u, Vector v, Vector n,
- Vector origin, double cameraTan,
- VectorFileWriter *out)
+ Vector u, Vector v, Vector n,
+ Vector origin, double cameraTan,
+ VectorFileWriter *out)
{
double s = 1.0 / SS.exportScale;
// Project into the export plane; so when we're done, z doesn't matter,
// and x and y are what goes in the DXF.
- SEdge *e;
- for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
+ for(SEdge *e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
// project into the specified csys, and apply export scale
(e->a) = e->a.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
(e->b) = e->b.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
}
- SBezier *b;
if(sbl) {
- for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
+ for(SBezier *b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
*b = b->InPerspective(u, v, n, origin, cameraTan);
int i;
for(i = 0; i <= b->deg; i++) {
// Generate the edges where a curved surface turns from front-facing
// to back-facing.
- if(SS.GW.showEdges) {
- root->MakeCertainEdgesInto(sel, SKdNode::TURNING_EDGES,
+ if(SS.GW.showEdges || SS.GW.showOutlines) {
+ root->MakeCertainEdgesInto(sel, EdgeKind::TURNING,
/*coplanarIsInter=*/false, NULL, NULL,
GW.showOutlines ? Style::OUTLINE : Style::SOLID_EDGE);
}
SEdgeList edges = {};
// Split the original edge against the mesh
edges.AddEdge(se->a, se->b, se->auxA);
- root->OcclusionTestLine(*se, &edges, cnt, /*removeHidden=*/!SS.GW.showHdnLines);
+ root->OcclusionTestLine(*se, &edges, cnt);
+ if(SS.GW.drawOccludedAs == GraphicsWindow::DrawOccludedAs::STIPPLED) {
+ for(SEdge &se : edges.l) {
+ if(se.tag == 1) {
+ se.auxA = Style::HIDDEN_EDGE;
+ }
+ }
+ } else if(SS.GW.drawOccludedAs == GraphicsWindow::DrawOccludedAs::INVISIBLE) {
+ edges.l.RemoveTagged();
+ }
+
// the occlusion test splits unnecessarily; so fix those
edges.MergeCollinearSegments(se->a, se->b);
cnt++;
// segments with zero-length projections.
sel->l.ClearTags();
for(int i = 0; i < sel->l.n; ++i) {
- SEdge *sei = &sel->l.elem[i];
+ SEdge *sei = &sel->l[i];
hStyle hsi = { (uint32_t)sei->auxA };
Style *si = Style::Get(hsi);
if(sei->tag != 0) continue;
}
for(int j = i + 1; j < sel->l.n; ++j) {
- SEdge *sej = &sel->l.elem[j];
+ SEdge *sej = &sel->l[j];
if(sej->tag != 0) continue;
Vector *pAj = &sej->a;
// We kept the line segments and Beziers separate until now; but put them
// all together, and also project everything into the xy plane, since not
// all export targets ignore the z component of the points.
- for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
+ ssassert(sbl != nullptr, "Adding line segments to beziers assumes bezier list is non-null.");
+ for(SEdge *e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
SBezier sb = SBezier::From(e->a, e->b);
sb.auxA = e->auxA;
sbl->l.Add(&sb);
}
- for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
+ for(SBezier *b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
for(int i = 0; i <= b->deg; i++) {
b->ctrl[i].z = 0;
}
// If possible, then we will assemble these output curves into loops. They
// will then get exported as closed paths.
SBezierLoopSetSet sblss = {};
- SBezierList leftovers = {};
+ SBezierLoopSet leftovers = {};
SSurface srf = SSurface::FromPlane(Vector::From(0, 0, 0),
Vector::From(1, 0, 0),
Vector::From(0, 1, 0));
&allClosed, ¬ClosedAt,
NULL, NULL,
&leftovers);
- for(b = leftovers.l.First(); b; b = leftovers.l.NextAfter(b)) {
- sblss.AddOpenPath(b);
- }
+ sblss.l.Add(&leftovers);
// Now write the lines and triangles to the output file
out->OutputLinesAndMesh(&sblss, &sms);
- leftovers.Clear();
spxyz.Clear();
sblss.Clear();
smp.Clear();
return (mm/25.4)*72;
}
-VectorFileWriter *VectorFileWriter::ForFile(const std::string &filename) {
+VectorFileWriter *VectorFileWriter::ForFile(const Platform::Path &filename) {
VectorFileWriter *ret;
bool needOpen = true;
- if(FilenameHasExtension(filename, ".dxf")) {
+ if(filename.HasExtension("dxf")) {
static DxfFileWriter DxfWriter;
ret = &DxfWriter;
needOpen = false;
- } else if(FilenameHasExtension(filename, ".ps") || FilenameHasExtension(filename, ".eps")) {
+ } else if(filename.HasExtension("ps") || filename.HasExtension("eps")) {
static EpsFileWriter EpsWriter;
ret = &EpsWriter;
- } else if(FilenameHasExtension(filename, ".pdf")) {
+ } else if(filename.HasExtension("pdf")) {
static PdfFileWriter PdfWriter;
ret = &PdfWriter;
- } else if(FilenameHasExtension(filename, ".svg")) {
+ } else if(filename.HasExtension("svg")) {
static SvgFileWriter SvgWriter;
ret = &SvgWriter;
- } else if(FilenameHasExtension(filename, ".plt")||FilenameHasExtension(filename, ".hpgl")) {
+ } else if(filename.HasExtension("plt") || filename.HasExtension("hpgl")) {
static HpglFileWriter HpglWriter;
ret = &HpglWriter;
- } else if(FilenameHasExtension(filename, ".step")||FilenameHasExtension(filename, ".stp")) {
+ } else if(filename.HasExtension("step") || filename.HasExtension("stp")) {
static Step2dFileWriter Step2dWriter;
ret = &Step2dWriter;
- } else if(FilenameHasExtension(filename, ".txt")) {
+ } else if(filename.HasExtension("txt") || filename.HasExtension("ngc")) {
static GCodeFileWriter GCodeWriter;
ret = &GCodeWriter;
} else {
Error("Can't identify output file type from file extension of "
"filename '%s'; try "
- ".step, .stp, .dxf, .svg, .plt, .hpgl, .pdf, .txt, "
+ ".step, .stp, .dxf, .svg, .plt, .hpgl, .pdf, .txt, .ngc, "
".eps, or .ps.",
- filename.c_str());
+ filename.raw.c_str());
return NULL;
}
ret->filename = filename;
if(!needOpen) return ret;
- FILE *f = ssfopen(filename, "wb");
+ FILE *f = OpenFile(filename, "wb");
if(!f) {
- Error("Couldn't write to '%s'", filename.c_str());
+ Error("Couldn't write to '%s'", filename.raw.c_str());
return NULL;
}
ret->f = f;
ptMin.y -= s*SS.exportMargin.bottom;
ptMax.y += s*SS.exportMargin.top;
} else {
- ptMin.x = -(s*SS.exportCanvas.dx);
- ptMin.y = -(s*SS.exportCanvas.dy);
+ ptMin.x = (s*SS.exportCanvas.dx);
+ ptMin.y = (s*SS.exportCanvas.dy);
ptMax.x = ptMin.x + (s*SS.exportCanvas.width);
ptMax.y = ptMin.y + (s*SS.exportCanvas.height);
}
StartFile();
+ if(SS.exportBackgroundColor) {
+ Background(SS.backgroundColor);
+ }
if(sm && SS.exportShadedTriangles) {
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
Triangle(tr);
hStyle hs = { (uint32_t)b->auxA };
Style *stl = Style::Get(hs);
double lineWidth = Style::WidthMm(b->auxA)*s;
- RgbaColor strokeRgb = Style::Color(hs, true);
- RgbaColor fillRgb = Style::FillColor(hs, true);
+ RgbaColor strokeRgb = Style::Color(hs, /*forExport=*/true);
+ RgbaColor fillRgb = Style::FillColor(hs, /*forExport=*/true);
StartPath(strokeRgb, lineWidth, stl->filled, fillRgb, hs);
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
void VectorFileWriter::BezierAsPwl(SBezier *sb) {
List<Vector> lv = {};
sb->MakePwlInto(&lv, SS.ExportChordTolMm());
- int i;
- for(i = 1; i < lv.n; i++) {
- SBezier sb = SBezier::From(lv.elem[i-1], lv.elem[i]);
+
+ for(int i = 1; i < lv.n; i++) {
+ SBezier sb = SBezier::From(lv[i-1], lv[i]);
Bezier(&sb);
}
lv.Clear();
//-----------------------------------------------------------------------------
// Export a triangle mesh, in the requested format.
//-----------------------------------------------------------------------------
-void SolveSpaceUI::ExportMeshTo(const std::string &filename) {
+void SolveSpaceUI::ExportMeshTo(const Platform::Path &filename) {
SS.exportMode = true;
- GenerateAll(GENERATE_ALL);
+ GenerateAll(Generate::ALL);
Group *g = SK.GetGroup(SS.GW.activeGroup);
g->GenerateDisplayItems();
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
if(m->IsEmpty()) {
- Error("Active group mesh is empty; nothing to export.");
+ Error(_("Active group mesh is empty; nothing to export."));
return;
}
- FILE *f = ssfopen(filename, "wb");
+ FILE *f = OpenFile(filename, "wb");
if(!f) {
- Error("Couldn't write to '%s'", filename.c_str());
+ Error("Couldn't write to '%s'", filename.raw.c_str());
return;
}
-
- if(FilenameHasExtension(filename, ".stl")) {
+ ShowNakedEdges(/*reportOnlyWhenNotOkay=*/true);
+ if(filename.HasExtension("stl")) {
ExportMeshAsStlTo(f, m);
- } else if(FilenameHasExtension(filename, ".obj")) {
- ExportMeshAsObjTo(f, m);
- } else if(FilenameHasExtension(filename, ".js") ||
- FilenameHasExtension(filename, ".html")) {
- SEdgeList *e = &(SK.GetGroup(SS.GW.activeGroup)->displayEdges);
+ } else if(filename.HasExtension("obj")) {
+ Platform::Path mtlFilename = filename.WithExtension("mtl");
+ FILE *fMtl = OpenFile(mtlFilename, "wb");
+ if(!fMtl) {
+ Error("Couldn't write to '%s'", filename.raw.c_str());
+ return;
+ }
+
+ fprintf(f, "mtllib %s\n", mtlFilename.FileName().c_str());
+ ExportMeshAsObjTo(f, fMtl, m);
+
+ fclose(fMtl);
+ } else if(filename.HasExtension("q3do")) {
+ ExportMeshAsQ3doTo(f, m);
+ } else if(filename.HasExtension("js") ||
+ filename.HasExtension("html")) {
+ SOutlineList *e = &(SK.GetGroup(SS.GW.activeGroup)->displayOutlines);
ExportMeshAsThreeJsTo(f, filename, m, e);
+ } else if(filename.HasExtension("wrl")) {
+ ExportMeshAsVrmlTo(f, filename, m);
} else {
Error("Can't identify output file type from file extension of "
- "filename '%s'; try .stl, .obj, .js.", filename.c_str());
+ "filename '%s'; try .stl, .obj, .js, .html.", filename.raw.c_str());
}
fclose(f);
SS.justExportedInfo.showOrigin = false;
SS.justExportedInfo.draw = true;
- InvalidateGraphics();
+ GW.Invalidate();
}
//-----------------------------------------------------------------------------
double s = SS.exportScale;
int i;
for(i = 0; i < sm->l.n; i++) {
- STriangle *tr = &(sm->l.elem[i]);
+ STriangle *tr = &(sm->l[i]);
Vector n = tr->Normal().WithMagnitude(1);
float w;
w = (float)n.x; fwrite(&w, 4, 1, f);
}
}
+//-----------------------------------------------------------------------------
+// Export the mesh as a Q3DO (https://github.com/q3k/q3d) file.
+//-----------------------------------------------------------------------------
+
+#include "q3d_object_generated.h"
+void SolveSpaceUI::ExportMeshAsQ3doTo(FILE *f, SMesh *sm) {
+ flatbuffers::FlatBufferBuilder builder(1024);
+ double s = SS.exportScale;
+
+ // Create a material for every colour used, keep note of triangles belonging to color/material.
+ std::map<RgbaColor, flatbuffers::Offset<q3d::Material>, RgbaColorCompare> materials;
+ std::map<RgbaColor, std::vector<flatbuffers::Offset<q3d::Triangle>>, RgbaColorCompare> materialTriangles;
+ for (const STriangle &t : sm->l) {
+ auto color = t.meta.color;
+ if (materials.find(color) == materials.end()) {
+ auto name = builder.CreateString(ssprintf("Color #%02x%02x%02x%02x", color.red, color.green, color.blue, color.alpha));
+ auto co = q3d::CreateColor(builder, color.red, color.green, color.blue, color.alpha);
+ auto mo = q3d::CreateMaterial(builder, name, co);
+ materials.emplace(color, mo);
+ }
+
+ Vector faceNormal = t.Normal();
+ auto a = q3d::Vector3((float)(t.a.x/s), (float)(t.a.y/s), (float)(t.a.z/s));
+ auto b = q3d::Vector3((float)(t.b.x/s), (float)(t.b.y/s), (float)(t.b.z/s));
+ auto c = q3d::Vector3((float)(t.c.x/s), (float)(t.c.y/s), (float)(t.c.z/s));
+ auto fn = q3d::Vector3((float)faceNormal.x, (float)faceNormal.y, (float)faceNormal.x);
+ auto n1 = q3d::Vector3((float)t.normals[0].x, (float)t.normals[0].y, (float)t.normals[0].z);
+ auto n2 = q3d::Vector3((float)t.normals[1].x, (float)t.normals[1].y, (float)t.normals[1].z);
+ auto n3 = q3d::Vector3((float)t.normals[2].x, (float)t.normals[2].y, (float)t.normals[2].z);
+ auto tri = q3d::CreateTriangle(builder, &a, &b, &c, &fn, &n1, &n2, &n3);
+ materialTriangles[color].push_back(tri);
+ }
+
+ // Build all meshes sorted by material.
+ std::vector<flatbuffers::Offset<q3d::Mesh>> meshes;
+ for (auto &it : materials) {
+ auto &mato = it.second;
+ auto to = builder.CreateVector(materialTriangles[it.first]);
+ auto mo = q3d::CreateMesh(builder, to, mato);
+ meshes.push_back(mo);
+ }
+
+ auto mo = builder.CreateVector(meshes);
+ auto o = q3d::CreateObject(builder, mo);
+ q3d::FinishObjectBuffer(builder, o);
+ fwrite(builder.GetBufferPointer(), builder.GetSize(), 1, f);
+}
+
//-----------------------------------------------------------------------------
// Export the mesh as Wavefront OBJ format. This requires us to reduce all the
// identical vertices to the same identifier, so do that first.
//-----------------------------------------------------------------------------
-void SolveSpaceUI::ExportMeshAsObjTo(FILE *f, SMesh *sm) {
- SPointList spl = {};
- STriangle *tr;
- for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
- spl.IncrementTagFor(tr->a);
- spl.IncrementTagFor(tr->b);
- spl.IncrementTagFor(tr->c);
+void SolveSpaceUI::ExportMeshAsObjTo(FILE *fObj, FILE *fMtl, SMesh *sm) {
+ std::map<RgbaColor, std::string, RgbaColorCompare> colors;
+ for(const STriangle &t : sm->l) {
+ RgbaColor color = t.meta.color;
+ if(colors.find(color) == colors.end()) {
+ std::string id = ssprintf("h%02x%02x%02x",
+ color.red,
+ color.green,
+ color.blue);
+ colors.emplace(color, id);
+ }
+ for(int i = 0; i < 3; i++) {
+ fprintf(fObj, "v %.10f %.10f %.10f\n",
+ CO(t.vertices[i].ScaledBy(1 / SS.exportScale)));
+ }
}
- // Output all the vertices.
- SPoint *sp;
- for(sp = spl.l.First(); sp; sp = spl.l.NextAfter(sp)) {
- fprintf(f, "v %.10f %.10f %.10f\r\n",
- sp->p.x / SS.exportScale,
- sp->p.y / SS.exportScale,
- sp->p.z / SS.exportScale);
+ for(auto &it : colors) {
+ fprintf(fMtl, "newmtl %s\n",
+ it.second.c_str());
+ fprintf(fMtl, "Kd %.3f %.3f %.3f\n",
+ it.first.redF(), it.first.greenF(), it.first.blueF());
}
- // And now all the triangular faces, in terms of those vertices. The
- // file format counts from 1, not 0.
- for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
- fprintf(f, "f %d %d %d\r\n",
- spl.IndexForPoint(tr->a) + 1,
- spl.IndexForPoint(tr->b) + 1,
- spl.IndexForPoint(tr->c) + 1);
+ for(const STriangle &t : sm->l) {
+ for(int i = 0; i < 3; i++) {
+ Vector n = t.normals[i].WithMagnitude(1.0);
+ fprintf(fObj, "vn %.10f %.10f %.10f\n",
+ CO(n));
+ }
}
- spl.Clear();
+ RgbaColor currentColor = {};
+ for(int i = 0; i < sm->l.n; i++) {
+ const STriangle &t = sm->l[i];
+ if(!currentColor.Equals(t.meta.color)) {
+ currentColor = t.meta.color;
+ fprintf(fObj, "usemtl %s\n", colors[currentColor].c_str());
+ }
+
+ fprintf(fObj, "f %d//%d %d//%d %d//%d\n",
+ i * 3 + 1, i * 3 + 1,
+ i * 3 + 2, i * 3 + 2,
+ i * 3 + 3, i * 3 + 3);
+ }
}
//-----------------------------------------------------------------------------
// Export the mesh as a JavaScript script, which is compatible with Three.js.
//-----------------------------------------------------------------------------
-void SolveSpaceUI::ExportMeshAsThreeJsTo(FILE *f, const std::string &filename,
- SMesh *sm, SEdgeList *sel)
+void SolveSpaceUI::ExportMeshAsThreeJsTo(FILE *f, const Platform::Path &filename,
+ SMesh *sm, SOutlineList *sol)
{
SPointList spl = {};
STriangle *tr;
- SEdge *e;
Vector bndl, bndh;
- const char htmlbegin0[] = R"(
+ const char htmlbegin[] = R"(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"></meta>
<title>Three.js Solvespace Mesh</title>
- <script src="http://threejs.org/build/three.js"></script>
- <script src="http://hammerjs.github.io/dist/hammer.js"></script>
+ <script id="three-r76.js">%s</script>
+ <script id="hammer-2.0.8.js">%s</script>
+ <script id="SolveSpaceControls.js">%s</script>
<style type="text/css">
body { margin: 0; overflow: hidden; }
</style>
</head>
<body>
<script>
- </script>
- <script>
-window.devicePixelRatio = window.devicePixelRatio || 1;
-
-SolvespaceCamera = function(renderWidth, renderHeight, scale, up, right, offset) {
- THREE.Camera.call(this);
-
- this.type = 'SolvespaceCamera';
-
- this.renderWidth = renderWidth;
- this.renderHeight = renderHeight;
- this.zoomScale = scale; /* Avoid namespace collision w/ THREE.Object.scale */
- this.up = up;
- this.right = right;
- this.offset = offset;
- this.depthBias = 0;
-
- this.updateProjectionMatrix();
- };
-
- SolvespaceCamera.prototype = Object.create(THREE.Camera.prototype);
- SolvespaceCamera.prototype.constructor = SolvespaceCamera;
- SolvespaceCamera.prototype.updateProjectionMatrix = function() {
- var temp = new THREE.Matrix4();
- var offset = new THREE.Matrix4().makeTranslation(this.offset.x, this.offset.y, this.offset.z);
- // Convert to right handed- do up cross right instead.
- var n = new THREE.Vector3().crossVectors(this.up, this.right);
- var rotate = new THREE.Matrix4().makeBasis(this.right, this.up, n);
- rotate.transpose();
- /* FIXME: At some point we ended up using row-major.
- THREE.js wants column major. Scale/depth correct unaffected b/c diagonal
- matrices remain the same when transposed. makeTranslation also makes
- a column-major matrix. */
-
- /* TODO: If we want perspective, we need an additional matrix
- here which will modify w for perspective divide. */
- var scale = new THREE.Matrix4().makeScale(2 * this.zoomScale / this.renderWidth,
- 2 * this.zoomScale / this.renderHeight, this.zoomScale / 30000.0);
-
- temp.multiply(scale);
- temp.multiply(rotate);
- temp.multiply(offset);
-
- this.projectionMatrix.copy(temp);
- };
-
- SolvespaceCamera.prototype.NormalizeProjectionVectors = function() {
- /* After rotating, up and right may no longer be orthogonal.
- However, their cross product will produce the correct
- rotated plane, and we can recover an orthogonal basis. */
- var n = new THREE.Vector3().crossVectors(this.right, this.up);
- this.up = new THREE.Vector3().crossVectors(n, this.right);
- this.right.normalize();
- this.up.normalize();
- };
-
- SolvespaceCamera.prototype.rotate = function(right, up) {
- var oldRight = new THREE.Vector3().copy(this.right).normalize();
- var oldUp = new THREE.Vector3().copy(this.up).normalize();
- this.up.applyAxisAngle(oldRight, up);
- this.right.applyAxisAngle(oldUp, right);
- this.NormalizeProjectionVectors();
- }
-
- SolvespaceCamera.prototype.offsetProj = function(right, up) {
- var shift = new THREE.Vector3(right * this.right.x + up * this.up.x,
- right * this.right.y + up * this.up.y,
- right * this.right.z + up * this.up.z);
- this.offset.add(shift);
- }
-
- /* Calculate the offset in terms of up and right projection vectors
- that will preserve the world coordinates of the current mouse position after
- the zoom. */
- SolvespaceCamera.prototype.zoomTo = function(x, y, delta) {
- // Get offset components in world coordinates, in terms of up/right.
- var projOffsetX = this.offset.dot(this.right);
- var projOffsetY = this.offset.dot(this.up);
-
- /* Remove offset before scaling so, that mouse position changes
- proportionally to the model and independent of current offset. */
- var centerRightI = x/this.zoomScale - projOffsetX;
- var centerUpI = y/this.zoomScale - projOffsetY;
- var zoomFactor;
-
- /* Zoom 20% every 100 delta. */
- if(delta < 0) {
- zoomFactor = (-delta * 0.002 + 1);
- }
- else if(delta > 0) {
- zoomFactor = (delta * (-1.0/600.0) + 1)
- }
- else {
- return;
- }
-
- this.zoomScale = this.zoomScale * zoomFactor;
- var centerRightF = x/this.zoomScale - projOffsetX;
- var centerUpF = y/this.zoomScale - projOffsetY;
-
- this.offset.addScaledVector(this.right, centerRightF - centerRightI);
- this.offset.addScaledVector(this.up, centerUpF - centerUpI);
- }
-
-
- SolvespaceControls = function(object, domElement) {
- var _this = this;
- this.object = object;
- this.domElement = ( domElement !== undefined ) ? domElement : document;
-
- var threePan = new Hammer.Pan({event : 'threepan', pointers : 3, enable : false});
- var panAfterTap = new Hammer.Pan({event : 'panaftertap', enable : false});
-
- this.touchControls = new Hammer.Manager(domElement, {
- recognizers: [
- [Hammer.Pinch, { enable: true }],
- [Hammer.Pan],
- [Hammer.Tap],
- ]
- });
-
- this.touchControls.add(threePan);
- this.touchControls.add(panAfterTap);
-
- var changeEvent = {
- type: 'change'
- };
- var startEvent = {
- type: 'start'
- };
- var endEvent = {
- type: 'end'
- };
-
- var _changed = false;
- var _mouseMoved = false;
- //var _touchPoints = new Array();
- var _offsetPrev = new THREE.Vector2(0, 0);
- var _offsetCur = new THREE.Vector2(0, 0);
- var _rotatePrev = new THREE.Vector2(0, 0);
- var _rotateCur = new THREE.Vector2(0, 0);
-
- // Used during touch events.
- var _rotateOrig = new THREE.Vector2(0, 0);
- var _offsetOrig = new THREE.Vector2(0, 0);
- var _prevScale = 1.0;
-
- this.handleEvent = function(event) {
- if (typeof this[event.type] == 'function') {
- this[event.type](event);
- }
- }
-
- function mousedown(event) {
- event.preventDefault();
- event.stopPropagation();
-
- switch (event.button) {
- case 0:
- _rotateCur.set(event.screenX/window.devicePixelRatio, event.screenY/window.devicePixelRatio);
- _rotatePrev.copy(_rotateCur);
- document.addEventListener('mousemove', mousemove, false);
- document.addEventListener('mouseup', mouseup, false);
- break;
- case 2:
- _offsetCur.set(event.screenX/window.devicePixelRatio, event.screenY/window.devicePixelRatio);
- _offsetPrev.copy(_offsetCur);
- document.addEventListener('mousemove', mousemove, false);
- document.addEventListener('mouseup', mouseup, false);
- break;
- default:
- break;
- }
- }
-
- function wheel( event ) {
- event.preventDefault();
- /* FIXME: Width and height might not be supported universally, but
- can be calculated? */
- var box = _this.domElement.getBoundingClientRect();
- object.zoomTo(event.clientX - box.width/2 - box.left,
- -(event.clientY - box.height/2 - box.top), event.deltaY);
- _changed = true;
- }
-
- function mousemove(event) {
- switch (event.button) {
- case 0:
- _rotateCur.set(event.screenX/window.devicePixelRatio, event.screenY/window.devicePixelRatio);
- var diff = new THREE.Vector2().subVectors(_rotateCur, _rotatePrev)
- .multiplyScalar(1 / object.zoomScale);
- object.rotate(-0.3 * Math.PI / 180 * diff.x * object.zoomScale,
- -0.3 * Math.PI / 180 * diff.y * object.zoomScale);
- _changed = true;
- _rotatePrev.copy(_rotateCur);
- break;
- case 2:
- _mouseMoved = true;
- _offsetCur.set(event.screenX/window.devicePixelRatio, event.screenY/window.devicePixelRatio);
- var diff = new THREE.Vector2().subVectors(_offsetCur, _offsetPrev)
- .multiplyScalar(1 / object.zoomScale);
- object.offsetProj(diff.x, -diff.y);
- _changed = true;
- _offsetPrev.copy(_offsetCur)
- break;
- }
- }
-
-
- function mouseup(event) {
- /* TODO: Opera mouse gestures will intercept this event, making it
- possible to have multiple mousedown events consecutively without
- a corresponding mouseup (so multiple viewports can be rotated/panned
- simultaneously). Disable mouse gestures for now. */
- event.preventDefault();
- event.stopPropagation();
-
- document.removeEventListener('mousemove', mousemove);
- document.removeEventListener('mouseup', mouseup);
-
- _this.dispatchEvent(endEvent);
- }
-
- function pan(event) {
- /* neWcur - prev does not necessarily equal (cur + diff) - prev.
- Floating point is not associative. */
- touchDiff = new THREE.Vector2(event.deltaX, event.deltaY);
- _rotateCur.addVectors(_rotateOrig, touchDiff);
- incDiff = new THREE.Vector2().subVectors(_rotateCur, _rotatePrev)
- .multiplyScalar(1 / object.zoomScale);
- object.rotate(-0.3 * Math.PI / 180 * incDiff.x * object.zoomScale,
- -0.3 * Math.PI / 180 * incDiff.y * object.zoomScale);
- _changed = true;
- _rotatePrev.copy(_rotateCur);
- }
-
- function panstart(event) {
- /* TODO: Dynamically enable pan function? */
- _rotateOrig.copy(_rotateCur);
- }
-
- function pinchstart(event) {
- _prevScale = event.scale;
- }
-
- function pinch(event) {
- /* FIXME: Width and height might not be supported universally, but
- can be calculated? */
- var box = _this.domElement.getBoundingClientRect();
-
- /* 16.6... pixels chosen heuristically... matches my touchpad. */
- if (event.scale < _prevScale) {
- object.zoomTo(event.center.x - box.width/2 - box.left,
- -(event.center.y - box.height/2 - box.top), 100/6.0);
- _changed = true;
- } else if (event.scale > _prevScale) {
- object.zoomTo(event.center.x - box.width/2 - box.left,
- -(event.center.y - box.height/2 - box.top), -100/6.0);
- _changed = true;
- }
-
- _prevScale = event.scale;
- }
-
- /* A tap will enable panning/disable rotate. */
- function tap(event) {
- panAfterTap.set({enable : true});
- _this.touchControls.get('pan').set({enable : false});
- }
-
- function panaftertap(event) {
- touchDiff = new THREE.Vector2(event.deltaX, event.deltaY);
- _offsetCur.addVectors(_offsetOrig, touchDiff);
- incDiff = new THREE.Vector2().subVectors(_offsetCur, _offsetPrev)
- .multiplyScalar(1 / object.zoomScale);
- object.offsetProj(incDiff.x, -incDiff.y);
- _changed = true;
- _offsetPrev.copy(_offsetCur);
- }
-
- function panaftertapstart(event) {
- _offsetOrig.copy(_offsetCur);
- }
-
- function panaftertapend(event) {
- panAfterTap.set({enable : false});
- _this.touchControls.get('pan').set({enable : true});
- }
-
- function contextmenu(event) {
- event.preventDefault();
- }
-
- this.update = function() {
- if (_changed) {
- _this.dispatchEvent(changeEvent);
- _changed = false;
- }
- }
-
- this.domElement.addEventListener('mousedown', mousedown, false);
- this.domElement.addEventListener('wheel', wheel, false);
- this.domElement.addEventListener('contextmenu', contextmenu, false);
-
- /* Hammer.on wraps addEventListener */
- // Rotate
- this.touchControls.on('pan', pan);
- this.touchControls.on('panstart', panstart);
-
- // Zoom
- this.touchControls.on('pinch', pinch);
- this.touchControls.on('pinchstart', pinchstart);
-
- //Pan
- this.touchControls.on('tap', tap);
- this.touchControls.on('panaftertapstart', panaftertapstart);
- this.touchControls.on('panaftertap', panaftertap);
- this.touchControls.on('panaftertapend', panaftertapend);
- }
-
- SolvespaceControls.prototype = Object.create(THREE.EventDispatcher.prototype);
- SolvespaceControls.prototype.constructor = SolvespaceControls;
-
-
- solvespace = function(obj, params) {
- var scene, edgeScene, camera, edgeCamera, renderer;
- var geometry, controls, material, mesh, edges;
- var width, height;
- var directionalLightArray = [];
-
- if (typeof params === "undefined" || !("width" in params)) {
- width = window.innerWidth;
- } else {
- width = params.width;
- }
-
- if (typeof params === "undefined" || !("height" in params)) {
- height = window.innerHeight;
- } else {
- height = params.height;
- }
-
- width *= window.devicePixelRatio;
- height *= window.devicePixelRatio;
-
- domElement = init();
- render();
- return domElement;
-
-
- function init() {
- scene = new THREE.Scene();
- edgeScene = new THREE.Scene();
-
- camera = new SolvespaceCamera(width/window.devicePixelRatio,
- height/window.devicePixelRatio, 5, new THREE.Vector3(0, 1, 0),
- new THREE.Vector3(1, 0, 0), new THREE.Vector3(0, 0, 0));
-
- mesh = createMesh(obj);
- scene.add(mesh);
- edges = createEdges(obj);
- edgeScene.add(edges);
-
- for (var i = 0; i < obj.lights.d.length; i++) {
- var lightColor = new THREE.Color(obj.lights.d[i].intensity,
- obj.lights.d[i].intensity, obj.lights.d[i].intensity);
- var directionalLight = new THREE.DirectionalLight(lightColor, 1);
- directionalLight.position.set(obj.lights.d[i].direction[0],
- obj.lights.d[i].direction[1], obj.lights.d[i].direction[2]);
- directionalLightArray.push(directionalLight);
- scene.add(directionalLight);
- }
-
- var lightColor = new THREE.Color(obj.lights.a, obj.lights.a, obj.lights.a);
- var ambientLight = new THREE.AmbientLight(lightColor.getHex());
- scene.add(ambientLight);
-
- renderer = new THREE.WebGLRenderer({ antialias: true});
- renderer.setSize(width, height);
- renderer.autoClear = false;
- renderer.domElement.style = "width:"+width/window.devicePixelRatio+"px;height:"+height/window.devicePixelRatio+"px;";
-
- controls = new SolvespaceControls(camera, renderer.domElement);
- controls.addEventListener("change", render);
- controls.addEventListener("change", lightUpdate);
-
- animate();
- return renderer.domElement;
- })";
- const char htmlbegin1[] = R"(
- function animate() {
- requestAnimationFrame(animate);
- controls.update();
- }
-
- function render() {
- var context = renderer.getContext();
- camera.updateProjectionMatrix();
- renderer.clear();
-
- context.depthRange(0.1, 1);
- renderer.render(scene, camera);
-
- context.depthRange(0.1-(2/60000.0), 1-(2/60000.0));
- renderer.render(edgeScene, camera);
- }
-
- function lightUpdate() {
- var changeBasis = new THREE.Matrix4();
-
- // The original light positions were in camera space.
- // Project them into standard space using camera's basis
- // vectors (up, target, and their cross product).
- n = new THREE.Vector3().crossVectors(camera.up, camera.right);
- changeBasis.makeBasis(camera.right, camera.up, n);
-
- for (var i = 0; i < 2; i++) {
- var newLightPos = changeBasis.applyToVector3Array(
- [obj.lights.d[i].direction[0], obj.lights.d[i].direction[1],
- obj.lights.d[i].direction[2]]);
- directionalLightArray[i].position.set(newLightPos[0],
- newLightPos[1], newLightPos[2]);
- }
- }
-
- function createMesh(meshObj) {
- var geometry = new THREE.Geometry();
- var materialIndex = 0;
- var materialList = [];
- var opacitiesSeen = {};
-
- for (var i = 0; i < meshObj.points.length; i++) {
- geometry.vertices.push(new THREE.Vector3(meshObj.points[i][0],
- meshObj.points[i][1], meshObj.points[i][2]));
- }
-
- for (var i = 0; i < meshObj.faces.length; i++) {
- var currOpacity = ((meshObj.colors[i] & 0xFF000000) >>> 24) / 255.0;
- if (opacitiesSeen[currOpacity] === undefined) {
- opacitiesSeen[currOpacity] = materialIndex;
- materialIndex++;
- materialList.push(new THREE.MeshLambertMaterial({
- vertexColors: THREE.FaceColors,
- opacity: currOpacity,
- transparent: true,
- side: THREE.DoubleSide
- }));
- }
-
- geometry.faces.push(new THREE.Face3(meshObj.faces[i][0],
- meshObj.faces[i][1], meshObj.faces[i][2],
- [new THREE.Vector3(meshObj.normals[i][0][0],
- meshObj.normals[i][0][1], meshObj.normals[i][0][2]),
- new THREE.Vector3(meshObj.normals[i][1][0],
- meshObj.normals[i][1][1], meshObj.normals[i][1][2]),
- new THREE.Vector3(meshObj.normals[i][2][0],
- meshObj.normals[i][2][1], meshObj.normals[i][2][2])],
- new THREE.Color(meshObj.colors[i] & 0x00FFFFFF),
- opacitiesSeen[currOpacity]));
- }
-
- geometry.computeBoundingSphere();
- return new THREE.Mesh(geometry, new THREE.MultiMaterial(materialList));
- }
-
- function createEdges(meshObj) {
- var geometry = new THREE.Geometry();
- var material = new THREE.LineBasicMaterial();
-
- for (var i = 0; i < meshObj.edges.length; i++) {
- geometry.vertices.push(new THREE.Vector3(meshObj.edges[i][0][0],
- meshObj.edges[i][0][1], meshObj.edges[i][0][2]),
- new THREE.Vector3(meshObj.edges[i][1][0],
- meshObj.edges[i][1][1], meshObj.edges[i][1][2]));
- }
-
- geometry.computeBoundingSphere();
- return new THREE.LineSegments(geometry, material);
- }
- };
)";
const char htmlend[] = R"(
- document.body.appendChild(solvespace(solvespace_model_%s));
+ document.body.appendChild(solvespace(solvespace_model_%s, {
+ scale: %g,
+ offset: new THREE.Vector3(%g, %g, %g),
+ projUp: new THREE.Vector3(%g, %g, %g),
+ projRight: new THREE.Vector3(%g, %g, %g)
+ }));
</script>
</body>
</html>
double largerBoundXY = max((bndh.x - bndl.x), (bndh.y - bndl.y));
double largerBoundZ = max(largerBoundXY, (bndh.z - bndl.z + 1));
- std::string extension = filename,
- noExtFilename = filename;
- size_t dot = noExtFilename.rfind('.');
- extension.erase(0, dot + 1);
- noExtFilename.erase(dot);
-
- std::string baseFilename = noExtFilename;
- size_t lastSlash = baseFilename.rfind(PATH_SEP);
- if(lastSlash == std::string::npos) oops();
- baseFilename.erase(0, lastSlash + 1);
-
- for(size_t i = 0; i < baseFilename.length(); i++) {
- if(!isalpha(baseFilename[i]) &&
- /* also permit UTF-8 */ !((unsigned char)baseFilename[i] >= 0x80))
- baseFilename[i] = '_';
+ std::string basename = filename.FileStem();
+ for(size_t i = 0; i < basename.length(); i++) {
+ if(!(isalnum(basename[i]) || ((unsigned)basename[i] >= 0x80))) {
+ basename[i] = '_';
+ }
}
- if(extension == "html") {
- fputs(htmlbegin0, f);
- fputs(htmlbegin1, f);
+ if(filename.HasExtension("html")) {
+ fprintf(f, htmlbegin,
+ LoadStringFromGzip("threejs/three-r76.js.gz").c_str(),
+ LoadStringFromGzip("threejs/hammer-2.0.8.js.gz").c_str(),
+ LoadString("threejs/SolveSpaceControls.js").c_str());
}
fprintf(f, "var solvespace_model_%s = {\n"
" bounds: {\n"
" x: %f, y: %f, near: %f, far: %f, z: %f, edgeBias: %f\n"
" },\n",
- baseFilename.c_str(),
+ basename.c_str(),
largerBoundXY,
largerBoundXY,
1.0,
// Directional.
int lightCount;
- for(lightCount = 0; lightCount < 2; lightCount++)
- {
+ for(lightCount = 0; lightCount < 2; lightCount++) {
fprintf(f, " {\n"
" intensity: %f, direction: [%f, %f, %f]\n"
" },\n",
fputs(" ],\n"
" edges: [\n", f);
// Output edges. Assume user's model colors do not obscure white edges.
- for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
+ for(const SOutline &so : sol->l) {
+ if(so.tag == 0) continue;
fprintf(f, " [[%f, %f, %f], [%f, %f, %f]],\n",
- e->a.x / SS.exportScale,
- e->a.y / SS.exportScale,
- e->a.z / SS.exportScale,
- e->b.x / SS.exportScale,
- e->b.y / SS.exportScale,
- e->b.z / SS.exportScale);
+ so.a.x / SS.exportScale,
+ so.a.y / SS.exportScale,
+ so.a.z / SS.exportScale,
+ so.b.x / SS.exportScale,
+ so.b.y / SS.exportScale,
+ so.b.z / SS.exportScale);
}
fputs(" ]\n};\n", f);
- if(extension == "html")
- fprintf(f, htmlend, baseFilename.c_str());
+ if(filename.HasExtension("html")) {
+ fprintf(f, htmlend,
+ basename.c_str(),
+ SS.GW.scale,
+ CO(SS.GW.offset),
+ CO(SS.GW.projUp),
+ CO(SS.GW.projRight));
+ }
spl.Clear();
}
//-----------------------------------------------------------------------------
-// Export a view of the model as an image; we just take a screenshot, by
-// rendering the view in the usual way and then copying the pixels.
+// Export the mesh as a VRML text file / WRL.
//-----------------------------------------------------------------------------
-void SolveSpaceUI::ExportAsPngTo(const std::string &filename) {
- int w = (int)SS.GW.width, h = (int)SS.GW.height;
- // No guarantee that the back buffer contains anything valid right now,
- // so repaint the scene. And hide the toolbar too.
- bool prevShowToolbar = SS.showToolbar;
- SS.showToolbar = false;
-#ifndef WIN32
- std::unique_ptr<GLOffscreen> gloffscreen(new GLOffscreen);
- gloffscreen->begin(w, h);
-#endif
- SS.GW.Paint();
- SS.showToolbar = prevShowToolbar;
-
- FILE *f = ssfopen(filename, "wb");
- if(!f) goto err;
-
- png_struct *png_ptr; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
- NULL, NULL, NULL);
- if(!png_ptr) goto err;
-
- png_info *info_ptr; info_ptr = png_create_info_struct(png_ptr);
- if(!png_ptr) goto err;
-
- if(setjmp(png_jmpbuf(png_ptr))) goto err;
-
- png_init_io(png_ptr, f);
-
- // glReadPixels wants to align things on 4-boundaries, and there's 3
- // bytes per pixel. As long as the row width is divisible by 4, all
- // works out.
- w &= ~3; h &= ~3;
-
- png_set_IHDR(png_ptr, info_ptr, w, h,
- 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
- PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT);
-
- png_write_info(png_ptr, info_ptr);
-
- // Get the pixel data from the framebuffer
- uint8_t *pixels; pixels = (uint8_t *)AllocTemporary(3*w*h);
- uint8_t **rowptrs; rowptrs = (uint8_t **)AllocTemporary(h*sizeof(uint8_t *));
- glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels);
-
- int y;
- for(y = 0; y < h; y++) {
- // gl puts the origin at lower left, but png puts it top left
- rowptrs[y] = pixels + ((h - 1) - y)*(3*w);
- }
- png_write_image(png_ptr, rowptrs);
-
- png_write_end(png_ptr, info_ptr);
- png_destroy_write_struct(&png_ptr, &info_ptr);
- fclose(f);
- return;
+void SolveSpaceUI::ExportMeshAsVrmlTo(FILE *f, const Platform::Path &filename, SMesh *sm) {
+ struct STriangleSpan {
+ STriangle *first, *past_last;
+
+ STriangle *begin() const { return first; }
+ STriangle *end() const { return past_last; }
+ };
+
+
+ std::string basename = filename.FileStem();
+ for(auto & c : basename) {
+ if(!(isalnum(c) || ((unsigned)c >= 0x80))) {
+ c = '_';
+ }
+ }
+
+ fprintf(f, "#VRML V2.0 utf8\n"
+ "#Exported from SolveSpace %s\n"
+ "\n"
+ "DEF %s Transform {\n"
+ " children [",
+ PACKAGE_VERSION,
+ basename.c_str());
+
+
+ std::map<std::uint8_t, std::vector<STriangleSpan>> opacities;
+ STriangle *start = sm->l.begin();
+ std::uint8_t last_opacity = start->meta.color.alpha;
+ for(auto & tr : sm->l) {
+ if(tr.meta.color.alpha != last_opacity) {
+ opacities[last_opacity].push_back(STriangleSpan{start, &tr});
+ start = &tr;
+ last_opacity = start->meta.color.alpha;
+ }
+ }
+ opacities[last_opacity].push_back(STriangleSpan{start, sm->l.end()});
+
+ for(auto && op : opacities) {
+ fprintf(f, "\n"
+ " Shape {\n"
+ " appearance Appearance {\n"
+ " material DEF %s_material_%u Material {\n"
+ " diffuseColor %f %f %f\n"
+ " ambientIntensity %f\n"
+ " transparency %f\n"
+ " }\n"
+ " }\n"
+ " geometry IndexedFaceSet {\n"
+ " colorPerVertex TRUE\n"
+ " coord Coordinate { point [\n",
+ basename.c_str(),
+ (unsigned)op.first,
+ SS.ambientIntensity,
+ SS.ambientIntensity,
+ SS.ambientIntensity,
+ SS.ambientIntensity,
+ 1.f - ((float)op.first / 255.0f));
+
+ SPointList spl = {};
+
+ for(const auto & sp : op.second) {
+ for(const auto & tr : sp) {
+ spl.IncrementTagFor(tr.a);
+ spl.IncrementTagFor(tr.b);
+ spl.IncrementTagFor(tr.c);
+ }
+ }
-err:
- Error("Error writing PNG file '%s'", filename.c_str());
- if(f) fclose(f);
- return;
+ // Output all the vertices.
+ for(auto sp : spl.l) {
+ fprintf(f, " %f %f %f,\n",
+ sp.p.x / SS.exportScale,
+ sp.p.y / SS.exportScale,
+ sp.p.z / SS.exportScale);
+ }
+
+ fputs(" ] }\n"
+ " coordIndex [\n", f);
+ // And now all the triangular faces, in terms of those vertices.
+ for(const auto & sp : op.second) {
+ for(const auto & tr : sp) {
+ fprintf(f, " %d, %d, %d, -1,\n",
+ spl.IndexForPoint(tr.a),
+ spl.IndexForPoint(tr.b),
+ spl.IndexForPoint(tr.c));
+ }
+ }
+
+ fputs(" ]\n"
+ " color Color { color [\n", f);
+ // Output triangle colors.
+ std::vector<int> triangle_colour_ids;
+ std::vector<RgbaColor> colours_present;
+ for(const auto & sp : op.second) {
+ for(const auto & tr : sp) {
+ const auto colour_itr = std::find_if(colours_present.begin(), colours_present.end(),
+ [&](const RgbaColor & c) {
+ return c.Equals(tr.meta.color);
+ });
+ if(colour_itr == colours_present.end()) {
+ fprintf(f, " %.10f %.10f %.10f,\n",
+ tr.meta.color.redF(),
+ tr.meta.color.greenF(),
+ tr.meta.color.blueF());
+ triangle_colour_ids.push_back(colours_present.size());
+ colours_present.insert(colours_present.end(), tr.meta.color);
+ } else {
+ triangle_colour_ids.push_back(colour_itr - colours_present.begin());
+ }
+ }
+ }
+
+ fputs(" ] }\n"
+ " colorIndex [\n", f);
+
+ for(auto colour_idx : triangle_colour_ids) {
+ fprintf(f, " %d, %d, %d, -1,\n", colour_idx, colour_idx, colour_idx);
+ }
+
+ fputs(" ]\n"
+ " }\n"
+ " }\n", f);
+
+ spl.Clear();
+ }
+
+ fputs(" ]\n"
+ "}\n", f);
}
+//-----------------------------------------------------------------------------
+// Export a view of the model as an image; we just take a screenshot, by
+// rendering the view in the usual way and then copying the pixels.
+//-----------------------------------------------------------------------------
+void SolveSpaceUI::ExportAsPngTo(const Platform::Path &filename) {
+ screenshotFile = filename;
+ // The rest of the work is done in the next redraw.
+ GW.Invalidate();
+}
//-----------------------------------------------------------------------------
#include "solvespace.h"
-void StepFileWriter::WriteHeader(void) {
+void StepFileWriter::WriteHeader() {
fprintf(f,
"ISO-10303-21;\n"
"HEADER;\n"
// Start the ID somewhere beyond the header IDs.
id = 200;
}
-void StepFileWriter::WriteProductHeader(void) {
+void StepFileWriter::WriteProductHeader() {
fprintf(f,
"#175 = SHAPE_DEFINITION_REPRESENTATION(#176, #169);\n"
"#176 = PRODUCT_DEFINITION_SHAPE('Version', 'Test Part', #177);\n"
}
int StepFileWriter::ExportCurveLoop(SBezierLoop *loop, bool inner) {
- if(loop->l.n < 1) oops();
+ ssassert(loop->l.n >= 1, "Expected at least one loop");
List<int> listOfTrims = {};
- SBezier *sb = &(loop->l.elem[loop->l.n - 1]);
+ SBezier *sb = loop->l.Last();
// Generate "exactly closed" contours, with the same vertex id for the
// finish of a previous edge and the start of the next one. So we need
List<int> listOfLoops = {};
// Create the face outer boundary from the outer loop.
- int fob = ExportCurveLoop(loop, false);
+ int fob = ExportCurveLoop(loop, /*inner=*/false);
listOfLoops.Add(&fob);
// And create the face inner boundaries from any inner loops that
// lie within this contour.
loop = sbls->l.NextAfter(loop);
for(; loop; loop = sbls->l.NextAfter(loop)) {
- int fib = ExportCurveLoop(loop, true);
+ int fib = ExportCurveLoop(loop, /*inner=*/true);
listOfLoops.Add(&fib);
}
}
fprintf(f, "),#%d,.T.);\n", srfid);
- fprintf(f, "\n");
advancedFaces.Add(&advFaceId);
+ // Export the surface color and transparency
+ // https://www.cax-if.org/documents/rec_prac_styling_org_v16.pdf sections 4.4.2 4.2.4 etc.
+ // https://tracker.dev.opencascade.org/view.php?id=31550
+ fprintf(f, "#%d=COLOUR_RGB('',%.2f,%.2f,%.2f);\n", ++id, ss->color.redF(),
+ ss->color.greenF(), ss->color.blueF());
+
+/* // This works in Kisters 3DViewStation but not in KiCAD and Horison EDA,
+ // it seems they do not support transparency so use the more verbose one below
+ fprintf(f, "#%d=SURFACE_STYLE_TRANSPARENT(%.2f);\n", ++id, 1.0 - ss->color.alphaF());
+ ++id;
+ fprintf(f, "#%d=SURFACE_STYLE_RENDERING_WITH_PROPERTIES(.NORMAL_SHADING.,#%d,(#%d));\n",
+ id, id - 2, id - 1);
+ ++id;
+ fprintf(f, "#%d=SURFACE_SIDE_STYLE('',(#%d));\n", id, id - 1);
+*/
+
+ // This works in Horison EDA but is more verbose.
+ ++id;
+ fprintf(f, "#%d=FILL_AREA_STYLE_COLOUR('',#%d);\n", id, id - 1);
+ ++id;
+ fprintf(f, "#%d=FILL_AREA_STYLE('',(#%d));\n", id, id - 1);
+ ++id;
+ fprintf(f, "#%d=SURFACE_STYLE_FILL_AREA(#%d);\n", id, id - 1);
+ fprintf(f, "#%d=SURFACE_STYLE_TRANSPARENT(%.2f);\n", ++id, 1.0 - ss->color.alphaF());
+ ++id;
+ fprintf(f, "#%d=SURFACE_STYLE_RENDERING_WITH_PROPERTIES(.NORMAL_SHADING.,#%d,(#%d));\n", id, id - 5, id - 1);
+ ++id;
+ fprintf(f, "#%d=SURFACE_SIDE_STYLE('',(#%d, #%d));\n", id, id - 3, id - 1);
+
+ ++id;
+ fprintf(f, "#%d=SURFACE_STYLE_USAGE(.BOTH.,#%d);\n", id, id - 1);
+ ++id;
+ fprintf(f, "#%d=PRESENTATION_STYLE_ASSIGNMENT((#%d));\n", id, id - 1);
+ ++id;
+ fprintf(f, "#%d=STYLED_ITEM('',(#%d),#%d);\n", id, id - 1, advFaceId);
+ fprintf(f, "\n");
+
id++;
listOfLoops.Clear();
}
spxyz.Clear();
}
-void StepFileWriter::WriteFooter(void) {
+void StepFileWriter::WriteFooter() {
fprintf(f,
"\n"
"ENDSEC;\n"
);
}
-void StepFileWriter::ExportSurfacesTo(const std::string &filename) {
+void StepFileWriter::ExportSurfacesTo(const Platform::Path &filename) {
Group *g = SK.GetGroup(SS.GW.activeGroup);
SShell *shell = &(g->runningShell);
- if(shell->surface.n == 0) {
+ if(shell->surface.IsEmpty()) {
Error("The model does not contain any surfaces to export.%s",
- g->runningMesh.l.n > 0 ?
- "\n\nThe model does contain triangles from a mesh, but "
- "a triangle mesh cannot be exported as a STEP file. Try "
- "File -> Export Mesh... instead." : "");
+ !g->runningMesh.l.IsEmpty()
+ ? "\n\nThe model does contain triangles from a mesh, but "
+ "a triangle mesh cannot be exported as a STEP file. Try "
+ "File -> Export Mesh... instead."
+ : "");
return;
}
- f = ssfopen(filename, "wb");
+ f = OpenFile(filename, "wb");
if(!f) {
- Error("Couldn't write to '%s'", filename.c_str());
+ Error("Couldn't write to '%s'", filename.raw.c_str());
return;
}
SSurface *ss;
for(ss = shell->surface.First(); ss; ss = shell->surface.NextAfter(ss)) {
- if(ss->trim.n == 0) continue;
+ if(ss->trim.IsEmpty())
+ continue;
// Get all of the loops of Beziers that trim our surface (with each
// Bezier split so that we use the section as t goes from 0 to 1), and
advancedFaces.Clear();
}
-void StepFileWriter::WriteWireframe(void) {
+void StepFileWriter::WriteWireframe() {
fprintf(f, "#%d=GEOMETRIC_CURVE_SET('curves',(", id);
int *c;
for(c = curves.First(); c; c = curves.NextAfter(c)) {
#include <libdxfrw.h>
#include "solvespace.h"
-VectorFileWriter::~VectorFileWriter() {
- // This out-of-line virtual method definition quells the following warning
- // from Clang++: "'VectorFileWriter' has no out-of-line virtual method
- // definitions; its vtable will be emitted in every translation unit
- // [-Wweak-vtables]"
-}
-
-class PolylineBuilder {
-public:
- struct Edge;
-
- struct Vertex {
- Vector pos;
- std::vector<Edge *> edges;
-
- bool getNext(hStyle hs, Vertex **next) {
- auto it = std::find_if(edges.begin(), edges.end(), [&](const Edge *e) {
- return e->tag == 0 && e->style.v == hs.v;
- });
- if(it != edges.end()) {
- (*it)->tag = 1;
- *next = (*it)->getOtherVertex(this);
- return true;
- } else {
- return false;
- }
- }
-
- size_t countEdgesWithTagAndStyle(int tag, hStyle hs) const {
- return std::count_if(edges.begin(), edges.end(), [&](const Edge *e) {
- return e->tag == tag && e->style.v == hs.v;
- });
- }
- };
-
- struct Edge {
- Vertex *a;
- Vertex *b;
- hStyle style;
- int tag;
-
- Vertex *getOtherVertex(Vertex *v) {
- if(a == v) return b;
- if(b == v) return a;
- return NULL;
- }
-
- bool getStartAndNext(Vertex **start, Vertex **next, bool loop) {
- size_t numA = a->countEdgesWithTagAndStyle(0, style);
- size_t numB = b->countEdgesWithTagAndStyle(0, style);
-
- if((numA == 1 && numB > 1) || (loop && numA > 1 && numB > 1)) {
- *start = a;
- *next = b;
- return true;
- }
-
- if(numA > 1 && numB == 1) {
- *start = b;
- *next = a;
- return true;
- }
-
- return false;
- }
- };
-
- struct VectorHash {
- size_t operator()(const Vector &v) const {
- static const size_t size = std::numeric_limits<size_t>::max() / 2 - 1;
- static const double eps = (4.0 * LENGTH_EPS);
-
- double x = fabs(v.x) / eps;
- double y = fabs(v.y) / eps;
-
- size_t xs = size_t(fmod(x, double(size)));
- size_t ys = size_t(fmod(y, double(size)));
-
- return ys * size + xs;
- }
- };
-
- struct VectorPred {
- bool operator()(Vector a, Vector b) const {
- return a.Equals(b, LENGTH_EPS);
- }
- };
-
- std::unordered_map<Vector, Vertex *, VectorHash, VectorPred> vertices;
- std::vector<Edge *> edges;
-
- ~PolylineBuilder() {
- clear();
- }
-
- void clear() {
- for(Edge *e : edges) {
- delete e;
- }
- edges.clear();
-
- for(auto &v : vertices) {
- delete v.second;
- }
- vertices.clear();
- }
-
- Vertex *addVertex(const Vector &pos) {
- auto it = vertices.find(pos);
- if(it != vertices.end()) {
- return it->second;
- }
-
- Vertex *result = new Vertex;
- result->pos = pos;
- vertices.emplace(pos, result);
-
- return result;
- }
-
- Edge *addEdge(const Vector &p0, const Vector &p1, int style) {
- Vertex *v0 = addVertex(p0);
- Vertex *v1 = addVertex(p1);
- if(v0 == v1) return NULL;
-
- Edge *edge = new Edge { v0, v1, hStyle { (uint32_t)style }, 0 };
- edges.push_back(edge);
-
- v0->edges.push_back(edge);
- v1->edges.push_back(edge);
-
- return edge;
- }
-};
-
//-----------------------------------------------------------------------------
// Routines for DXF export
//-----------------------------------------------------------------------------
class DxfWriteInterface : public DRW_Interface {
+public:
DxfFileWriter *writer;
- dxfRW *dxf;
+ dxfRW *dxf;
+
+ std::set<std::string> messages;
static DRW_Coord toCoord(const Vector &v) {
return DRW_Coord(v.x, v.y, v.z);
return writer->Transform(v);
}
-public:
- DxfWriteInterface(DxfFileWriter *w, dxfRW *dxfrw) :
- writer(w), dxf(dxfrw) {}
-
- virtual void writeTextstyles() {
+ void writeTextstyles() override {
DRW_Textstyle ts;
ts.name = "unicode";
ts.font = "unicode";
dxf->writeTextstyle(&ts);
}
- virtual void writeLayers() {
+ void writeLayers() override {
DRW_Layer layer;
layer.name = "dimensions";
}
}
- virtual void writeLTypes() {
- for(int i = 0; i <= Style::LAST_STIPPLE; i++) {
+ void writeLTypes() override {
+ for(uint32_t i = 0; i <= (uint32_t)StipplePattern::LAST; i++) {
+ StipplePattern st = (StipplePattern)i;
DRW_LType type;
// LibreCAD requires the line type to have one of these exact names,
// or otherwise it overwrites it with its own (continuous) style.
- type.name = DxfFileWriter::lineTypeName(i);
+ type.name = DxfFileWriter::lineTypeName(st);
double sw = 1.0;
- switch(i) {
- case Style::STIPPLE_CONTINUOUS:
+ switch(st) {
+ case StipplePattern::CONTINUOUS:
+ break;
+
+ case StipplePattern::SHORT_DASH:
+ type.path.push_back(sw);
+ type.path.push_back(-sw * 2.0);
break;
- case Style::STIPPLE_DASH:
+ case StipplePattern::DASH:
type.path.push_back(sw);
type.path.push_back(-sw);
break;
- case Style::STIPPLE_LONG_DASH:
+ case StipplePattern::LONG_DASH:
type.path.push_back(sw * 2.0);
type.path.push_back(-sw);
break;
- case Style::STIPPLE_DASH_DOT:
+ case StipplePattern::DASH_DOT:
type.path.push_back(sw);
type.path.push_back(-sw);
type.path.push_back(0.0);
type.path.push_back(-sw);
break;
- case Style::STIPPLE_DOT:
+ case StipplePattern::DOT:
type.path.push_back(sw);
type.path.push_back(0.0);
break;
- case Style::STIPPLE_DASH_DOT_DOT:
+ case StipplePattern::DASH_DOT_DOT:
type.path.push_back(sw);
type.path.push_back(-sw);
type.path.push_back(0.0);
type.path.push_back(0.0);
type.path.push_back(-sw);
break;
+
+ case StipplePattern::FREEHAND:
+ case StipplePattern::ZIGZAG:
+ // Not implemented; exported as continuous.
+ break;
}
dxf->writeLineType(&type);
}
for(DxfFileWriter::BezierPath &path : writer->paths) {
for(SBezier *sb : path.beziers) {
if(sb->deg != 1) continue;
- builder.addEdge(sb->ctrl[0], sb->ctrl[1], sb->auxA);
+ builder.AddEdge(sb->ctrl[0], sb->ctrl[1], (uint32_t)sb->auxA);
}
}
- bool found;
- bool loop = false;
- do {
- found = false;
- for(PolylineBuilder::Edge *e : builder.edges) {
- if(e->tag != 0) continue;
-
- PolylineBuilder::Vertex *start;
- PolylineBuilder::Vertex *next;
- if(!e->getStartAndNext(&start, &next, loop)) continue;
- found = true;
- e->tag = 1;
-
- DRW_Polyline polyline;
- assignEntityDefaults(&polyline, e->style);
- polyline.vertlist.push_back(
- new DRW_Vertex(start->pos.x, start->pos.y, start->pos.z, 0.0));
- polyline.vertlist.push_back(
- new DRW_Vertex(next->pos.x, next->pos.y, next->pos.z, 0.0));
- while(next->getNext(e->style, &next)) {
- polyline.vertlist.push_back(
- new DRW_Vertex(next->pos.x, next->pos.y, next->pos.z, 0.0));
- }
- dxf->writePolyline(&polyline);
- }
+ DRW_Polyline polyline;
- if(!found && !loop) {
- loop = true;
- found = true;
+ auto startFunc = [&](PolylineBuilder::Vertex *start,
+ PolylineBuilder::Vertex *next,
+ PolylineBuilder::Edge *e) {
+ hStyle hs = { e->kind };
+ polyline = {};
+ assignEntityDefaults(&polyline, hs);
+
+ if(!(EXACT(start->pos.z == 0.0) && EXACT(next->pos.z == 0.0))) {
+ polyline.flags |= 8 /* 3d polyline */;
+ }
+ polyline.vertlist.push_back(
+ new DRW_Vertex(start->pos.x, start->pos.y, start->pos.z, 0.0));
+ polyline.vertlist.push_back(
+ new DRW_Vertex(next->pos.x, next->pos.y, next->pos.z, 0.0));
+ };
+
+ auto nextFunc = [&](PolylineBuilder::Vertex *next, PolylineBuilder::Edge *e) {
+ if(!EXACT(next->pos.z == 0.0)) {
+ polyline.flags |= 8 /* 3d polyline */;
}
- } while(found);
+ polyline.vertlist.push_back(
+ new DRW_Vertex(next->pos.x, next->pos.y, next->pos.z, 0.0));
+ };
- for(PolylineBuilder::Edge *e : builder.edges) {
- if(e->tag != 0) continue;
- writeLine(e->a->pos, e->b->pos, e->style);
- }
+ auto endFunc = [&]() {
+ dxf->writePolyline(&polyline);
+ };
+
+ auto aloneFunc = [&](PolylineBuilder::Edge *e) {
+ hStyle hs = { e->kind };
+ writeLine(e->a->pos, e->b->pos, hs);
+ };
+
+ builder.Generate(startFunc, nextFunc, aloneFunc, endFunc);
}
- virtual void writeEntities() {
+ void writeEntities() override {
writePolylines();
for(DxfFileWriter::BezierPath &path : writer->paths) {
for(c = writer->constraint->First(); c; c = writer->constraint->NextAfter(c)) {
if(!writer->NeedToOutput(c)) continue;
switch(c->type) {
- case Constraint::PT_PT_DISTANCE: {
+ case Constraint::Type::PT_PT_DISTANCE: {
Vector ap = SK.GetEntity(c->ptA)->PointGetNum();
Vector bp = SK.GetEntity(c->ptB)->PointGetNum();
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(c->disp.offset);
break;
}
- case Constraint::PT_LINE_DISTANCE: {
+ case Constraint::Type::PT_LINE_DISTANCE: {
Vector pt = SK.GetEntity(c->ptA)->PointGetNum();
Entity *line = SK.GetEntity(c->entityA);
Vector lA = SK.GetEntity(line->point[0])->PointGetNum();
break;
}
- case Constraint::DIAMETER: {
+ case Constraint::Type::DIAMETER: {
Entity *circle = SK.GetEntity(c->entityA);
Vector center = SK.GetEntity(circle->point[0])->PointGetNum();
Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum();
break;
}
- case Constraint::ANGLE: {
+ case Constraint::Type::ANGLE: {
Entity *a = SK.GetEntity(c->entityA);
Entity *b = SK.GetEntity(c->entityB);
ref = pi.Plus(bisect.WithMagnitude(c->disp.offset.Magnitude()));
- // Get lines agian to write exact line.
+ // Get lines again to write exact line.
a0 = a->VectorGetStartPoint();
b0 = b->VectorGetStartPoint();
da = a->VectorGetNum();
break;
}
- case Constraint::COMMENT: {
+ case Constraint::Type::COMMENT: {
Style *st = SK.style.FindById(c->GetStyle());
writeText(xfrm(c->disp.offset), c->Label(),
Style::TextHeight(c->GetStyle()) / SS.GW.scale,
st->textAngle, st->textOrigin, c->GetStyle());
break;
}
+
+ default:
+ // Other types of constraints do not have a DXF dimension equivalent.
+ break;
}
}
}
void assignEntityDefaults(DRW_Entity *entity, hStyle hs) {
Style *s = Style::Get(hs);
- RgbaColor color = s->Color(hs, true);
+ RgbaColor color = Style::Color(hs, /*forExport=*/true);
entity->color24 = color.ToPackedIntBGRA();
entity->color = findDxfColor(color);
entity->layer = s->DescriptionString();
entity->lineType = DxfFileWriter::lineTypeName(s->stippleType);
entity->ltypeScale = Style::StippleScaleMm(s->h);
entity->setWidthMm(Style::WidthMm(hs.v));
+
+ if(s->stippleType == StipplePattern::FREEHAND) {
+ messages.insert(_("freehand lines were replaced with continuous lines"));
+ } else if(s->stippleType == StipplePattern::ZIGZAG) {
+ messages.insert(_("zigzag lines were replaced with continuous lines"));
+ }
}
void assignDimensionDefaults(DRW_Dimension *dimension, hStyle hs) {
DRW_Polyline polyline;
assignEntityDefaults(&polyline, hs);
for(int i = 0; i < lv.n; i++) {
- Vector *v = &lv.elem[i];
+ Vector *v = &lv[i];
DRW_Vertex *vertex = new DRW_Vertex(v->x, v->y, v->z, 0.0);
polyline.vertlist.push_back(vertex);
}
spline->knotslist.push_back(1.0);
spline->knotslist.push_back(1.0);
spline->knotslist.push_back(1.0);
- } else {
- oops();
- }
+ } else ssassert(false, "Unexpected degree of spline");
}
void writeSpline(SBezier *sb) {
// Rational bezier
// We'd like to export rational beziers exactly, but the resulting DXF
// files can only be read by AutoCAD; LibreCAD/QCad simply do not
- // implement the feature. So, export as piecewise linear for compatiblity.
+ // implement the feature. So, export as piecewise linear for compatibility.
writeBezierAsPwl(sb);
} else {
// Any other curve
}
void writeText(Vector textp, const std::string &text,
- double height, double angle, int origin, hStyle hs) {
+ double height, double angle, Style::TextOrigin origin, hStyle hs) {
DRW_Text txt;
assignEntityDefaults(&txt, hs);
txt.layer = "text";
txt.height = height;
txt.angle = angle;
txt.alignH = DRW_Text::HCenter;
- if(origin & Style::ORIGIN_LEFT) {
+ if((uint32_t)origin & (uint32_t)Style::TextOrigin::LEFT) {
txt.alignH = DRW_Text::HLeft;
- } else if(origin & Style::ORIGIN_RIGHT) {
+ } else if((uint32_t)origin & (uint32_t)Style::TextOrigin::RIGHT) {
txt.alignH = DRW_Text::HRight;
}
txt.alignV = DRW_Text::VMiddle;
- if(origin & Style::ORIGIN_TOP) {
+ if((uint32_t)origin & (uint32_t)Style::TextOrigin::TOP) {
txt.alignV = DRW_Text::VTop;
- } else if(origin & Style::ORIGIN_BOT) {
+ } else if((uint32_t)origin & (uint32_t)Style::TextOrigin::BOT) {
txt.alignV = DRW_Text::VBaseLine;
}
dxf->writeText(&txt);
return true;
}
-void DxfFileWriter::StartFile(void) {
+void DxfFileWriter::StartFile() {
paths.clear();
}
+void DxfFileWriter::Background(RgbaColor color) {
+}
+
void DxfFileWriter::StartPath(RgbaColor strokeRgb, double lineWidth,
bool filled, RgbaColor fillRgb, hStyle hs)
{
void DxfFileWriter::FinishAndCloseFile() {
dxfRW dxf;
- DxfWriteInterface interface(this, &dxf);
+ DxfWriteInterface interface = {};
+ interface.writer = this;
+ interface.dxf = &dxf;
+
std::stringstream stream;
dxf.write(stream, &interface, DRW::AC1021, /*bin=*/false);
paths.clear();
constraint = NULL;
if(!WriteFile(filename, stream.str())) {
- Error("Couldn't write to '%s'", filename.c_str());
+ Error("Couldn't write to '%s'", filename.raw.c_str());
return;
}
+
+ if(!interface.messages.empty()) {
+ std::string text = _("Some aspects of the drawing have no DXF equivalent and "
+ "were not exported:\n");
+ for(const std::string &message : interface.messages) {
+ text += " * " + message + "\n";
+ }
+ Message(text.c_str());
+ }
}
bool DxfFileWriter::NeedToOutput(Constraint *c) {
switch(c->type) {
- case Constraint::PT_PT_DISTANCE:
- case Constraint::PT_LINE_DISTANCE:
- case Constraint::DIAMETER:
- case Constraint::ANGLE:
- case Constraint::COMMENT:
+ case Constraint::Type::PT_PT_DISTANCE:
+ case Constraint::Type::PT_LINE_DISTANCE:
+ case Constraint::Type::DIAMETER:
+ case Constraint::Type::ANGLE:
+ case Constraint::Type::COMMENT:
return c->IsVisible();
+
+ default: // See writeEntities().
+ break;
}
return false;
}
-const char *DxfFileWriter::lineTypeName(int stippleType) {
+const char *DxfFileWriter::lineTypeName(StipplePattern stippleType) {
switch(stippleType) {
- case Style::STIPPLE_CONTINUOUS: return "CONTINUOUS";
- case Style::STIPPLE_DASH: return "DASHED";
- case Style::STIPPLE_LONG_DASH: return "DASHEDX2";
- case Style::STIPPLE_DASH_DOT: return "DASHDOT";
- case Style::STIPPLE_DASH_DOT_DOT: return "DIVIDE";
- case Style::STIPPLE_DOT: return "DOT";
- case Style::STIPPLE_FREEHAND: return "CONTINUOUS";
- case Style::STIPPLE_ZIGZAG: return "CONTINUOUS";
+ case StipplePattern::CONTINUOUS: return "CONTINUOUS";
+ case StipplePattern::SHORT_DASH: return "DASHED";
+ case StipplePattern::DASH: return "DASHED";
+ case StipplePattern::LONG_DASH: return "DASHEDX2";
+ case StipplePattern::DASH_DOT: return "DASHDOT";
+ case StipplePattern::DASH_DOT_DOT: return "DIVIDE";
+ case StipplePattern::DOT: return "DOT";
+
+ case StipplePattern::FREEHAND:
+ case StipplePattern::ZIGZAG:
+ /* no corresponding DXF line type */
+ break;
}
return "CONTINUOUS";
// Routines for EPS output
//-----------------------------------------------------------------------------
-static std::string MakeStipplePattern(int pattern, double scale, char delimiter,
+static std::string MakeStipplePattern(StipplePattern pattern, double scale, char delimiter,
bool inkscapeWorkaround = false) {
scale /= 2.0;
std::string result;
switch(pattern) {
- case Style::STIPPLE_CONTINUOUS:
- case Style::STIPPLE_FREEHAND:
- case Style::STIPPLE_ZIGZAG:
+ case StipplePattern::CONTINUOUS:
return "";
- case Style::STIPPLE_DASH:
+ case StipplePattern::SHORT_DASH:
+ result = ssprintf("%.3f_%.3f", scale, scale * 2.0);
+ break;
+ case StipplePattern::DASH:
result = ssprintf("%.3f_%.3f", scale, scale);
break;
- case Style::STIPPLE_DASH_DOT:
+ case StipplePattern::DASH_DOT:
result = ssprintf("%.3f_%.3f_%.6f_%.3f",
scale, scale * 0.5, zero, scale * 0.5);
break;
- case Style::STIPPLE_DASH_DOT_DOT:
+ case StipplePattern::DASH_DOT_DOT:
result = ssprintf("%.3f_%.3f_%.6f_%.3f_%.6f_%.3f",
scale, scale * 0.5, zero,
scale * 0.5, scale * 0.5, zero);
break;
- case Style::STIPPLE_DOT:
+ case StipplePattern::DOT:
result = ssprintf("%.6f_%.3f", zero, scale * 0.5);
break;
- case Style::STIPPLE_LONG_DASH:
+ case StipplePattern::LONG_DASH:
result = ssprintf("%.3f_%.3f", scale * 2.0, scale * 0.5);
break;
- default:
- oops();
+ case StipplePattern::FREEHAND:
+ case StipplePattern::ZIGZAG:
+ ssassert(false, "Freehand and zigzag export not implemented");
}
std::replace(result.begin(), result.end(), '_', delimiter);
return result;
}
-void EpsFileWriter::StartFile(void) {
+void EpsFileWriter::StartFile() {
fprintf(f,
"%%!PS-Adobe-2.0\r\n"
"%%%%Creator: SolveSpace\r\n"
MmToPts(ptMax.y - ptMin.y));
}
+void EpsFileWriter::Background(RgbaColor color) {
+ double width = ptMax.x - ptMin.x;
+ double height = ptMax.y - ptMin.y;
+
+ fprintf(f,
+"%.3f %.3f %.3f setrgbcolor\r\n"
+"newpath\r\n"
+" %.3f %.3f moveto\r\n"
+" %.3f %.3f lineto\r\n"
+" %.3f %.3f lineto\r\n"
+" %.3f %.3f lineto\r\n"
+" closepath\r\n"
+"gsave fill grestore\r\n",
+ color.redF(), color.greenF(), color.blueF(),
+ MmToPts(0), MmToPts(0),
+ MmToPts(width), MmToPts(0),
+ MmToPts(width), MmToPts(height),
+ MmToPts(0), MmToPts(height));
+
+ // same issue with cracks, stroke it to avoid them
+ double sw = max(width, height) / 1000;
+ fprintf(f,
+"1 setlinejoin\r\n"
+"1 setlinecap\r\n"
+"%.3f setlinewidth\r\n"
+"gsave stroke grestore\r\n",
+ MmToPts(sw));
+}
+
void EpsFileWriter::StartPath(RgbaColor strokeRgb, double lineWidth,
bool filled, RgbaColor fillRgb, hStyle hs)
{
void EpsFileWriter::FinishPath(RgbaColor strokeRgb, double lineWidth,
bool filled, RgbaColor fillRgb, hStyle hs)
{
- int pattern = Style::PatternType(hs);
+ StipplePattern pattern = Style::PatternType(hs);
double stippleScale = MmToPts(Style::StippleScaleMm(hs));
fprintf(f, " %.3f setlinewidth\r\n"
" gsave stroke grestore\r\n",
MmToPts(lineWidth),
strokeRgb.redF(), strokeRgb.greenF(), strokeRgb.blueF(),
- MakeStipplePattern(pattern, stippleScale, ' ').c_str()
- );
+ MakeStipplePattern(pattern, stippleScale, ' ').c_str());
if(filled) {
fprintf(f, " %.3f %.3f %.3f setrgbcolor\r\n"
" gsave fill grestore\r\n",
}
}
-void EpsFileWriter::FinishAndCloseFile(void) {
+void EpsFileWriter::FinishAndCloseFile() {
fprintf(f,
"\r\n"
"grestore\r\n"
// Routines for PDF output, some extra complexity because we have to generate
// a correct xref table.
//-----------------------------------------------------------------------------
-void PdfFileWriter::StartFile(void) {
+void PdfFileWriter::StartFile() {
if((ptMax.x - ptMin.x) > 200*25.4 ||
(ptMax.y - ptMin.y) > 200*25.4)
{
- Message("PDF page size exceeds 200 by 200 inches; many viewers may "
- "reject this file.");
+ Message(_("PDF page size exceeds 200 by 200 inches; many viewers may "
+ "reject this file."));
}
fprintf(f,
bodyStart = (uint32_t)ftell(f);
}
-void PdfFileWriter::FinishAndCloseFile(void) {
+void PdfFileWriter::FinishAndCloseFile() {
uint32_t bodyEnd = (uint32_t)ftell(f);
fprintf(f,
}
+void PdfFileWriter::Background(RgbaColor color) {
+ double width = ptMax.x - ptMin.x;
+ double height = ptMax.y - ptMin.y;
+ double sw = max(width, height) / 1000;
+
+ fprintf(f,
+"1 J 1 j\r\n"
+"%.3f %.3f %.3f RG\r\n"
+"%.3f %.3f %.3f rg\r\n"
+"%.3f w\r\n"
+"%.3f %.3f m\r\n"
+"%.3f %.3f l\r\n"
+"%.3f %.3f l\r\n"
+"%.3f %.3f l\r\n"
+"b\r\n",
+ color.redF(), color.greenF(), color.blueF(),
+ color.redF(), color.greenF(), color.blueF(),
+ MmToPts(sw),
+ MmToPts(0), MmToPts(0),
+ MmToPts(width), MmToPts(0),
+ MmToPts(width), MmToPts(height),
+ MmToPts(0), MmToPts(height));
+}
+
void PdfFileWriter::StartPath(RgbaColor strokeRgb, double lineWidth,
bool filled, RgbaColor fillRgb, hStyle hs)
{
- int pattern = Style::PatternType(hs);
+ StipplePattern pattern = Style::PatternType(hs);
double stippleScale = MmToPts(Style::StippleScaleMm(hs));
fprintf(f, "1 J 1 j " // round endcaps and joins
//-----------------------------------------------------------------------------
// Routines for SVG output
//-----------------------------------------------------------------------------
-void SvgFileWriter::StartFile(void) {
+void SvgFileWriter::StartFile() {
fprintf(f,
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" "
"\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\r\n"
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
fprintf(f, "stroke-width:%f;\r\n", sw);
fprintf(f, "}\r\n");
- for(int i = 0; i < SK.style.n; i++) {
- Style *s = &SK.style.elem[i];
- RgbaColor strokeRgb = Style::Color(s->h, true);
- int pattern = Style::PatternType(s->h);
+ for(auto &style : SK.style) {
+ Style *s = &style;
+
+ RgbaColor strokeRgb = Style::Color(s->h, /*forExport=*/true);
+ StipplePattern pattern = Style::PatternType(s->h);
double stippleScale = Style::StippleScaleMm(s->h);
fprintf(f, ".s%x {\r\n", s->h.v);
fprintf(f, "]]></style>\r\n");
}
+void SvgFileWriter::Background(RgbaColor color) {
+ fprintf(f,
+"<style><![CDATA[\r\n"
+"svg {\r\n"
+"background-color:#%02x%02x%02x;\r\n"
+"}\r\n"
+"]]></style>\r\n",
+ color.red, color.green, color.blue);
+}
+
void SvgFileWriter::StartPath(RgbaColor strokeRgb, double lineWidth,
bool filled, RgbaColor fillRgb, hStyle hs)
{
}
}
-void SvgFileWriter::FinishAndCloseFile(void) {
+void SvgFileWriter::FinishAndCloseFile() {
fprintf(f, "\r\n</svg>\r\n");
fclose(f);
}
return mm*40;
}
-void HpglFileWriter::StartFile(void) {
+void HpglFileWriter::StartFile() {
fprintf(f, "IN;\r\n");
fprintf(f, "SP1;\r\n");
}
+void HpglFileWriter::Background(RgbaColor color) {
+}
+
void HpglFileWriter::StartPath(RgbaColor strokeRgb, double lineWidth,
bool filled, RgbaColor fillRgb, hStyle hs)
{
}
}
-void HpglFileWriter::FinishAndCloseFile(void) {
+void HpglFileWriter::FinishAndCloseFile() {
fclose(f);
}
// multiple passes, and to specify the feeds and depth; those parameters get
// set in the configuration screen.
//-----------------------------------------------------------------------------
-void GCodeFileWriter::StartFile(void) {
+void GCodeFileWriter::StartFile() {
sel = {};
}
void GCodeFileWriter::StartPath(RgbaColor strokeRgb, double lineWidth,
bool filled, RgbaColor fillRgb, hStyle hs)
{
}
+void GCodeFileWriter::Background(RgbaColor color) {
+}
void GCodeFileWriter::FinishPath(RgbaColor strokeRgb, double lineWidth,
bool filled, RgbaColor fillRgb, hStyle hs)
{
}
}
-void GCodeFileWriter::FinishAndCloseFile(void) {
+void GCodeFileWriter::FinishAndCloseFile() {
SPolygon sp = {};
sel.AssemblePolygon(&sp, NULL);
// Routine for STEP output; just a wrapper around the general STEP stuff that
// can also be used for surfaces or 3d curves.
//-----------------------------------------------------------------------------
-void Step2dFileWriter::StartFile(void) {
+void Step2dFileWriter::StartFile() {
sfw = {};
sfw.f = f;
sfw.WriteHeader();
}
+void Step2dFileWriter::Background(RgbaColor color) {
+}
+
void Step2dFileWriter::Triangle(STriangle *tr) {
}
sfw.curves.Add(&c);
}
-void Step2dFileWriter::FinishAndCloseFile(void) {
+void Step2dFileWriter::FinishAndCloseFile() {
sfw.WriteWireframe();
sfw.WriteFooter();
fclose(f);
return ve;
}
-ExprVector ExprVector::Minus(ExprVector b) {
+ExprVector ExprVector::Minus(ExprVector b) const {
ExprVector r;
r.x = x->Minus(b.x);
r.y = y->Minus(b.y);
return r;
}
-ExprVector ExprVector::Plus(ExprVector b) {
+ExprVector ExprVector::Plus(ExprVector b) const {
ExprVector r;
r.x = x->Plus(b.x);
r.y = y->Plus(b.y);
return r;
}
-Expr *ExprVector::Dot(ExprVector b) {
+Expr *ExprVector::Dot(ExprVector b) const {
Expr *r;
r = x->Times(b.x);
r = r->Plus(y->Times(b.y));
return r;
}
-ExprVector ExprVector::Cross(ExprVector b) {
+ExprVector ExprVector::Cross(ExprVector b) const {
ExprVector r;
r.x = (y->Times(b.z))->Minus(z->Times(b.y));
r.y = (z->Times(b.x))->Minus(x->Times(b.z));
return r;
}
-ExprVector ExprVector::ScaledBy(Expr *s) {
+ExprVector ExprVector::ScaledBy(Expr *s) const {
ExprVector r;
r.x = x->Times(s);
r.y = y->Times(s);
return r;
}
-ExprVector ExprVector::WithMagnitude(Expr *s) {
+ExprVector ExprVector::WithMagnitude(Expr *s) const {
Expr *m = Magnitude();
return ScaledBy(s->Div(m));
}
-Expr *ExprVector::Magnitude(void) {
+Expr *ExprVector::Magnitude() const {
Expr *r;
r = x->Square();
r = r->Plus(y->Square());
return r->Sqrt();
}
-Vector ExprVector::Eval(void) {
+Vector ExprVector::Eval() const {
Vector r;
r.x = x->Eval();
r.y = y->Eval();
return qe;
}
-ExprVector ExprQuaternion::RotationU(void) {
+ExprVector ExprQuaternion::RotationU() const {
ExprVector u;
Expr *two = Expr::From(2);
return u;
}
-ExprVector ExprQuaternion::RotationV(void) {
+ExprVector ExprQuaternion::RotationV() const {
ExprVector v;
Expr *two = Expr::From(2);
return v;
}
-ExprVector ExprQuaternion::RotationN(void) {
+ExprVector ExprQuaternion::RotationN() const {
ExprVector n;
Expr *two = Expr::From(2);
return n;
}
-ExprVector ExprQuaternion::Rotate(ExprVector p) {
+ExprVector ExprQuaternion::Rotate(ExprVector p) const {
// Express the point in the new basis
return (RotationU().ScaledBy(p.x)).Plus(
RotationV().ScaledBy(p.y)).Plus(
RotationN().ScaledBy(p.z));
}
-ExprQuaternion ExprQuaternion::Times(ExprQuaternion b) {
+ExprQuaternion ExprQuaternion::Times(ExprQuaternion b) const {
Expr *sa = w, *sb = b.w;
ExprVector va = { vx, vy, vz };
ExprVector vb = { b.vx, b.vy, b.vz };
return r;
}
-Expr *ExprQuaternion::Magnitude(void) {
+Expr *ExprQuaternion::Magnitude() const {
return ((w ->Square())->Plus(
(vx->Square())->Plus(
(vy->Square())->Plus(
Expr *Expr::From(hParam p) {
Expr *r = AllocExpr();
- r->op = PARAM;
+ r->op = Op::PARAM;
r->parh = p;
return r;
}
}
Expr *r = AllocExpr();
- r->op = CONSTANT;
+ r->op = Op::CONSTANT;
r->v = v;
return r;
}
-Expr *Expr::AnyOp(int newOp, Expr *b) {
+Expr *Expr::AnyOp(Op newOp, Expr *b) {
Expr *r = AllocExpr();
r->op = newOp;
r->a = this;
return r;
}
-int Expr::Children(void) {
+int Expr::Children() const {
switch(op) {
- case PARAM:
- case PARAM_PTR:
- case CONSTANT:
+ case Op::PARAM:
+ case Op::PARAM_PTR:
+ case Op::CONSTANT:
+ case Op::VARIABLE:
return 0;
- case PLUS:
- case MINUS:
- case TIMES:
- case DIV:
+ case Op::PLUS:
+ case Op::MINUS:
+ case Op::TIMES:
+ case Op::DIV:
return 2;
- case NEGATE:
- case SQRT:
- case SQUARE:
- case SIN:
- case COS:
- case ASIN:
- case ACOS:
+ case Op::NEGATE:
+ case Op::SQRT:
+ case Op::SQUARE:
+ case Op::SIN:
+ case Op::COS:
+ case Op::ASIN:
+ case Op::ACOS:
return 1;
-
- default: oops();
}
+ ssassert(false, "Unexpected operation");
}
-int Expr::Nodes(void) {
+int Expr::Nodes() const {
switch(Children()) {
case 0: return 1;
case 1: return 1 + a->Nodes();
case 2: return 1 + a->Nodes() + b->Nodes();
- default: oops();
+ default: ssassert(false, "Unexpected children count");
}
}
-Expr *Expr::DeepCopy(void) {
+Expr *Expr::DeepCopy() const {
Expr *n = AllocExpr();
*n = *this;
int c = n->Children();
}
Expr *Expr::DeepCopyWithParamsAsPointers(IdList<Param,hParam> *firstTry,
- IdList<Param,hParam> *thenTry)
+ IdList<Param,hParam> *thenTry) const
{
Expr *n = AllocExpr();
- if(op == PARAM) {
+ if(op == Op::PARAM) {
// A param that is referenced by its hParam gets rewritten to go
// straight in to the parameter table with a pointer, or simply
// into a constant if it's already known.
Param *p = firstTry->FindByIdNoOops(parh);
if(!p) p = thenTry->FindById(parh);
if(p->known) {
- n->op = CONSTANT;
+ n->op = Op::CONSTANT;
n->v = p->val;
} else {
- n->op = PARAM_PTR;
+ n->op = Op::PARAM_PTR;
n->parp = p;
}
return n;
return n;
}
-double Expr::Eval(void) {
+double Expr::Eval() const {
switch(op) {
- case PARAM: return SK.GetParam(parh)->val;
- case PARAM_PTR: return parp->val;
-
- case CONSTANT: return v;
-
- case PLUS: return a->Eval() + b->Eval();
- case MINUS: return a->Eval() - b->Eval();
- case TIMES: return a->Eval() * b->Eval();
- case DIV: return a->Eval() / b->Eval();
-
- case NEGATE: return -(a->Eval());
- case SQRT: return sqrt(a->Eval());
- case SQUARE: { double r = a->Eval(); return r*r; }
- case SIN: return sin(a->Eval());
- case COS: return cos(a->Eval());
- case ACOS: return acos(a->Eval());
- case ASIN: return asin(a->Eval());
-
- default: oops();
+ case Op::PARAM: return SK.GetParam(parh)->val;
+ case Op::PARAM_PTR: return parp->val;
+
+ case Op::CONSTANT: return v;
+ case Op::VARIABLE: ssassert(false, "Not supported yet");
+
+ case Op::PLUS: return a->Eval() + b->Eval();
+ case Op::MINUS: return a->Eval() - b->Eval();
+ case Op::TIMES: return a->Eval() * b->Eval();
+ case Op::DIV: return a->Eval() / b->Eval();
+
+ case Op::NEGATE: return -(a->Eval());
+ case Op::SQRT: return sqrt(a->Eval());
+ case Op::SQUARE: { double r = a->Eval(); return r*r; }
+ case Op::SIN: return sin(a->Eval());
+ case Op::COS: return cos(a->Eval());
+ case Op::ACOS: return acos(a->Eval());
+ case Op::ASIN: return asin(a->Eval());
}
+ ssassert(false, "Unexpected operation");
}
-Expr *Expr::PartialWrt(hParam p) {
+Expr *Expr::PartialWrt(hParam p) const {
Expr *da, *db;
switch(op) {
- case PARAM_PTR: return From(p.v == parp->h.v ? 1 : 0);
- case PARAM: return From(p.v == parh.v ? 1 : 0);
+ case Op::PARAM_PTR: return From(p == parp->h ? 1 : 0);
+ case Op::PARAM: return From(p == parh ? 1 : 0);
- case CONSTANT: return From(0.0);
+ case Op::CONSTANT: return From(0.0);
+ case Op::VARIABLE: ssassert(false, "Not supported yet");
- case PLUS: return (a->PartialWrt(p))->Plus(b->PartialWrt(p));
- case MINUS: return (a->PartialWrt(p))->Minus(b->PartialWrt(p));
+ case Op::PLUS: return (a->PartialWrt(p))->Plus(b->PartialWrt(p));
+ case Op::MINUS: return (a->PartialWrt(p))->Minus(b->PartialWrt(p));
- case TIMES:
+ case Op::TIMES:
da = a->PartialWrt(p);
db = b->PartialWrt(p);
return (a->Times(db))->Plus(b->Times(da));
- case DIV:
+ case Op::DIV:
da = a->PartialWrt(p);
db = b->PartialWrt(p);
return ((da->Times(b))->Minus(a->Times(db)))->Div(b->Square());
- case SQRT:
+ case Op::SQRT:
return (From(0.5)->Div(a->Sqrt()))->Times(a->PartialWrt(p));
- case SQUARE:
+ case Op::SQUARE:
return (From(2.0)->Times(a))->Times(a->PartialWrt(p));
- case NEGATE: return (a->PartialWrt(p))->Negate();
- case SIN: return (a->Cos())->Times(a->PartialWrt(p));
- case COS: return ((a->Sin())->Times(a->PartialWrt(p)))->Negate();
+ case Op::NEGATE: return (a->PartialWrt(p))->Negate();
+ case Op::SIN: return (a->Cos())->Times(a->PartialWrt(p));
+ case Op::COS: return ((a->Sin())->Times(a->PartialWrt(p)))->Negate();
- case ASIN:
+ case Op::ASIN:
return (From(1)->Div((From(1)->Minus(a->Square()))->Sqrt()))
->Times(a->PartialWrt(p));
- case ACOS:
+ case Op::ACOS:
return (From(-1)->Div((From(1)->Minus(a->Square()))->Sqrt()))
->Times(a->PartialWrt(p));
-
- default: oops();
}
+ ssassert(false, "Unexpected operation");
}
-uint64_t Expr::ParamsUsed(void) {
+uint64_t Expr::ParamsUsed() const {
uint64_t r = 0;
- if(op == PARAM) r |= ((uint64_t)1 << (parh.v % 61));
- if(op == PARAM_PTR) r |= ((uint64_t)1 << (parp->h.v % 61));
+ if(op == Op::PARAM) r |= ((uint64_t)1 << (parh.v % 61));
+ if(op == Op::PARAM_PTR) r |= ((uint64_t)1 << (parp->h.v % 61));
int c = Children();
if(c >= 1) r |= a->ParamsUsed();
return r;
}
-bool Expr::DependsOn(hParam p) {
- if(op == PARAM) return (parh.v == p.v);
- if(op == PARAM_PTR) return (parp->h.v == p.v);
+bool Expr::DependsOn(hParam p) const {
+ if(op == Op::PARAM) return (parh == p);
+ if(op == Op::PARAM_PTR) return (parp->h == p);
int c = Children();
if(c == 1) return a->DependsOn(p);
bool Expr::Tol(double a, double b) {
return fabs(a - b) < 0.001;
}
-Expr *Expr::FoldConstants(void) {
+Expr *Expr::FoldConstants() {
Expr *n = AllocExpr();
*n = *this;
if(c >= 2) n->b = b->FoldConstants();
switch(op) {
- case PARAM_PTR:
- case PARAM:
- case CONSTANT:
+ case Op::PARAM_PTR:
+ case Op::PARAM:
+ case Op::CONSTANT:
+ case Op::VARIABLE:
break;
- case MINUS:
- case TIMES:
- case DIV:
- case PLUS:
+ case Op::MINUS:
+ case Op::TIMES:
+ case Op::DIV:
+ case Op::PLUS:
// If both ops are known, then we can evaluate immediately
- if(n->a->op == CONSTANT && n->b->op == CONSTANT) {
+ if(n->a->op == Op::CONSTANT && n->b->op == Op::CONSTANT) {
double nv = n->Eval();
- n->op = CONSTANT;
+ n->op = Op::CONSTANT;
n->v = nv;
break;
}
// x + 0 = 0 + x = x
- if(op == PLUS && n->b->op == CONSTANT && Tol(n->b->v, 0)) {
+ if(op == Op::PLUS && n->b->op == Op::CONSTANT && Tol(n->b->v, 0)) {
*n = *(n->a); break;
}
- if(op == PLUS && n->a->op == CONSTANT && Tol(n->a->v, 0)) {
+ if(op == Op::PLUS && n->a->op == Op::CONSTANT && Tol(n->a->v, 0)) {
*n = *(n->b); break;
}
// 1*x = x*1 = x
- if(op == TIMES && n->b->op == CONSTANT && Tol(n->b->v, 1)) {
+ if(op == Op::TIMES && n->b->op == Op::CONSTANT && Tol(n->b->v, 1)) {
*n = *(n->a); break;
}
- if(op == TIMES && n->a->op == CONSTANT && Tol(n->a->v, 1)) {
+ if(op == Op::TIMES && n->a->op == Op::CONSTANT && Tol(n->a->v, 1)) {
*n = *(n->b); break;
}
// 0*x = x*0 = 0
- if(op == TIMES && n->b->op == CONSTANT && Tol(n->b->v, 0)) {
- n->op = CONSTANT; n->v = 0; break;
+ if(op == Op::TIMES && n->b->op == Op::CONSTANT && Tol(n->b->v, 0)) {
+ n->op = Op::CONSTANT; n->v = 0; break;
}
- if(op == TIMES && n->a->op == CONSTANT && Tol(n->a->v, 0)) {
- n->op = CONSTANT; n->v = 0; break;
+ if(op == Op::TIMES && n->a->op == Op::CONSTANT && Tol(n->a->v, 0)) {
+ n->op = Op::CONSTANT; n->v = 0; break;
}
break;
- case SQRT:
- case SQUARE:
- case NEGATE:
- case SIN:
- case COS:
- case ASIN:
- case ACOS:
- if(n->a->op == CONSTANT) {
+ case Op::SQRT:
+ case Op::SQUARE:
+ case Op::NEGATE:
+ case Op::SIN:
+ case Op::COS:
+ case Op::ASIN:
+ case Op::ACOS:
+ if(n->a->op == Op::CONSTANT) {
double nv = n->Eval();
- n->op = CONSTANT;
+ n->op = Op::CONSTANT;
n->v = nv;
}
break;
-
- default: oops();
}
return n;
}
void Expr::Substitute(hParam oldh, hParam newh) {
- if(op == PARAM_PTR) oops();
+ ssassert(op != Op::PARAM_PTR, "Expected an expression that refer to params via handles");
- if(op == PARAM && parh.v == oldh.v) {
+ if(op == Op::PARAM && parh == oldh) {
parh = newh;
}
int c = Children();
//-----------------------------------------------------------------------------
const hParam Expr::NO_PARAMS = { 0 };
const hParam Expr::MULTIPLE_PARAMS = { 1 };
-hParam Expr::ReferencedParams(ParamList *pl) {
- if(op == PARAM) {
+hParam Expr::ReferencedParams(ParamList *pl) const {
+ if(op == Op::PARAM) {
if(pl->FindByIdNoOops(parh)) {
return parh;
} else {
return NO_PARAMS;
}
}
- if(op == PARAM_PTR) oops();
+ ssassert(op != Op::PARAM_PTR, "Expected an expression that refer to params via handles");
int c = Children();
if(c == 0) {
hParam pa, pb;
pa = a->ReferencedParams(pl);
pb = b->ReferencedParams(pl);
- if(pa.v == NO_PARAMS.v) {
+ if(pa == NO_PARAMS) {
return pb;
- } else if(pb.v == NO_PARAMS.v) {
+ } else if(pb == NO_PARAMS) {
return pa;
- } else if(pa.v == pb.v) {
+ } else if(pa == pb) {
return pa; // either, doesn't matter
} else {
return MULTIPLE_PARAMS;
}
- } else oops();
+ } else ssassert(false, "Unexpected children count");
}
// Routines to pretty-print an expression. Mostly for debugging.
//-----------------------------------------------------------------------------
-std::string Expr::Print(void) {
-
+std::string Expr::Print() const {
char c;
switch(op) {
- case PARAM: return ssprintf("param(%08x)", parh.v);
- case PARAM_PTR: return ssprintf("param(p%08x)", parp->h.v);
+ case Op::PARAM: return ssprintf("param(%08x)", parh.v);
+ case Op::PARAM_PTR: return ssprintf("param(p%08x)", parp->h.v);
- case CONSTANT: return ssprintf("%.3f", v);
+ case Op::CONSTANT: return ssprintf("%.3f", v);
+ case Op::VARIABLE: return "(var)";
- case PLUS: c = '+'; goto p;
- case MINUS: c = '-'; goto p;
- case TIMES: c = '*'; goto p;
- case DIV: c = '/'; goto p;
+ case Op::PLUS: c = '+'; goto p;
+ case Op::MINUS: c = '-'; goto p;
+ case Op::TIMES: c = '*'; goto p;
+ case Op::DIV: c = '/'; goto p;
p:
return "(" + a->Print() + " " + c + " " + b->Print() + ")";
break;
- case NEGATE: return "(- " + a->Print() + ")";
- case SQRT: return "(sqrt " + a->Print() + ")";
- case SQUARE: return "(square " + a->Print() + ")";
- case SIN: return "(sin " + a->Print() + ")";
- case COS: return "(cos " + a->Print() + ")";
- case ASIN: return "(asin " + a->Print() + ")";
- case ACOS: return "(acos " + a->Print() + ")";
-
- default: oops();
+ case Op::NEGATE: return "(- " + a->Print() + ")";
+ case Op::SQRT: return "(sqrt " + a->Print() + ")";
+ case Op::SQUARE: return "(square " + a->Print() + ")";
+ case Op::SIN: return "(sin " + a->Print() + ")";
+ case Op::COS: return "(cos " + a->Print() + ")";
+ case Op::ASIN: return "(asin " + a->Print() + ")";
+ case Op::ACOS: return "(acos " + a->Print() + ")";
}
+ ssassert(false, "Unexpected operation");
}
// to provide calculator type functionality wherever numbers are entered.
//-----------------------------------------------------------------------------
-#define MAX_UNPARSED 1024
-static Expr *Unparsed[MAX_UNPARSED];
-static int UnparsedCnt, UnparsedP;
+class ExprParser {
+public:
+ enum class TokenType {
+ ERROR = 0,
-static Expr *Operands[MAX_UNPARSED];
-static int OperandsP;
+ PAREN_LEFT,
+ PAREN_RIGHT,
+ BINARY_OP,
+ UNARY_OP,
+ OPERAND,
-static Expr *Operators[MAX_UNPARSED];
-static int OperatorsP;
+ END,
+ };
-void Expr::PushOperator(Expr *e) {
- if(OperatorsP >= MAX_UNPARSED) throw "operator stack full!";
- Operators[OperatorsP++] = e;
-}
-Expr *Expr::TopOperator(void) {
- if(OperatorsP <= 0) throw "operator stack empty (get top)";
- return Operators[OperatorsP-1];
-}
-Expr *Expr::PopOperator(void) {
- if(OperatorsP <= 0) throw "operator stack empty (pop)";
- return Operators[--OperatorsP];
-}
-void Expr::PushOperand(Expr *e) {
- if(OperandsP >= MAX_UNPARSED) throw "operand stack full";
- Operands[OperandsP++] = e;
-}
-Expr *Expr::PopOperand(void) {
- if(OperandsP <= 0) throw "operand stack empty";
- return Operands[--OperandsP];
+ class Token {
+ public:
+ TokenType type;
+ Expr *expr;
+
+ static Token From(TokenType type = TokenType::ERROR, Expr *expr = NULL);
+ static Token From(TokenType type, Expr::Op op);
+ bool IsError() const { return type == TokenType::ERROR; }
+ };
+
+ std::string::const_iterator it, end;
+ std::vector<Token> stack;
+
+ char ReadChar();
+ char PeekChar();
+
+ std::string ReadWord();
+ void SkipSpace();
+
+ Token PopOperator(std::string *error);
+ Token PopOperand(std::string *error);
+
+ int Precedence(Token token);
+ Token LexNumber(std::string *error);
+ Token Lex(std::string *error);
+ bool Reduce(std::string *error);
+ bool Parse(std::string *error, size_t reduceUntil = 0);
+
+ static Expr *Parse(const std::string &input, std::string *error);
+};
+
+ExprParser::Token ExprParser::Token::From(TokenType type, Expr *expr) {
+ Token t;
+ t.type = type;
+ t.expr = expr;
+ return t;
}
-Expr *Expr::Next(void) {
- if(UnparsedP >= UnparsedCnt) return NULL;
- return Unparsed[UnparsedP];
+
+ExprParser::Token ExprParser::Token::From(TokenType type, Expr::Op op) {
+ Token t;
+ t.type = type;
+ t.expr = Expr::AllocExpr();
+ t.expr->op = op;
+ return t;
}
-void Expr::Consume(void) {
- if(UnparsedP >= UnparsedCnt) throw "no token to consume";
- UnparsedP++;
+
+char ExprParser::ReadChar() {
+ return *it++;
}
-int Expr::Precedence(Expr *e) {
- if(e->op == ALL_RESOLVED) return -1; // never want to reduce this marker
- if(e->op != BINARY_OP && e->op != UNARY_OP) oops();
+char ExprParser::PeekChar() {
+ if(it == end) {
+ return '\0';
+ } else {
+ return *it;
+ }
+}
- switch(e->c) {
- case 'q':
- case 's':
- case 'c':
- case 'n': return 30;
+std::string ExprParser::ReadWord() {
+ std::string s;
- case '*':
- case '/': return 20;
+ while(char c = PeekChar()) {
+ if(!isalnum(c)) break;
+ s.push_back(ReadChar());
+ }
- case '+':
- case '-': return 10;
+ return s;
+}
- default: oops();
+void ExprParser::SkipSpace() {
+ while(char c = PeekChar()) {
+ if(!isspace(c)) break;
+ ReadChar();
}
}
-void Expr::Reduce(void) {
- Expr *a, *b;
-
- Expr *op = PopOperator();
- Expr *n;
- int o;
- switch(op->c) {
- case '+': o = PLUS; goto c;
- case '-': o = MINUS; goto c;
- case '*': o = TIMES; goto c;
- case '/': o = DIV; goto c;
-c:
- b = PopOperand();
- a = PopOperand();
- n = a->AnyOp(o, b);
- break;
+ExprParser::Token ExprParser::LexNumber(std::string *error) {
+ std::string s;
- case 'n': n = PopOperand()->Negate(); break;
- case 'q': n = PopOperand()->Sqrt(); break;
- case 's': n = (PopOperand()->Times(Expr::From(PI/180)))->Sin(); break;
- case 'c': n = (PopOperand()->Times(Expr::From(PI/180)))->Cos(); break;
-
- default: oops();
+ while(char c = PeekChar()) {
+ if(!((c >= '0' && c <= '9') || c == 'e' || c == 'E' || c == '.' || c == '_')) break;
+ if(c == '_') {
+ ReadChar();
+ continue;
+ }
+ s.push_back(ReadChar());
}
- PushOperand(n);
-}
-void Expr::ReduceAndPush(Expr *n) {
- while(Precedence(n) <= Precedence(TopOperator())) {
- Reduce();
+ char *endptr;
+ double d = strtod(s.c_str(), &endptr);
+
+ Token t = Token::From();
+ if(endptr == s.c_str() + s.size()) {
+ t = Token::From(TokenType::OPERAND, Expr::Op::CONSTANT);
+ t.expr->v = d;
+ } else {
+ *error = "'" + s + "' is not a valid number";
}
- PushOperator(n);
-}
-
-void Expr::Parse(void) {
- Expr *e = AllocExpr();
- e->op = ALL_RESOLVED;
- PushOperator(e);
-
- for(;;) {
- Expr *n = Next();
- if(!n) throw "end of expression unexpected";
-
- if(n->op == CONSTANT) {
- PushOperand(n);
- Consume();
- } else if(n->op == PAREN && n->c == '(') {
- Consume();
- Parse();
- n = Next();
- if(n->op != PAREN || n->c != ')') throw "expected: )";
- Consume();
- } else if(n->op == UNARY_OP) {
- PushOperator(n);
- Consume();
- continue;
- } else if(n->op == BINARY_OP && n->c == '-') {
- // The minus sign is special, because it might be binary or
- // unary, depending on context.
- n->op = UNARY_OP;
- n->c = 'n';
- PushOperator(n);
- Consume();
- continue;
+ return t;
+}
+
+ExprParser::Token ExprParser::Lex(std::string *error) {
+ SkipSpace();
+
+ Token t = Token::From();
+ char c = PeekChar();
+ if(isupper(c)) {
+ std::string n = ReadWord();
+ t = Token::From(TokenType::OPERAND, Expr::Op::VARIABLE);
+ } else if(isalpha(c)) {
+ std::string s = ReadWord();
+ if(s == "sqrt") {
+ t = Token::From(TokenType::UNARY_OP, Expr::Op::SQRT);
+ } else if(s == "square") {
+ t = Token::From(TokenType::UNARY_OP, Expr::Op::SQUARE);
+ } else if(s == "sin") {
+ t = Token::From(TokenType::UNARY_OP, Expr::Op::SIN);
+ } else if(s == "cos") {
+ t = Token::From(TokenType::UNARY_OP, Expr::Op::COS);
+ } else if(s == "asin") {
+ t = Token::From(TokenType::UNARY_OP, Expr::Op::ASIN);
+ } else if(s == "acos") {
+ t = Token::From(TokenType::UNARY_OP, Expr::Op::ACOS);
+ } else if(s == "pi") {
+ t = Token::From(TokenType::OPERAND, Expr::Op::CONSTANT);
+ t.expr->v = PI;
} else {
- throw "expected expression";
+ *error = "'" + s + "' is not a valid variable, function or constant";
}
-
- n = Next();
- if(n && n->op == BINARY_OP) {
- ReduceAndPush(n);
- Consume();
+ } else if(isdigit(c) || c == '.') {
+ return LexNumber(error);
+ } else if(ispunct(c)) {
+ ReadChar();
+ if(c == '+') {
+ t = Token::From(TokenType::BINARY_OP, Expr::Op::PLUS);
+ } else if(c == '-') {
+ t = Token::From(TokenType::BINARY_OP, Expr::Op::MINUS);
+ } else if(c == '*') {
+ t = Token::From(TokenType::BINARY_OP, Expr::Op::TIMES);
+ } else if(c == '/') {
+ t = Token::From(TokenType::BINARY_OP, Expr::Op::DIV);
+ } else if(c == '(') {
+ t = Token::From(TokenType::PAREN_LEFT);
+ } else if(c == ')') {
+ t = Token::From(TokenType::PAREN_RIGHT);
} else {
- break;
+ *error = "'" + std::string(1, c) + "' is not a valid operator";
}
+ } else if(c == '\0') {
+ t = Token::From(TokenType::END);
+ } else {
+ *error = "Unexpected character '" + std::string(1, c) + "'";
}
- while(TopOperator()->op != ALL_RESOLVED) {
- Reduce();
+ return t;
+}
+
+ExprParser::Token ExprParser::PopOperand(std::string *error) {
+ Token t = Token::From();
+ if(stack.empty() || stack.back().type != TokenType::OPERAND) {
+ *error = "Expected an operand";
+ } else {
+ t = stack.back();
+ stack.pop_back();
}
- PopOperator(); // discard the ALL_RESOLVED marker
+ return t;
}
-void Expr::Lex(const char *in) {
- while(*in) {
- if(UnparsedCnt >= MAX_UNPARSED) throw "too long";
+ExprParser::Token ExprParser::PopOperator(std::string *error) {
+ Token t = Token::From();
+ if(stack.empty() || (stack.back().type != TokenType::UNARY_OP &&
+ stack.back().type != TokenType::BINARY_OP)) {
+ *error = "Expected an operator";
+ } else {
+ t = stack.back();
+ stack.pop_back();
+ }
+ return t;
+}
+
+int ExprParser::Precedence(Token t) {
+ ssassert(t.type == TokenType::BINARY_OP ||
+ t.type == TokenType::UNARY_OP ||
+ t.type == TokenType::OPERAND,
+ "Unexpected token type");
+
+ if(t.type == TokenType::UNARY_OP) {
+ return 30;
+ } else if(t.expr->op == Expr::Op::TIMES ||
+ t.expr->op == Expr::Op::DIV) {
+ return 20;
+ } else if(t.expr->op == Expr::Op::PLUS ||
+ t.expr->op == Expr::Op::MINUS) {
+ return 10;
+ } else if(t.type == TokenType::OPERAND) {
+ return 0;
+ } else ssassert(false, "Unexpected operator");
+}
+
+bool ExprParser::Reduce(std::string *error) {
+ Token a = PopOperand(error);
+ if(a.IsError()) return false;
+
+ Token op = PopOperator(error);
+ if(op.IsError()) return false;
+
+ Token r = Token::From(TokenType::OPERAND);
+ switch(op.type) {
+ case TokenType::BINARY_OP: {
+ Token b = PopOperand(error);
+ if(b.IsError()) return false;
+ r.expr = b.expr->AnyOp(op.expr->op, a.expr);
+ break;
+ }
- char c = *in;
- if(isdigit(c) || c == '.') {
- // A number literal
- char number[70];
- int len = 0;
- while((isdigit(*in) || *in == '.') && len < 30) {
- number[len++] = *in;
- in++;
- }
- number[len++] = '\0';
- Expr *e = AllocExpr();
- e->op = CONSTANT;
- e->v = atof(number);
- Unparsed[UnparsedCnt++] = e;
- } else if(isalpha(c) || c == '_') {
- char name[70];
- int len = 0;
- while(isforname(*in) && len < 30) {
- name[len++] = *in;
- in++;
+ case TokenType::UNARY_OP: {
+ Expr *e = a.expr;
+ switch(op.expr->op) {
+ case Expr::Op::NEGATE: e = e->Negate(); break;
+ case Expr::Op::SQRT: e = e->Sqrt(); break;
+ case Expr::Op::SQUARE: e = e->Times(e); break;
+ case Expr::Op::SIN: e = e->Times(Expr::From(PI/180))->Sin(); break;
+ case Expr::Op::COS: e = e->Times(Expr::From(PI/180))->Cos(); break;
+ case Expr::Op::ASIN: e = e->ASin()->Times(Expr::From(180/PI)); break;
+ case Expr::Op::ACOS: e = e->ACos()->Times(Expr::From(180/PI)); break;
+ default: ssassert(false, "Unexpected unary operator");
}
- name[len++] = '\0';
-
- Expr *e = AllocExpr();
- if(strcmp(name, "sqrt")==0) {
- e->op = UNARY_OP;
- e->c = 'q';
- } else if(strcmp(name, "cos")==0) {
- e->op = UNARY_OP;
- e->c = 'c';
- } else if(strcmp(name, "sin")==0) {
- e->op = UNARY_OP;
- e->c = 's';
- } else if(strcmp(name, "pi")==0) {
- e->op = CONSTANT;
- e->v = PI;
- } else {
- throw "unknown name";
+ r.expr = e;
+ break;
+ }
+
+ default: ssassert(false, "Unexpected operator");
+ }
+ stack.push_back(r);
+
+ return true;
+}
+
+bool ExprParser::Parse(std::string *error, size_t reduceUntil) {
+ while(true) {
+ Token t = Lex(error);
+ switch(t.type) {
+ case TokenType::ERROR:
+ return false;
+
+ case TokenType::END:
+ case TokenType::PAREN_RIGHT:
+ while(stack.size() > 1 + reduceUntil) {
+ if(!Reduce(error)) return false;
+ }
+
+ if(t.type == TokenType::PAREN_RIGHT) {
+ stack.push_back(t);
+ }
+ return true;
+
+ case TokenType::PAREN_LEFT: {
+ // sub-expression
+ if(!Parse(error, /*reduceUntil=*/stack.size())) return false;
+
+ if(stack.empty() || stack.back().type != TokenType::PAREN_RIGHT) {
+ *error = "Expected ')'";
+ return false;
+ }
+ stack.pop_back();
+ break;
}
- Unparsed[UnparsedCnt++] = e;
- } else if(strchr("+-*/()", c)) {
- Expr *e = AllocExpr();
- e->op = (c == '(' || c == ')') ? PAREN : BINARY_OP;
- e->c = c;
- Unparsed[UnparsedCnt++] = e;
- in++;
- } else if(isspace(c)) {
- // Ignore whitespace
- in++;
- } else {
- // This is a lex error.
- throw "unexpected characters";
+
+ case TokenType::BINARY_OP:
+ if((stack.size() > reduceUntil && stack.back().type != TokenType::OPERAND) ||
+ stack.size() == reduceUntil) {
+ if(t.expr->op == Expr::Op::MINUS) {
+ t.type = TokenType::UNARY_OP;
+ t.expr->op = Expr::Op::NEGATE;
+ stack.push_back(t);
+ break;
+ }
+ }
+
+ while(stack.size() > 1 + reduceUntil &&
+ Precedence(t) <= Precedence(stack[stack.size() - 2])) {
+ if(!Reduce(error)) return false;
+ }
+
+ stack.push_back(t);
+ break;
+
+ case TokenType::UNARY_OP:
+ case TokenType::OPERAND:
+ stack.push_back(t);
+ break;
}
}
+
+ return true;
}
-Expr *Expr::From(const char *in, bool popUpError) {
- UnparsedCnt = 0;
- UnparsedP = 0;
- OperandsP = 0;
- OperatorsP = 0;
+Expr *ExprParser::Parse(const std::string &input, std::string *error) {
+ ExprParser parser;
+ parser.it = input.cbegin();
+ parser.end = input.cend();
+ if(!parser.Parse(error)) return NULL;
- Expr *r;
- try {
- Lex(in);
- Parse();
- r = PopOperand();
- } catch (const char *e) {
- dbp("exception: parse/lex error: %s", e);
+ Token r = parser.PopOperand(error);
+ if(r.IsError()) return NULL;
+ return r.expr;
+}
+
+Expr *Expr::Parse(const std::string &input, std::string *error) {
+ return ExprParser::Parse(input, error);
+}
+
+Expr *Expr::From(const std::string &input, bool popUpError) {
+ std::string error;
+ Expr *e = ExprParser::Parse(input, &error);
+ if(!e) {
+ dbp("Parse/lex error: %s", error.c_str());
if(popUpError) {
- Error("Not a valid number or expression: '%s'", in);
+ Error("Not a valid number or expression: '%s'.\n%s.",
+ input.c_str(), error.c_str());
}
- return NULL;
}
- return r;
+ return e;
}
-
//
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
-
-#ifndef __EXPR_H
-#define __EXPR_H
-
-class Expr;
+#ifndef SOLVESPACE_EXPR_H
+#define SOLVESPACE_EXPR_H
class Expr {
public:
- enum {
+ enum class Op : uint32_t {
// A parameter, by the hParam handle
PARAM = 0,
// A parameter, by a pointer straight in to the param table (faster,
// if we know that the param table won't move around)
PARAM_PTR = 1,
+ // Operands
CONSTANT = 20,
+ VARIABLE = 21,
+ // Binary ops
PLUS = 100,
MINUS = 101,
TIMES = 102,
DIV = 103,
+ // Unary ops
NEGATE = 104,
SQRT = 105,
SQUARE = 106,
COS = 108,
ASIN = 109,
ACOS = 110,
-
- // Special helpers for when we're parsing an expression from text.
- // Initially, literals (like a constant number) appear in the same
- // format as they will in the finished expression, but the operators
- // are different until the parser fixes things up (and builds the
- // tree from the flat list that the lexer outputs).
- ALL_RESOLVED = 1000,
- PAREN = 1001,
- BINARY_OP = 1002,
- UNARY_OP = 1003
};
- int op;
+ Op op;
Expr *a;
union {
double v;
hParam parh;
Param *parp;
Expr *b;
-
- // For use while parsing
- char c;
};
- Expr() { }
- Expr(double val) : op(CONSTANT) { v = val; }
+ Expr() = default;
+ Expr(double val) : op(Op::CONSTANT) { v = val; }
- static inline Expr *AllocExpr(void)
+ static inline Expr *AllocExpr()
{ return (Expr *)AllocTemporary(sizeof(Expr)); }
static Expr *From(hParam p);
static Expr *From(double v);
- Expr *AnyOp(int op, Expr *b);
- inline Expr *Plus (Expr *b_) { return AnyOp(PLUS, b_); }
- inline Expr *Minus(Expr *b_) { return AnyOp(MINUS, b_); }
- inline Expr *Times(Expr *b_) { return AnyOp(TIMES, b_); }
- inline Expr *Div (Expr *b_) { return AnyOp(DIV, b_); }
-
- inline Expr *Negate(void) { return AnyOp(NEGATE, NULL); }
- inline Expr *Sqrt (void) { return AnyOp(SQRT, NULL); }
- inline Expr *Square(void) { return AnyOp(SQUARE, NULL); }
- inline Expr *Sin (void) { return AnyOp(SIN, NULL); }
- inline Expr *Cos (void) { return AnyOp(COS, NULL); }
- inline Expr *ASin (void) { return AnyOp(ASIN, NULL); }
- inline Expr *ACos (void) { return AnyOp(ACOS, NULL); }
-
- Expr *PartialWrt(hParam p);
- double Eval(void);
- uint64_t ParamsUsed(void);
- bool DependsOn(hParam p);
+ Expr *AnyOp(Op op, Expr *b);
+ inline Expr *Plus (Expr *b_) { return AnyOp(Op::PLUS, b_); }
+ inline Expr *Minus(Expr *b_) { return AnyOp(Op::MINUS, b_); }
+ inline Expr *Times(Expr *b_) { return AnyOp(Op::TIMES, b_); }
+ inline Expr *Div (Expr *b_) { return AnyOp(Op::DIV, b_); }
+
+ inline Expr *Negate() { return AnyOp(Op::NEGATE, NULL); }
+ inline Expr *Sqrt () { return AnyOp(Op::SQRT, NULL); }
+ inline Expr *Square() { return AnyOp(Op::SQUARE, NULL); }
+ inline Expr *Sin () { return AnyOp(Op::SIN, NULL); }
+ inline Expr *Cos () { return AnyOp(Op::COS, NULL); }
+ inline Expr *ASin () { return AnyOp(Op::ASIN, NULL); }
+ inline Expr *ACos () { return AnyOp(Op::ACOS, NULL); }
+
+ Expr *PartialWrt(hParam p) const;
+ double Eval() const;
+ uint64_t ParamsUsed() const;
+ bool DependsOn(hParam p) const;
static bool Tol(double a, double b);
- Expr *FoldConstants(void);
+ Expr *FoldConstants();
void Substitute(hParam oldh, hParam newh);
static const hParam NO_PARAMS, MULTIPLE_PARAMS;
- hParam ReferencedParams(ParamList *pl);
+ hParam ReferencedParams(ParamList *pl) const;
- void ParamsToPointers(void);
+ void ParamsToPointers();
- std::string Print(void);
+ std::string Print() const;
// number of child nodes: 0 (e.g. constant), 1 (sqrt), or 2 (+)
- int Children(void);
+ int Children() const;
// total number of nodes in the tree
- int Nodes(void);
+ int Nodes() const;
// Make a simple copy
- Expr *DeepCopy(void);
+ Expr *DeepCopy() const;
// Make a copy, with the parameters (usually referenced by hParam)
// resolved to pointers to the actual value. This speeds things up
// considerably.
Expr *DeepCopyWithParamsAsPointers(IdList<Param,hParam> *firstTry,
- IdList<Param,hParam> *thenTry);
-
- static Expr *From(const char *in, bool popUpError);
- static void Lex(const char *in);
- static Expr *Next(void);
- static void Consume(void);
+ IdList<Param,hParam> *thenTry) const;
- static void PushOperator(Expr *e);
- static Expr *PopOperator(void);
- static Expr *TopOperator(void);
- static void PushOperand(Expr *e);
- static Expr *PopOperand(void);
-
- static void Reduce(void);
- static void ReduceAndPush(Expr *e);
- static int Precedence(Expr *e);
-
- static int Precedence(int op);
- static void Parse(void);
+ static Expr *Parse(const std::string &input, std::string *error);
+ static Expr *From(const std::string &input, bool popUpError);
};
class ExprVector {
static ExprVector From(hParam x, hParam y, hParam z);
static ExprVector From(double x, double y, double z);
- ExprVector Plus(ExprVector b);
- ExprVector Minus(ExprVector b);
- Expr *Dot(ExprVector b);
- ExprVector Cross(ExprVector b);
- ExprVector ScaledBy(Expr *s);
- ExprVector WithMagnitude(Expr *s);
- Expr *Magnitude(void);
+ ExprVector Plus(ExprVector b) const;
+ ExprVector Minus(ExprVector b) const;
+ Expr *Dot(ExprVector b) const;
+ ExprVector Cross(ExprVector b) const;
+ ExprVector ScaledBy(Expr *s) const;
+ ExprVector WithMagnitude(Expr *s) const;
+ Expr *Magnitude() const;
- Vector Eval(void);
+ Vector Eval() const;
};
class ExprQuaternion {
static ExprQuaternion From(Quaternion qn);
static ExprQuaternion From(hParam w, hParam vx, hParam vy, hParam vz);
- ExprVector RotationU(void);
- ExprVector RotationV(void);
- ExprVector RotationN(void);
+ ExprVector RotationU() const;
+ ExprVector RotationV() const;
+ ExprVector RotationN() const;
- ExprVector Rotate(ExprVector p);
- ExprQuaternion Times(ExprQuaternion b);
+ ExprVector Rotate(ExprVector p) const;
+ ExprQuaternion Times(ExprQuaternion b) const;
- Expr *Magnitude(void);
+ Expr *Magnitude() const;
};
-
#endif
-
// sketch. This does not leave the program in an acceptable state (with the
// references created, and so on), so anyone calling this must fix that later.
//-----------------------------------------------------------------------------
-void SolveSpaceUI::ClearExisting(void) {
+void SolveSpaceUI::ClearExisting() {
UndoClearStack(&redo);
UndoClearStack(&undo);
- for(int i = 0; i < SK.groupOrder.n; i++) {
- Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
+ for(hGroup hg : SK.groupOrder) {
+ Group *g = SK.GetGroup(hg);
g->Clear();
}
SK.entity.Clear();
SK.param.Clear();
+ images.clear();
}
-hGroup SolveSpaceUI::CreateDefaultDrawingGroup(void) {
+hGroup SolveSpaceUI::CreateDefaultDrawingGroup() {
Group g = {};
// And an empty group, for the first stuff the user draws.
g.visible = true;
- g.name = "sketch-in-plane";
- g.type = Group::DRAWING_WORKPLANE;
- g.subtype = Group::WORKPLANE_BY_POINT_ORTHO;
+ g.name = C_("group-name", "sketch-in-plane");
+ g.type = Group::Type::DRAWING_WORKPLANE;
+ g.subtype = Group::Subtype::WORKPLANE_BY_POINT_ORTHO;
g.order = 1;
g.predef.q = Quaternion::From(1, 0, 0, 0);
hRequest hr = Request::HREQUEST_REFERENCE_XY;
return g.h;
}
-void SolveSpaceUI::NewFile(void) {
+void SolveSpaceUI::NewFile() {
ClearExisting();
// Our initial group, that contains the references.
Group g = {};
g.visible = true;
- g.name = "#references";
- g.type = Group::DRAWING_3D;
+ g.name = C_("group-name", "#references");
+ g.type = Group::Type::DRAWING_3D;
g.order = 0;
g.h = Group::HGROUP_REFERENCES;
SK.group.Add(&g);
// Let's create three two-d coordinate systems, for the coordinate
// planes; these are our references, present in every sketch.
Request r = {};
- r.type = Request::WORKPLANE;
+ r.type = Request::Type::WORKPLANE;
r.group = Group::HGROUP_REFERENCES;
r.workplane = Entity::FREE_IN_3D;
{ 'g', "Group.allDimsReference", 'b', &(SS.sv.g.allDimsReference) },
{ 'g', "Group.scale", 'f', &(SS.sv.g.scale) },
{ 'g', "Group.remap", 'M', &(SS.sv.g.remap) },
- { 'g', "Group.impFile", 'S', &(SS.sv.g.linkFile) },
- { 'g', "Group.impFileRel", 'S', &(SS.sv.g.linkFileRel) },
+ { 'g', "Group.impFile", 'i', NULL },
+ { 'g', "Group.impFileRel", 'P', &(SS.sv.g.linkFile) },
{ 'p', "Param.h.v.", 'x', &(SS.sv.p.h.v) },
{ 'p', "Param.val", 'f', &(SS.sv.p.val) },
{ 'r', "Request.style", 'x', &(SS.sv.r.style) },
{ 'r', "Request.str", 'S', &(SS.sv.r.str) },
{ 'r', "Request.font", 'S', &(SS.sv.r.font) },
+ { 'r', "Request.file", 'P', &(SS.sv.r.file) },
+ { 'r', "Request.aspectRatio", 'f', &(SS.sv.r.aspectRatio) },
{ 'e', "Entity.h.v", 'x', &(SS.sv.e.h.v) },
{ 'e', "Entity.type", 'd', &(SS.sv.e.type) },
{ 'e', "Entity.style", 'x', &(SS.sv.e.style) },
{ 'e', "Entity.str", 'S', &(SS.sv.e.str) },
{ 'e', "Entity.font", 'S', &(SS.sv.e.font) },
+ { 'e', "Entity.file", 'P', &(SS.sv.e.file) },
{ 'e', "Entity.point[0].v", 'x', &(SS.sv.e.point[0].v) },
{ 'e', "Entity.point[1].v", 'x', &(SS.sv.e.point[1].v) },
{ 'e', "Entity.point[2].v", 'x', &(SS.sv.e.point[2].v) },
{ 'c', "Constraint.group.v", 'x', &(SS.sv.c.group.v) },
{ 'c', "Constraint.workplane.v", 'x', &(SS.sv.c.workplane.v) },
{ 'c', "Constraint.valA", 'f', &(SS.sv.c.valA) },
+ { 'c', "Constraint.valP.v", 'x', &(SS.sv.c.valP.v) },
{ 'c', "Constraint.ptA.v", 'x', &(SS.sv.c.ptA.v) },
{ 'c', "Constraint.ptB.v", 'x', &(SS.sv.c.ptB.v) },
{ 'c', "Constraint.entityA.v", 'x', &(SS.sv.c.entityA.v) },
};
struct SAVEDptr {
- IdList<EntityMap,EntityId> &M() { return *((IdList<EntityMap,EntityId> *)this); }
- std::string &S() { return *((std::string *)this); }
+ EntityMap &M() { return *((EntityMap *)this); }
+ std::string &S() { return *((std::string *)this); }
+ Platform::Path &P() { return *((Platform::Path *)this); }
bool &b() { return *((bool *)this); }
RgbaColor &c() { return *((RgbaColor *)this); }
int &d() { return *((int *)this); }
uint32_t &x() { return *((uint32_t *)this); }
};
-void SolveSpaceUI::SaveUsingTable(int type) {
+void SolveSpaceUI::SaveUsingTable(const Platform::Path &filename, int type) {
int i;
for(i = 0; SAVED[i].type != 0; i++) {
if(SAVED[i].type != type) continue;
SAVEDptr *p = (SAVEDptr *)SAVED[i].ptr;
// Any items that aren't specified are assumed to be zero
if(fmt == 'S' && p->S().empty()) continue;
+ if(fmt == 'P' && p->P().IsEmpty()) continue;
if(fmt == 'd' && p->d() == 0) continue;
if(fmt == 'f' && EXACT(p->f() == 0.0)) continue;
if(fmt == 'x' && p->x() == 0) continue;
+ if(fmt == 'i') continue;
fprintf(fh, "%s=", SAVED[i].desc);
switch(fmt) {
case 'f': fprintf(fh, "%.20f", p->f()); break;
case 'x': fprintf(fh, "%08x", p->x()); break;
+ case 'P': {
+ if(!p->P().IsEmpty()) {
+ Platform::Path relativePath = p->P().RelativeTo(filename.Parent());
+ ssassert(!relativePath.IsEmpty(), "Cannot relativize path");
+ fprintf(fh, "%s", relativePath.ToPortable().c_str());
+ }
+ break;
+ }
+
case 'M': {
- int j;
fprintf(fh, "{\n");
- for(j = 0; j < p->M().n; j++) {
- EntityMap *em = &(p->M().elem[j]);
+ // Sort the mapping, since EntityMap is not deterministic.
+ std::vector<std::pair<EntityKey, EntityId>> sorted(p->M().begin(), p->M().end());
+ std::sort(sorted.begin(), sorted.end(),
+ [](std::pair<EntityKey, EntityId> &a, std::pair<EntityKey, EntityId> &b) {
+ return a.second.v < b.second.v;
+ });
+ for(auto it : sorted) {
fprintf(fh, " %d %08x %d\n",
- em->h.v, em->input.v, em->copyNumber);
+ it.second.v, it.first.input.v, it.first.copyNumber);
}
fprintf(fh, "}");
break;
}
- default: oops();
+ case 'i': break;
+
+ default: ssassert(false, "Unexpected value format");
}
fprintf(fh, "\n");
}
}
-bool SolveSpaceUI::SaveToFile(const std::string &filename) {
- // Make sure all the entities are regenerated up to date, since they
- // will be exported. We reload the linked files because that rewrites
- // the linkFileRel for our possibly-new filename.
+bool SolveSpaceUI::SaveToFile(const Platform::Path &filename) {
+ // Make sure all the entities are regenerated up to date, since they will be exported.
SS.ScheduleShowTW();
- SS.ReloadAllImported();
- SS.GenerateAll(SolveSpaceUI::GENERATE_ALL);
+ SS.GenerateAll(SolveSpaceUI::Generate::ALL);
+
+ for(Group &g : SK.group) {
+ if(g.type != Group::Type::LINKED) continue;
+
+ if(g.linkFile.RelativeTo(filename).IsEmpty()) {
+ Error("This sketch links the sketch '%s'; it can only be saved "
+ "on the same volume.", g.linkFile.raw.c_str());
+ return false;
+ }
+ }
- fh = ssfopen(filename, "wb");
+ fh = OpenFile(filename, "wb");
if(!fh) {
- Error("Couldn't write to file '%s'", filename.c_str());
+ Error("Couldn't write to file '%s'", filename.raw.c_str());
return false;
}
fprintf(fh, "%s\n\n\n", VERSION_STRING);
int i, j;
- for(i = 0; i < SK.group.n; i++) {
- sv.g = SK.group.elem[i];
- SaveUsingTable('g');
+ for(auto &g : SK.group) {
+ sv.g = g;
+ SaveUsingTable(filename, 'g');
fprintf(fh, "AddGroup\n\n");
}
- for(i = 0; i < SK.param.n; i++) {
- sv.p = SK.param.elem[i];
- SaveUsingTable('p');
+ for(auto &p : SK.param) {
+ sv.p = p;
+ SaveUsingTable(filename, 'p');
fprintf(fh, "AddParam\n\n");
}
- for(i = 0; i < SK.request.n; i++) {
- sv.r = SK.request.elem[i];
- SaveUsingTable('r');
+ for(auto &r : SK.request) {
+ sv.r = r;
+ SaveUsingTable(filename, 'r');
fprintf(fh, "AddRequest\n\n");
}
- for(i = 0; i < SK.entity.n; i++) {
- (SK.entity.elem[i]).CalculateNumerical(true);
- sv.e = SK.entity.elem[i];
- SaveUsingTable('e');
+ for(auto &e : SK.entity) {
+ e.CalculateNumerical(/*forExport=*/true);
+ sv.e = e;
+ SaveUsingTable(filename, 'e');
fprintf(fh, "AddEntity\n\n");
}
- for(i = 0; i < SK.constraint.n; i++) {
- sv.c = SK.constraint.elem[i];
- SaveUsingTable('c');
+ for(auto &c : SK.constraint) {
+ sv.c = c;
+ SaveUsingTable(filename, 'c');
fprintf(fh, "AddConstraint\n\n");
}
- for(i = 0; i < SK.style.n; i++) {
- sv.s = SK.style.elem[i];
+ for(auto &s : SK.style) {
+ sv.s = s;
if(sv.s.h.v >= Style::FIRST_CUSTOM) {
- SaveUsingTable('s');
+ SaveUsingTable(filename, 's');
fprintf(fh, "AddStyle\n\n");
}
}
// A group will have either a mesh or a shell, but not both; but the code
// to print either of those just does nothing if the mesh/shell is empty.
- Group *g = SK.GetGroup(SK.groupOrder.elem[SK.groupOrder.n - 1]);
+ Group *g = SK.GetGroup(*SK.groupOrder.Last());
SMesh *m = &g->runningMesh;
for(i = 0; i < m->l.n; i++) {
- STriangle *tr = &(m->l.elem[i]);
+ STriangle *tr = &(m->l[i]);
fprintf(fh, "Triangle %08x %08x "
"%.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f\n",
tr->meta.face, tr->meta.color.ToPackedInt(),
return true;
}
-void SolveSpaceUI::LoadUsingTable(char *key, char *val) {
+void SolveSpaceUI::LoadUsingTable(const Platform::Path &filename, char *key, char *val) {
int i;
for(i = 0; SAVED[i].type != 0; i++) {
if(strcmp(SAVED[i].desc, key)==0) {
case 'f': p->f() = atof(val); break;
case 'x': sscanf(val, "%x", &u); p->x()= u; break;
+ case 'P': {
+ Platform::Path path = Platform::Path::FromPortable(val);
+ if(!path.IsEmpty()) {
+ p->P() = filename.Parent().Join(path).Expand();
+ }
+ break;
+ }
+
case 'c':
sscanf(val, "%x", &u);
p->c() = RgbaColor::FromPackedInt(u);
break;
- case 'P':
- p->S() = val;
- break;
-
case 'M': {
- // Don't clear this list! When the group gets added, it
- // makes a shallow copy, so that would result in us
- // freeing memory that we want to keep around. Just
- // zero it out so that new memory is allocated.
- p->M() = {};
+ p->M().clear();
for(;;) {
- EntityMap em;
+ EntityKey ek;
+ EntityId ei;
char line2[1024];
if (fgets(line2, (int)sizeof(line2), fh) == NULL)
break;
- if(sscanf(line2, "%d %x %d", &(em.h.v), &(em.input.v),
- &(em.copyNumber)) == 3)
- {
- p->M().Add(&em);
+ if(sscanf(line2, "%d %x %d", &(ei.v), &(ek.input.v),
+ &(ek.copyNumber)) == 3) {
+ if(ei.v == Entity::NO_ENTITY.v) {
+ // Commit bd84bc1a mistakenly introduced code that would remap
+ // some entities to NO_ENTITY. This was fixed in commit bd84bc1a,
+ // but files created meanwhile are corrupt, and can cause crashes.
+ //
+ // To fix this, we skip any such remaps when loading; they will be
+ // recreated on the next regeneration. Any resulting orphans will
+ // be pruned in the usual way, recovering to a well-defined state.
+ continue;
+ }
+ p->M().insert({ ek, ei });
} else {
break;
}
break;
}
- default: oops();
+ case 'i': break;
+
+ default: ssassert(false, "Unexpected value format");
}
break;
}
}
}
-bool SolveSpaceUI::LoadFromFile(const std::string &filename) {
+bool SolveSpaceUI::LoadFromFile(const Platform::Path &filename, bool canCancel) {
allConsistent = false;
fileLoadError = false;
- fh = ssfopen(filename, "rb");
+ fh = OpenFile(filename, "rb");
if(!fh) {
- Error("Couldn't read from file '%s'", filename.c_str());
+ Error("Couldn't read from file '%s'", filename.raw.c_str());
return false;
}
if(e) {
*e = '\0';
char *key = line, *val = e+1;
- LoadUsingTable(key, val);
+ LoadUsingTable(filename, key, val);
} else if(strcmp(line, "AddGroup")==0) {
// legacy files have a spurious dependency between linked groups
// and their parent groups, remove
- if(sv.g.type == Group::LINKED)
+ if(sv.g.type == Group::Type::LINKED)
sv.g.opA.v = 0;
SK.group.Add(&(sv.g));
fclose(fh);
if(fileLoadError) {
- Error("Unrecognized data in file. This file may be corrupt, or "
- "from a new version of the program.");
+ Error(_("Unrecognized data in file. This file may be corrupt, or "
+ "from a newer version of the program."));
// At least leave the program in a non-crashing state.
- if(SK.group.n == 0) {
+ if(SK.group.IsEmpty()) {
NewFile();
}
}
+ if(!ReloadAllLinked(filename, canCancel)) {
+ return false;
+ }
+ UpgradeLegacyData();
return true;
}
-bool SolveSpaceUI::LoadEntitiesFromFile(const std::string &filename, EntityList *le,
+void SolveSpaceUI::UpgradeLegacyData() {
+ for(Request &r : SK.request) {
+ switch(r.type) {
+ // TTF text requests saved in versions prior to 3.0 only have two
+ // reference points (origin and origin plus v); version 3.0 adds two
+ // more points, and if we don't do anything, then they will appear
+ // at workplane origin, and the solver will mess up the sketch if
+ // it is not fully constrained.
+ case Request::Type::TTF_TEXT: {
+ IdList<Entity,hEntity> entity = {};
+ IdList<Param,hParam> param = {};
+ r.Generate(&entity, ¶m);
+
+ // If we didn't load all of the entities and params that this
+ // request would generate, then add them now, so that we can
+ // force them to their appropriate positions.
+ for(Param &p : param) {
+ if(SK.param.FindByIdNoOops(p.h) != NULL) continue;
+ SK.param.Add(&p);
+ }
+ bool allPointsExist = true;
+ for(Entity &e : entity) {
+ if(SK.entity.FindByIdNoOops(e.h) != NULL) continue;
+ SK.entity.Add(&e);
+ allPointsExist = false;
+ }
+
+ if(!allPointsExist) {
+ Entity *text = entity.FindById(r.h.entity(0));
+ Entity *b = entity.FindById(text->point[2]);
+ Entity *c = entity.FindById(text->point[3]);
+ ExprVector bex, cex;
+ text->RectGetPointsExprs(&bex, &cex);
+ b->PointForceParamTo(bex.Eval());
+ c->PointForceParamTo(cex.Eval());
+ }
+ entity.Clear();
+ param.Clear();
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ // Constraints saved in versions prior to 3.0 never had any params;
+ // version 3.0 introduced params to constraints to avoid the hairy ball problem,
+ // so force them where they belong.
+ IdList<Param,hParam> oldParam = {};
+ SK.param.DeepCopyInto(&oldParam);
+ SS.GenerateAll(SolveSpaceUI::Generate::REGEN);
+
+ auto AllParamsExistFor = [&](Constraint &c) {
+ IdList<Param,hParam> param = {};
+ c.Generate(¶m);
+ bool allParamsExist = true;
+ for(Param &p : param) {
+ if(oldParam.FindByIdNoOops(p.h) != NULL) continue;
+ allParamsExist = false;
+ break;
+ }
+ param.Clear();
+ return allParamsExist;
+ };
+
+ for(Constraint &c : SK.constraint) {
+ switch(c.type) {
+ case Constraint::Type::PT_ON_LINE: {
+ if(AllParamsExistFor(c)) continue;
+
+ EntityBase *eln = SK.GetEntity(c.entityA);
+ EntityBase *ea = SK.GetEntity(eln->point[0]);
+ EntityBase *eb = SK.GetEntity(eln->point[1]);
+ EntityBase *ep = SK.GetEntity(c.ptA);
+
+ ExprVector exp = ep->PointGetExprsInWorkplane(c.workplane);
+ ExprVector exa = ea->PointGetExprsInWorkplane(c.workplane);
+ ExprVector exb = eb->PointGetExprsInWorkplane(c.workplane);
+ ExprVector exba = exb.Minus(exa);
+ Param *p = SK.GetParam(c.h.param(0));
+ p->val = exba.Dot(exp.Minus(exa))->Eval() / exba.Dot(exba)->Eval();
+ break;
+ }
+
+ case Constraint::Type::CUBIC_LINE_TANGENT: {
+ if(AllParamsExistFor(c)) continue;
+
+ EntityBase *cubic = SK.GetEntity(c.entityA);
+ EntityBase *line = SK.GetEntity(c.entityB);
+
+ ExprVector a;
+ if(c.other) {
+ a = cubic->CubicGetFinishTangentExprs();
+ } else {
+ a = cubic->CubicGetStartTangentExprs();
+ }
+
+ ExprVector b = line->VectorGetExprs();
+
+ Param *param = SK.GetParam(c.h.param(0));
+ param->val = a.Dot(b)->Eval() / b.Dot(b)->Eval();
+ break;
+ }
+
+ case Constraint::Type::SAME_ORIENTATION: {
+ if(AllParamsExistFor(c)) continue;
+
+ EntityBase *an = SK.GetEntity(c.entityA);
+ EntityBase *bn = SK.GetEntity(c.entityB);
+
+ ExprVector a = an->NormalExprsN();
+ ExprVector b = bn->NormalExprsN();
+
+ Param *param = SK.GetParam(c.h.param(0));
+ param->val = a.Dot(b)->Eval() / b.Dot(b)->Eval();
+ break;
+ }
+
+ case Constraint::Type::PARALLEL: {
+ if(AllParamsExistFor(c)) continue;
+
+ EntityBase *ea = SK.GetEntity(c.entityA),
+ *eb = SK.GetEntity(c.entityB);
+ ExprVector a = ea->VectorGetExprsInWorkplane(c.workplane);
+ ExprVector b = eb->VectorGetExprsInWorkplane(c.workplane);
+
+ Param *param = SK.GetParam(c.h.param(0));
+ param->val = a.Dot(b)->Eval() / b.Dot(b)->Eval();
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ oldParam.Clear();
+}
+
+bool SolveSpaceUI::LoadEntitiesFromFile(const Platform::Path &filename, EntityList *le,
+ SMesh *m, SShell *sh)
+{
+ if(strcmp(filename.Extension().c_str(), "emn")==0) {
+ return LinkIDF(filename, le, m, sh);
+ } else {
+ return LoadEntitiesFromSlvs(filename, le, m, sh);
+ }
+}
+
+bool SolveSpaceUI::LoadEntitiesFromSlvs(const Platform::Path &filename, EntityList *le,
SMesh *m, SShell *sh)
{
SSurface srf = {};
SCurve crv = {};
- fh = ssfopen(filename, "rb");
+ fh = OpenFile(filename, "rb");
if(!fh) return false;
le->Clear();
if(e) {
*e = '\0';
char *key = line, *val = e+1;
- LoadUsingTable(key, val);
+ LoadUsingTable(filename, key, val);
} else if(strcmp(line, "AddGroup")==0) {
- // Don't leak memory; these get allocated whether we want them
- // or not.
- sv.g.remap.Clear();
+ // These get allocated whether we want them or not.
+ sv.g.remap.clear();
} else if(strcmp(line, "AddParam")==0) {
} else if(strcmp(line, "AddEntity")==0) {
} else if(strcmp(line, "AddConstraint")==0) {
} else if(strcmp(line, "AddStyle")==0) {
-
+ // Linked file contains a style that we don't have yet,
+ // so import it.
+ if (SK.style.FindByIdNoOops(sv.s.h) == nullptr) {
+ SK.style.Add(&(sv.s));
+ }
+ sv.s = {};
+ Style::FillDefaultStyle(&sv.s);
} else if(strcmp(line, VERSION_STRING)==0) {
} else if(StrStartsWith(line, "Triangle ")) {
&(tr.a.x), &(tr.a.y), &(tr.a.z),
&(tr.b.x), &(tr.b.y), &(tr.b.z),
&(tr.c.x), &(tr.c.y), &(tr.c.z)) != 11) {
- oops();
+ ssassert(false, "Unexpected Triangle format");
}
tr.meta.color = RgbaColor::FromPackedInt((uint32_t)rgba);
m->AddTriangle(&tr);
if(sscanf(line, "Surface %x %x %x %d %d",
&(srf.h.v), &rgba, &(srf.face),
&(srf.degm), &(srf.degn)) != 5) {
- oops();
+ ssassert(false, "Unexpected Surface format");
}
srf.color = RgbaColor::FromPackedInt((uint32_t)rgba);
} else if(StrStartsWith(line, "SCtrl ")) {
if(sscanf(line, "SCtrl %d %d %lf %lf %lf Weight %lf",
&i, &j, &(c.x), &(c.y), &(c.z), &w) != 6)
{
- oops();
+ ssassert(false, "Unexpected SCtrl format");
}
srf.ctrl[i][j] = c;
srf.weight[i][j] = w;
&(stb.start.x), &(stb.start.y), &(stb.start.z),
&(stb.finish.x), &(stb.finish.y), &(stb.finish.z)) != 8)
{
- oops();
+ ssassert(false, "Unexpected TrimBy format");
}
stb.backwards = (backwards != 0);
srf.trim.Add(&stb);
&(crv.exact.deg),
&(crv.surfA.v), &(crv.surfB.v)) != 5)
{
- oops();
+ ssassert(false, "Unexpected Curve format");
}
crv.isExact = (isExact != 0);
} else if(StrStartsWith(line, "CCtrl ")) {
if(sscanf(line, "CCtrl %d %lf %lf %lf Weight %lf",
&i, &(c.x), &(c.y), &(c.z), &w) != 5)
{
- oops();
+ ssassert(false, "Unexpected CCtrl format");
}
crv.exact.ctrl[i] = c;
crv.exact.weight[i] = w;
&vertex,
&(scpt.p.x), &(scpt.p.y), &(scpt.p.z)) != 4)
{
- oops();
+ ssassert(false, "Unexpected CurvePt format");
}
scpt.vertex = (vertex != 0);
crv.pts.Add(&scpt);
} else if(strcmp(line, "AddCurve")==0) {
sh->curve.Add(&crv);
crv = {};
- } else {
- oops();
- }
+ } else ssassert(false, "Unexpected operation");
}
fclose(fh);
return true;
}
-//-----------------------------------------------------------------------------
-// Handling of the relative-absolute path transformations for links
-//-----------------------------------------------------------------------------
-static std::vector<std::string> Split(const std::string &haystack, const std::string &needle)
-{
- std::vector<std::string> result;
-
- size_t oldpos = 0, pos = 0;
- while(true) {
- oldpos = pos;
- pos = haystack.find(needle, pos);
- if(pos == std::string::npos) break;
- result.push_back(haystack.substr(oldpos, pos - oldpos));
- pos += needle.length();
+static Platform::MessageDialog::Response LocateImportedFile(const Platform::Path &filename,
+ bool canCancel) {
+ Platform::MessageDialogRef dialog = CreateMessageDialog(SS.GW.window);
+
+ using Platform::MessageDialog;
+ dialog->SetType(MessageDialog::Type::QUESTION);
+ dialog->SetTitle(C_("title", "Missing File"));
+ dialog->SetMessage(ssprintf(C_("dialog", "The linked file “%s” is not present."),
+ filename.raw.c_str()));
+ dialog->SetDescription(C_("dialog", "Do you want to locate it manually?\n\n"
+ "If you decline, any geometry that depends on "
+ "the missing file will be permanently removed."));
+ dialog->AddButton(C_("button", "&Yes"), MessageDialog::Response::YES,
+ /*isDefault=*/true);
+ dialog->AddButton(C_("button", "&No"), MessageDialog::Response::NO);
+ if(canCancel) {
+ dialog->AddButton(C_("button", "&Cancel"), MessageDialog::Response::CANCEL);
}
- if(oldpos != haystack.length() - 1)
- result.push_back(haystack.substr(oldpos));
-
- return result;
+ // FIXME(async): asyncify this call
+ return dialog->RunModal();
}
-static std::string Join(const std::vector<std::string> &parts, const std::string &separator)
-{
- bool first = true;
- std::string result;
- for(auto &part : parts) {
- if(!first) result += separator;
- result += part;
- first = false;
- }
- return result;
-}
+bool SolveSpaceUI::ReloadAllLinked(const Platform::Path &saveFile, bool canCancel) {
+ Platform::SettingsRef settings = Platform::GetSettings();
-static bool PlatformPathEqual(const std::string &a, const std::string &b)
-{
- // Case-sensitivity is actually per-volume on both Windows and OS X,
- // but it is extremely tedious to implement and test for little benefit.
-#if defined(WIN32)
- std::wstring wa = Widen(a), wb = Widen(b);
- return std::equal(wa.begin(), wa.end(), wb.begin(), /*wb.end(),*/
- [](wchar_t wca, wchar_t wcb) { return towlower(wca) == towlower(wcb); });
-#elif defined(__APPLE__)
- return !strcasecmp(a.c_str(), b.c_str());
-#else
- return a == b;
-#endif
-}
+ std::map<Platform::Path, Platform::Path, Platform::PathLess> linkMap;
-static std::string MakePathRelative(const std::string &base, const std::string &path)
-{
- std::vector<std::string> baseParts = Split(base, PATH_SEP),
- pathParts = Split(path, PATH_SEP),
- resultParts;
- baseParts.pop_back();
-
- size_t common;
- for(common = 0; common < baseParts.size() && common < pathParts.size(); common++) {
- if(!PlatformPathEqual(baseParts[common], pathParts[common]))
- break;
- }
+ allConsistent = false;
- for(size_t i = common; i < baseParts.size(); i++)
- resultParts.push_back("..");
+ for(Group &g : SK.group) {
+ if(g.type != Group::Type::LINKED) continue;
- resultParts.insert(resultParts.end(),
- pathParts.begin() + common, pathParts.end());
+ g.impEntity.Clear();
+ g.impMesh.Clear();
+ g.impShell.Clear();
- return Join(resultParts, PATH_SEP);
-}
+ // If we prompted for this specific file before, don't ask again.
+ if(linkMap.count(g.linkFile)) {
+ g.linkFile = linkMap[g.linkFile];
+ }
-static std::string MakePathAbsolute(const std::string &base, const std::string &path)
-{
- std::vector<std::string> resultParts = Split(base, PATH_SEP),
- pathParts = Split(path, PATH_SEP);
- resultParts.pop_back();
-
- for(auto &part : pathParts) {
- if(part == ".") {
- /* do nothing */
- } else if(part == "..") {
- if(resultParts.empty()) oops();
- resultParts.pop_back();
+try_again:
+ if(LoadEntitiesFromFile(g.linkFile, &g.impEntity, &g.impMesh, &g.impShell)) {
+ // We loaded the data, good. Now import its dependencies as well.
+ for(Entity &e : g.impEntity) {
+ if(e.type != Entity::Type::IMAGE) continue;
+ if(!ReloadLinkedImage(g.linkFile, &e.file, canCancel)) {
+ return false;
+ }
+ }
+ } else if(linkMap.count(g.linkFile) == 0) {
+ dbp("Missing file for group: %s", g.name.c_str());
+ // The file was moved; prompt the user for its new location.
+ switch(LocateImportedFile(g.linkFile.RelativeTo(saveFile), canCancel)) {
+ case Platform::MessageDialog::Response::YES: {
+ Platform::FileDialogRef dialog = Platform::CreateOpenFileDialog(SS.GW.window);
+ dialog->AddFilters(Platform::SolveSpaceModelFileFilters);
+ dialog->ThawChoices(settings, "LinkSketch");
+ if(dialog->RunModal()) {
+ dialog->FreezeChoices(settings, "LinkSketch");
+ linkMap[g.linkFile] = dialog->GetFilename();
+ g.linkFile = dialog->GetFilename();
+ goto try_again;
+ } else {
+ if(canCancel) return false;
+ break;
+ }
+ }
+
+ case Platform::MessageDialog::Response::NO:
+ linkMap[g.linkFile].Clear();
+ // Geometry will be pruned by GenerateAll().
+ break;
+
+ case Platform::MessageDialog::Response::CANCEL:
+ return false;
+
+ default:
+ ssassert(false, "Unexpected dialog response");
+ }
} else {
- resultParts.push_back(part);
+ // User was already asked to and refused to locate a missing linked file.
}
}
- return Join(resultParts, PATH_SEP);
-}
-
-static void PathSepNormalize(std::string &filename)
-{
- for(size_t i = 0; i < filename.length(); i++) {
- if(filename[i] == '\\')
- filename[i] = '/';
- }
-}
+ for(Request &r : SK.request) {
+ if(r.type != Request::Type::IMAGE) continue;
-static std::string PathSepPlatformToUNIX(const std::string &filename)
-{
-#if defined(WIN32)
- std::string result = filename;
- for(size_t i = 0; i < result.length(); i++) {
- if(result[i] == '\\')
- result[i] = '/';
+ if(!ReloadLinkedImage(saveFile, &r.file, canCancel)) {
+ return false;
+ }
}
- return result;
-#else
- return filename;
-#endif
-}
-static std::string PathSepUNIXToPlatform(const std::string &filename)
-{
-#if defined(WIN32)
- std::string result = filename;
- for(size_t i = 0; i < result.length(); i++) {
- if(result[i] == '/')
- result[i] = '\\';
- }
- return result;
-#else
- return filename;
-#endif
+ return true;
}
-bool SolveSpaceUI::ReloadAllImported(bool canCancel)
-{
- std::map<std::string, std::string> linkMap;
- allConsistent = false;
-
- int i;
- for(i = 0; i < SK.group.n; i++) {
- Group *g = &(SK.group.elem[i]);
- if(g->type != Group::LINKED) continue;
-
- if(isalpha(g->linkFile[0]) && g->linkFile[1] == ':') {
- // Make sure that g->linkFileRel always contains a relative path
- // in an UNIX format, even after we load an old file which had
- // the path in Windows format
- PathSepNormalize(g->linkFileRel);
- }
+bool SolveSpaceUI::ReloadLinkedImage(const Platform::Path &saveFile,
+ Platform::Path *filename, bool canCancel) {
+ Platform::SettingsRef settings = Platform::GetSettings();
+
+ std::shared_ptr<Pixmap> pixmap;
+ bool promptOpenFile = false;
+ if(filename->IsEmpty()) {
+ // We're prompting the user for a new image.
+ promptOpenFile = true;
+ } else {
+ auto image = SS.images.find(*filename);
+ if(image != SS.images.end()) return true;
+
+ pixmap = Pixmap::ReadPng(*filename);
+ if(pixmap == NULL) {
+ // The file was moved; prompt the user for its new location.
+ switch(LocateImportedFile(filename->RelativeTo(saveFile), canCancel)) {
+ case Platform::MessageDialog::Response::YES:
+ promptOpenFile = true;
+ break;
- g->impEntity.Clear();
- g->impMesh.Clear();
- g->impShell.Clear();
+ case Platform::MessageDialog::Response::NO:
+ // We don't know where the file is, record it as absent.
+ break;
- if(linkMap.count(g->linkFile)) {
- std::string newPath = linkMap[g->linkFile];
- if(!newPath.empty())
- g->linkFile = newPath;
- }
+ case Platform::MessageDialog::Response::CANCEL:
+ return false;
- // In a newly created group we only have an absolute path.
- if(!g->linkFileRel.empty()) {
- std::string rel = PathSepUNIXToPlatform(g->linkFileRel);
- std::string fromRel = MakePathAbsolute(SS.saveFile, rel);
- FILE *test = ssfopen(fromRel, "rb");
- if(test) {
- fclose(test);
- // Okay, exists; update the absolute path.
- g->linkFile = fromRel;
- } else {
- // It doesn't exist. Perhaps the file was moved but the tree wasn't, and we
- // can use the absolute filename to get us back. The relative path will be
- // updated below.
+ default:
+ ssassert(false, "Unexpected dialog response");
}
}
+ }
-try_load_file:
- if(LoadEntitiesFromFile(g->linkFile, &(g->impEntity), &(g->impMesh), &(g->impShell)))
- {
- if(!SS.saveFile.empty()) {
- // Record the linked file's name relative to our filename;
- // if the entire tree moves, then everything will still work
- std::string rel = MakePathRelative(SS.saveFile, g->linkFile);
- g->linkFileRel = PathSepPlatformToUNIX(rel);
- } else {
- // We're not yet saved, so can't make it absolute.
- // This will only be used for display purposes, as SS.saveFile
- // is always nonempty when we are actually writing anything.
- g->linkFileRel = g->linkFile;
- }
- } else if(!linkMap.count(g->linkFile)) {
- switch(LocateImportedFileYesNoCancel(g->linkFileRel, canCancel)) {
- case DIALOG_YES: {
- std::string oldImpFile = g->linkFile;
- if(!GetOpenFile(&g->linkFile, "", SlvsFileFilter)) {
- if(canCancel)
- return false;
- break;
- } else {
- linkMap[oldImpFile] = g->linkFile;
- goto try_load_file;
- }
- }
-
- case DIALOG_NO:
- linkMap[g->linkFile] = "";
- /* Geometry will be pruned by GenerateAll(). */
- break;
-
- case DIALOG_CANCEL:
- return false;
+ if(promptOpenFile) {
+ Platform::FileDialogRef dialog = Platform::CreateOpenFileDialog(SS.GW.window);
+ dialog->AddFilters(Platform::RasterFileFilters);
+ dialog->ThawChoices(settings, "LinkImage");
+ if(dialog->RunModal()) {
+ dialog->FreezeChoices(settings, "LinkImage");
+ *filename = dialog->GetFilename();
+ pixmap = Pixmap::ReadPng(*filename);
+ if(pixmap == NULL) {
+ Error("The image '%s' is corrupted.", filename->raw.c_str());
}
- } else {
- // User was already asked to and refused to locate a missing
- // linked file.
+ // We know where the file is now, good.
+ } else if(canCancel) {
+ return false;
}
}
+ // We loaded the data, good.
+ SS.images[*filename] = pixmap;
return true;
}
-
MarkGroupDirty(e->group);
}
-void SolveSpaceUI::MarkGroupDirty(hGroup hg) {
- int i;
+void SolveSpaceUI::MarkGroupDirty(hGroup hg, bool onlyThis) {
bool go = false;
- for(i = 0; i < SK.groupOrder.n; i++) {
- Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
- if(g->h.v == hg.v) {
+ for(auto const &gh : SK.groupOrder) {
+ Group *g = SK.GetGroup(gh);
+ if(g->h == hg) {
go = true;
}
if(go) {
g->clean = false;
+ if(onlyThis) break;
}
}
unsaved = true;
+ ScheduleGenerateAll();
}
-bool SolveSpaceUI::PruneOrphans(void) {
- int i;
- for(i = 0; i < SK.request.n; i++) {
- Request *r = &(SK.request.elem[i]);
- if(GroupExists(r->group)) continue;
+bool SolveSpaceUI::PruneOrphans() {
+ auto r = std::find_if(SK.request.begin(), SK.request.end(),
+ [&](Request &r) { return !GroupExists(r.group); });
+ if(r != SK.request.end()) {
(deleted.requests)++;
SK.request.RemoveById(r->h);
return true;
}
- for(i = 0; i < SK.constraint.n; i++) {
- Constraint *c = &(SK.constraint.elem[i]);
- if(GroupExists(c->group)) continue;
-
+ auto c = std::find_if(SK.constraint.begin(), SK.constraint.end(),
+ [&](Constraint &c) { return !GroupExists(c.group); });
+ if(c != SK.constraint.end()) {
(deleted.constraints)++;
(deleted.nonTrivialConstraints)++;
-
SK.constraint.RemoveById(c->h);
return true;
}
bool SolveSpaceUI::GroupsInOrder(hGroup before, hGroup after) {
if(before.v == 0) return true;
if(after.v == 0) return true;
-
- int beforep = -1, afterp = -1;
- int i;
- for(i = 0; i < SK.groupOrder.n; i++) {
- Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
- if(g->h.v == before.v) beforep = i;
- if(g->h.v == after.v) afterp = i;
- }
- if(beforep < 0 || afterp < 0) return false;
+ if(!GroupExists(before)) return false;
+ if(!GroupExists(after)) return false;
+ int beforep = SK.GetGroup(before)->order;
+ int afterp = SK.GetGroup(after)->order;
if(beforep >= afterp) return false;
return true;
}
bool SolveSpaceUI::EntityExists(hEntity he) {
// A nonexstient entity is acceptable, though, usually just means it
// doesn't apply.
- if(he.v == Entity::NO_ENTITY.v) return true;
+ if(he == Entity::NO_ENTITY) return true;
return SK.entity.FindByIdNoOops(he) ? true : false;
}
}
bool SolveSpaceUI::PruneRequests(hGroup hg) {
- int i;
- for(i = 0; i < SK.entity.n; i++) {
- Entity *e = &(SK.entity.elem[i]);
- if(e->group.v != hg.v) continue;
-
- if(EntityExists(e->workplane)) continue;
-
- if(!e->h.isFromRequest()) oops();
-
+ auto e = std::find_if(SK.entity.begin(), SK.entity.end(),
+ [&](Entity &e) { return e.group == hg && !EntityExists(e.workplane); });
+ if(e != SK.entity.end()) {
(deleted.requests)++;
- SK.request.RemoveById(e->h.request());
+ SK.entity.RemoveById(e->h);
return true;
}
return false;
}
bool SolveSpaceUI::PruneConstraints(hGroup hg) {
- int i;
- for(i = 0; i < SK.constraint.n; i++) {
- Constraint *c = &(SK.constraint.elem[i]);
- if(c->group.v != hg.v) continue;
-
- if(EntityExists(c->workplane) &&
- EntityExists(c->ptA) &&
- EntityExists(c->ptB) &&
- EntityExists(c->entityA) &&
- EntityExists(c->entityB) &&
- EntityExists(c->entityC) &&
- EntityExists(c->entityD))
- {
- continue;
+ auto c = std::find_if(SK.constraint.begin(), SK.constraint.end(), [&](Constraint &c) {
+ if(c.group != hg)
+ return false;
+
+ if(EntityExists(c.workplane) &&
+ EntityExists(c.ptA) &&
+ EntityExists(c.ptB) &&
+ EntityExists(c.entityA) &&
+ EntityExists(c.entityB) &&
+ EntityExists(c.entityC) &&
+ EntityExists(c.entityD)) {
+ return false;
}
+ return true;
+ });
+ if(c != SK.constraint.end()) {
(deleted.constraints)++;
- if(c->type != Constraint::POINTS_COINCIDENT &&
- c->type != Constraint::HORIZONTAL &&
- c->type != Constraint::VERTICAL)
- {
+ if(c->type != Constraint::Type::POINTS_COINCIDENT &&
+ c->type != Constraint::Type::HORIZONTAL &&
+ c->type != Constraint::Type::VERTICAL) {
(deleted.nonTrivialConstraints)++;
}
return false;
}
-void SolveSpaceUI::GenerateAll(GenerateType type, bool andFindFree, bool genForBBox) {
- int first, last, i, j;
+void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox) {
+ int first = 0, last = 0, i;
+
+ uint64_t startMillis = GetMilliseconds(),
+ endMillis;
SK.groupOrder.Clear();
- for(int i = 0; i < SK.group.n; i++)
- SK.groupOrder.Add(&SK.group.elem[i].h);
- std::sort(&SK.groupOrder.elem[0], &SK.groupOrder.elem[SK.groupOrder.n],
+ for(auto &g : SK.group) { SK.groupOrder.Add(&g.h); }
+ std::sort(SK.groupOrder.begin(), SK.groupOrder.end(),
[](const hGroup &ha, const hGroup &hb) {
return SK.GetGroup(ha)->order < SK.GetGroup(hb)->order;
});
switch(type) {
- case GENERATE_DIRTY: {
+ case Generate::DIRTY: {
first = INT_MAX;
last = 0;
// Start from the first dirty group, and solve until the active group,
// since all groups after the active group are hidden.
+ // Not using range-for because we're tracking the indices.
for(i = 0; i < SK.groupOrder.n; i++) {
- Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
+ Group *g = SK.GetGroup(SK.groupOrder[i]);
if((!g->clean) || !g->IsSolvedOkay()) {
first = min(first, i);
}
- if(g->h.v == SS.GW.activeGroup.v) {
+ if(g->h == SS.GW.activeGroup) {
last = i;
}
}
break;
}
- case GENERATE_ALL:
+ case Generate::ALL:
first = 0;
last = INT_MAX;
break;
- case GENERATE_REGEN:
+ case Generate::REGEN:
first = -1;
last = -1;
break;
- case GENERATE_UNTIL_ACTIVE: {
+ case Generate::UNTIL_ACTIVE: {
for(i = 0; i < SK.groupOrder.n; i++) {
- if(SK.groupOrder.elem[i].v == SS.GW.activeGroup.v)
+ if(SK.groupOrder[i] == SS.GW.activeGroup)
break;
}
last = i;
break;
}
-
- default: oops();
}
// If we're generating entities for display, first we need to find
// Don't lose our numerical guesses when we regenerate.
IdList<Param,hParam> prev = {};
SK.param.MoveSelfInto(&prev);
+ SK.param.ReserveMore(prev.n);
+ int oldEntityCount = SK.entity.n;
SK.entity.Clear();
+ SK.entity.ReserveMore(oldEntityCount);
- int64_t inTime = GetMilliseconds();
-
- bool displayedStatusMessage = false;
+ // Not using range-for because we're using the index inside the loop.
for(i = 0; i < SK.groupOrder.n; i++) {
- Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
-
- int64_t now = GetMilliseconds();
- // Display the status message if we've taken more than 400 ms, or
- // if we've taken 200 ms but we're not even halfway done, or if
- // we've already started displaying the status message.
- if( (now - inTime > 400) ||
- ((now - inTime > 200) && i < (SK.groupOrder.n / 2)) ||
- displayedStatusMessage)
- {
- displayedStatusMessage = true;
- std::string msg = ssprintf("generating group %d/%d", i, SK.groupOrder.n);
-
- int w, h;
- GetGraphicsWindowSize(&w, &h);
- glDrawBuffer(GL_FRONT);
- glViewport(0, 0, w, h);
- glLoadIdentity();
- glTranslated(-1, 1, 0);
- glScaled(2.0/w, 2.0/h, 1.0);
- glDisable(GL_DEPTH_TEST);
-
- double left = 80, top = -20, width = 240, height = 24;
- glColor3d(0.9, 0.8, 0.8);
- ssglAxisAlignedQuad(left, left+width, top, top-height);
- ssglLineWidth(1);
- glColor3d(0.0, 0.0, 0.0);
- ssglAxisAlignedLineLoop(left, left+width, top, top-height);
-
- ssglInitializeBitmapFont();
- glColor3d(0, 0, 0);
- glPushMatrix();
- glTranslated(left+8, top-20, 0);
- glScaled(1, -1, 1);
- ssglBitmapText(msg, Vector::From(0, 0, 0));
- glPopMatrix();
- glFlush();
- glDrawBuffer(GL_BACK);
- }
+ hGroup hg = SK.groupOrder[i];
// The group may depend on entities or other groups, to define its
// workplane geometry or for its operands. Those must already exist
// in a previous group, so check them before generating.
- if(PruneGroups(g->h))
+ if(PruneGroups(hg))
goto pruned;
- for(j = 0; j < SK.request.n; j++) {
- Request *r = &(SK.request.elem[j]);
- if(r->group.v != g->h.v) continue;
+ for(auto &req : SK.request) {
+ Request *r = &req;
+ if(r->group != hg) continue;
r->Generate(&(SK.entity), &(SK.param));
}
- g->Generate(&(SK.entity), &(SK.param));
+ for(auto &con : SK.constraint) {
+ Constraint *c = &con;
+ if(c->group != hg) continue;
+
+ c->Generate(&(SK.param));
+ }
+ SK.GetGroup(hg)->Generate(&(SK.entity), &(SK.param));
// The requests and constraints depend on stuff in this or the
// previous group, so check them after generating.
- if(PruneRequests(g->h) || PruneConstraints(g->h))
+ if(PruneRequests(hg) || PruneConstraints(hg))
goto pruned;
// Use the previous values for params that we've seen before, as
// initial guesses for the solver.
- for(j = 0; j < SK.param.n; j++) {
- Param *newp = &(SK.param.elem[j]);
+ for(auto &p : SK.param) {
+ Param *newp = &p;
if(newp->known) continue;
Param *prevp = prev.FindByIdNoOops(newp->h);
}
}
- if(g->h.v == Group::HGROUP_REFERENCES.v) {
+ if(hg == Group::HGROUP_REFERENCES) {
ForceReferences();
- g->solved.how = System::SOLVED_OKAY;
+ Group *g = SK.GetGroup(hg);
+ g->solved.how = SolveResult::OKAY;
g->clean = true;
} else {
+ // this i is an index in groupOrder
if(i >= first && i <= last) {
// The group falls inside the range, so really solve it,
// and then regenerate the mesh based on the solved stuff.
+ Group *g = SK.GetGroup(hg);
if(genForBBox) {
- SolveGroupAndReport(g->h, andFindFree);
- } else {
+ SolveGroupAndReport(hg, andFindFree);
g->GenerateLoops();
+ } else {
g->GenerateShellAndMesh();
g->clean = true;
}
// The group falls outside the range, so just assume that
// it's good wherever we left it. The mesh is unchanged,
// and the parameters must be marked as known.
- for(j = 0; j < SK.param.n; j++) {
- Param *newp = &(SK.param.elem[j]);
+ for(auto &p : SK.param) {
+ Param *newp = &p;
Param *prevp = prev.FindByIdNoOops(newp->h);
if(prevp) newp->known = true;
}
// And update any reference dimensions with their new values
- for(i = 0; i < SK.constraint.n; i++) {
- Constraint *c = &(SK.constraint.elem[i]);
+ for(auto &con : SK.constraint) {
+ Constraint *c = &con;
if(c->reference) {
c->ModifyToSatisfy();
}
}
prev.Clear();
- InvalidateGraphics();
+ GW.Invalidate();
// Remove nonexistent selection items, for same reason we waited till
// the end to put up a dialog box.
FreeAllTemporary();
allConsistent = true;
+ SS.GW.persistentDirty = true;
+ SS.centerOfMass.dirty = true;
+
+ endMillis = GetMilliseconds();
+
+ if(endMillis - startMillis > 30) {
+ const char *typeStr = "";
+ switch(type) {
+ case Generate::DIRTY: typeStr = "DIRTY"; break;
+ case Generate::ALL: typeStr = "ALL"; break;
+ case Generate::REGEN: typeStr = "REGEN"; break;
+ case Generate::UNTIL_ACTIVE: typeStr = "UNTIL_ACTIVE"; break;
+ }
+ if(endMillis)
+ dbp("Generate::%s%s took %lld ms",
+ typeStr,
+ (genForBBox ? " (for bounding box)" : ""),
+ GetMilliseconds() - startMillis);
+ }
+
return;
pruned:
GenerateAll(type, andFindFree, genForBBox);
}
-void SolveSpaceUI::ForceReferences(void) {
+void SolveSpaceUI::ForceReferences() {
// Force the values of the parameters that define the three reference
// coordinate systems.
static const struct {
// The origin for our coordinate system, always zero
Entity *origin = SK.GetEntity(wrkpl->point[0]);
origin->PointForceTo(Vector::From(0, 0, 0));
+ origin->construction = true;
SK.GetParam(origin->param[0])->known = true;
SK.GetParam(origin->param[1])->known = true;
SK.GetParam(origin->param[2])->known = true;
}
}
-void SolveSpaceUI::MarkDraggedParams(void) {
+void SolveSpaceUI::UpdateCenterOfMass() {
+ SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
+ SS.centerOfMass.position = m->GetCenterOfMass();
+ SS.centerOfMass.dirty = false;
+}
+
+void SolveSpaceUI::MarkDraggedParams() {
sys.dragged.Clear();
for(int i = -1; i < SS.GW.pending.points.n; i++) {
if(i == -1) {
hp = SS.GW.pending.point;
} else {
- hp = SS.GW.pending.points.elem[i];
+ hp = SS.GW.pending.points[i];
}
if(!hp.v) continue;
Entity *pt = SK.entity.FindByIdNoOops(hp);
if(pt) {
switch(pt->type) {
- case Entity::POINT_N_TRANS:
- case Entity::POINT_IN_3D:
+ case Entity::Type::POINT_N_TRANS:
+ case Entity::Type::POINT_IN_3D:
+ case Entity::Type::POINT_N_ROT_AXIS_TRANS:
sys.dragged.Add(&(pt->param[0]));
sys.dragged.Add(&(pt->param[1]));
sys.dragged.Add(&(pt->param[2]));
break;
- case Entity::POINT_IN_2D:
+ case Entity::Type::POINT_IN_2D:
sys.dragged.Add(&(pt->param[0]));
sys.dragged.Add(&(pt->param[1]));
break;
+
+ default: // Only the entities above can be dragged.
+ break;
}
}
}
if(circ) {
Entity *dist = SK.GetEntity(circ->distance);
switch(dist->type) {
- case Entity::DISTANCE:
+ case Entity::Type::DISTANCE:
sys.dragged.Add(&(dist->param[0]));
break;
+
+ default: // Only the entities above can be dragged.
+ break;
}
}
}
Entity *norm = SK.entity.FindByIdNoOops(SS.GW.pending.normal);
if(norm) {
switch(norm->type) {
- case Entity::NORMAL_IN_3D:
+ case Entity::Type::NORMAL_IN_3D:
sys.dragged.Add(&(norm->param[0]));
sys.dragged.Add(&(norm->param[1]));
sys.dragged.Add(&(norm->param[2]));
sys.dragged.Add(&(norm->param[3]));
break;
- // other types are locked, so not draggable
+
+ default: // Only the entities above can be dragged.
+ break;
}
}
}
SolveGroup(hg, andFindFree);
Group *g = SK.GetGroup(hg);
- if(g->solved.how == System::REDUNDANT_OKAY) {
- // Solve again, in case we lost a degree of freedom because of a numeric error.
- SolveGroup(hg, andFindFree);
- }
-
- bool isOkay = g->solved.how == System::SOLVED_OKAY ||
- (g->allowRedundant && g->solved.how == System::REDUNDANT_OKAY);
-
+ bool isOkay = g->solved.how == SolveResult::OKAY ||
+ (g->allowRedundant && g->solved.how == SolveResult::REDUNDANT_OKAY);
if(!isOkay || (isOkay && !g->IsSolvedOkay())) {
TextWindow::ReportHowGroupSolved(g->h);
}
}
-void SolveSpaceUI::SolveGroup(hGroup hg, bool andFindFree) {
- int i;
+void SolveSpaceUI::WriteEqSystemForGroup(hGroup hg) {
// Clear out the system to be solved.
sys.entity.Clear();
sys.param.Clear();
sys.eq.Clear();
// And generate all the params for requests in this group
- for(i = 0; i < SK.request.n; i++) {
- Request *r = &(SK.request.elem[i]);
- if(r->group.v != hg.v) continue;
+ for(auto &req : SK.request) {
+ Request *r = &req;
+ if(r->group != hg) continue;
r->Generate(&(sys.entity), &(sys.param));
}
+ for(auto &con : SK.constraint) {
+ Constraint *c = &con;
+ if(c->group != hg) continue;
+
+ c->Generate(&(sys.param));
+ }
// And for the group itself
Group *g = SK.GetGroup(hg);
g->Generate(&(sys.entity), &(sys.param));
// Set the initial guesses for all the params
- for(i = 0; i < sys.param.n; i++) {
- Param *p = &(sys.param.elem[i]);
+ for(auto ¶m : sys.param) {
+ Param *p = ¶m;
p->known = false;
p->val = SK.GetParam(p->h)->val;
}
MarkDraggedParams();
+}
+
+void SolveSpaceUI::SolveGroup(hGroup hg, bool andFindFree) {
+ WriteEqSystemForGroup(hg);
+ Group *g = SK.GetGroup(hg);
g->solved.remove.Clear();
- int how = sys.Solve(g, &(g->solved.dof),
- &(g->solved.remove), true, andFindFree);
+ g->solved.findToFixTimeout = SS.timeoutRedundantConstr;
+ SolveResult how = sys.Solve(g, NULL,
+ &(g->solved.dof),
+ &(g->solved.remove),
+ /*andFindBad=*/!g->allowRedundant,
+ /*andFindFree=*/andFindFree,
+ /*forceDofCheck=*/!g->dofCheckOk);
+ if(how == SolveResult::OKAY) {
+ g->dofCheckOk = true;
+ }
g->solved.how = how;
FreeAllTemporary();
}
+SolveResult SolveSpaceUI::TestRankForGroup(hGroup hg, int *rank) {
+ WriteEqSystemForGroup(hg);
+ Group *g = SK.GetGroup(hg);
+ SolveResult result = sys.SolveRank(g, rank);
+ FreeAllTemporary();
+ return result;
+}
+
bool SolveSpaceUI::ActiveGroupsOkay() {
for(int i = 0; i < SK.groupOrder.n; i++) {
- Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
+ Group *g = SK.GetGroup(SK.groupOrder[i]);
if(!g->IsSolvedOkay())
return false;
- if(g->h.v == SS.GW.activeGroup.v)
+ if(g->h == SS.GW.activeGroup)
break;
}
return true;
+++ /dev/null
-//-----------------------------------------------------------------------------
-// Helper functions that ultimately draw stuff with gl.
-//
-// Copyright 2008-2013 Jonathan Westhues.
-//-----------------------------------------------------------------------------
-#include <zlib.h>
-#include "solvespace.h"
-
-namespace SolveSpace {
-
-// A vector font.
-#include "generated/vectorfont.table.h"
-
-// A bitmap font.
-#include "generated/bitmapfont.table.h"
-
-static bool ColorLocked;
-static bool DepthOffsetLocked;
-
-static const VectorGlyph &GetVectorGlyph(char32_t chr) {
- int first = 0;
- int last = sizeof(VectorFont) / sizeof(VectorGlyph);
- while(first <= last) {
- int mid = (first + last) / 2;
- char32_t midChr = VectorFont[mid].character;
- if(midChr > chr) {
- last = mid - 1; // and first stays the same
- continue;
- }
- if(midChr < chr) {
- first = mid + 1; // and last stays the same
- continue;
- }
- return VectorFont[mid];
- }
- return GetVectorGlyph(0xfffd); // replacement character
-}
-
-// Internally and in the UI, the vector font is sized using cap height.
-#define FONT_SCALE(h) ((h)/(double)FONT_CAP_HEIGHT)
-double ssglStrCapHeight(double h)
-{
- return FONT_CAP_HEIGHT * FONT_SCALE(h) / SS.GW.scale;
-}
-double ssglStrFontSize(double h)
-{
- return FONT_SIZE * FONT_SCALE(h) / SS.GW.scale;
-}
-double ssglStrWidth(const std::string &str, double h)
-{
- int width = 0;
- for(char32_t chr : ReadUTF8(str)) {
- const VectorGlyph &glyph = GetVectorGlyph(chr);
- if(glyph.baseCharacter != 0) {
- const VectorGlyph &baseGlyph = GetVectorGlyph(glyph.baseCharacter);
- width += max(glyph.advanceWidth, baseGlyph.advanceWidth);
- } else {
- width += glyph.advanceWidth;
- }
- }
- return width * FONT_SCALE(h) / SS.GW.scale;
-}
-void ssglWriteTextRefCenter(const std::string &str, double h, Vector t, Vector u, Vector v,
- ssglLineFn *fn, void *fndata)
-{
- u = u.WithMagnitude(1);
- v = v.WithMagnitude(1);
-
- double scale = FONT_SCALE(h)/SS.GW.scale;
- double fh = ssglStrCapHeight(h);
- double fw = ssglStrWidth(str, h);
-
- t = t.Plus(u.ScaledBy(-fw/2));
- t = t.Plus(v.ScaledBy(-fh/2));
-
- // Apply additional offset to get an exact center alignment.
- t = t.Plus(v.ScaledBy(-4608*scale));
-
- ssglWriteText(str, h, t, u, v, fn, fndata);
-}
-
-void ssglLineWidth(GLfloat width) {
- // Intel GPUs with Mesa on *nix render thin lines poorly.
- static bool workaroundChecked, workaroundEnabled;
- if(!workaroundChecked) {
- // ssglLineWidth can be called before GL is initialized
- if(glGetString(GL_VENDOR)) {
- workaroundChecked = true;
- if(!strcmp((char*)glGetString(GL_VENDOR), "Intel Open Source Technology Center"))
- workaroundEnabled = true;
- }
- }
-
- if(workaroundEnabled && width < 1.6f)
- width = 1.6f;
-
- glLineWidth(width);
-}
-
-static void LineDrawCallback(void *fndata, Vector a, Vector b)
-{
- ssglLineWidth(1);
- glBegin(GL_LINES);
- ssglVertex3v(a);
- ssglVertex3v(b);
- glEnd();
-}
-
-Vector pixelAlign(Vector v) {
- v = SS.GW.ProjectPoint3(v);
- v.x = floor(v.x) + 0.5;
- v.y = floor(v.y) + 0.5;
- v = SS.GW.UnProjectPoint3(v);
- return v;
-}
-
-int ssglDrawCharacter(const VectorGlyph &glyph, Vector t, Vector o, Vector u, Vector v,
- double scale, ssglLineFn *fn, void *fndata, bool gridFit) {
- int advanceWidth = glyph.advanceWidth;
-
- if(glyph.baseCharacter != 0) {
- const VectorGlyph &baseGlyph = GetVectorGlyph(glyph.baseCharacter);
- int baseWidth = ssglDrawCharacter(baseGlyph, t, o, u, v, scale, fn, fndata, gridFit);
- advanceWidth = max(glyph.advanceWidth, baseWidth);
- }
-
- int actualWidth, offsetX;
- if(gridFit) {
- o.x += glyph.leftSideBearing;
- offsetX = glyph.leftSideBearing;
- actualWidth = glyph.boundingWidth;
- if(actualWidth == 0) {
- // Dot, "i", etc.
- actualWidth = 1;
- }
- } else {
- offsetX = 0;
- actualWidth = advanceWidth;
- }
-
- Vector tt = t;
- tt = tt.Plus(u.ScaledBy(o.x * scale));
- tt = tt.Plus(v.ScaledBy(o.y * scale));
-
- Vector tu = tt;
- tu = tu.Plus(u.ScaledBy(actualWidth * scale));
-
- Vector tv = tt;
- tv = tv.Plus(v.ScaledBy(FONT_CAP_HEIGHT * scale));
-
- if(gridFit) {
- tt = pixelAlign(tt);
- tu = pixelAlign(tu);
- tv = pixelAlign(tv);
- }
-
- tu = tu.Minus(tt).ScaledBy(1.0 / actualWidth);
- tv = tv.Minus(tt).ScaledBy(1.0 / FONT_CAP_HEIGHT);
-
- const int16_t *data = glyph.data;
- bool pen_up = true;
- Vector prevp;
- while(true) {
- int16_t x = *data++;
- int16_t y = *data++;
-
- if(x == PEN_UP && y == PEN_UP) {
- if(pen_up) break;
- pen_up = true;
- } else {
- Vector p = tt;
- p = p.Plus(tu.ScaledBy(x - offsetX));
- p = p.Plus(tv.ScaledBy(y));
- if(!pen_up) fn(fndata, prevp, p);
- prevp = p;
- pen_up = false;
- }
- }
-
- return advanceWidth;
-}
-
-void ssglWriteText(const std::string &str, double h, Vector t, Vector u, Vector v,
- ssglLineFn *fn, void *fndata)
-{
- if(!fn) fn = LineDrawCallback;
- u = u.WithMagnitude(1);
- v = v.WithMagnitude(1);
-
- // Perform grid-fitting only when the text is parallel to the view plane.
- bool gridFit = !SS.exportMode && u.Equals(SS.GW.projRight) && v.Equals(SS.GW.projUp);
-
- double scale = FONT_SCALE(h) / SS.GW.scale;
- Vector o = { 3840.0, 3840.0, 0.0 };
- for(char32_t chr : ReadUTF8(str)) {
- const VectorGlyph &glyph = GetVectorGlyph(chr);
- o.x += ssglDrawCharacter(glyph, t, o, u, v, scale, fn, fndata, gridFit);
- }
-}
-
-void ssglVertex3v(Vector u)
-{
- glVertex3f((GLfloat)u.x, (GLfloat)u.y, (GLfloat)u.z);
-}
-
-void ssglAxisAlignedQuad(double l, double r, double t, double b, bool lone)
-{
- if(lone) glBegin(GL_QUADS);
- glVertex2d(l, t);
- glVertex2d(l, b);
- glVertex2d(r, b);
- glVertex2d(r, t);
- if(lone) glEnd();
-}
-
-void ssglAxisAlignedLineLoop(double l, double r, double t, double b)
-{
- glBegin(GL_LINE_LOOP);
- glVertex2d(l, t);
- glVertex2d(l, b);
- glVertex2d(r, b);
- glVertex2d(r, t);
- glEnd();
-}
-
-static void FatLineEndcap(Vector p, Vector u, Vector v)
-{
- // A table of cos and sin of (pi*i/10 + pi/2), as i goes from 0 to 10
- static const double Circle[11][2] = {
- { 0.0000, 1.0000 },
- { -0.3090, 0.9511 },
- { -0.5878, 0.8090 },
- { -0.8090, 0.5878 },
- { -0.9511, 0.3090 },
- { -1.0000, 0.0000 },
- { -0.9511, -0.3090 },
- { -0.8090, -0.5878 },
- { -0.5878, -0.8090 },
- { -0.3090, -0.9511 },
- { 0.0000, -1.0000 },
- };
- glBegin(GL_TRIANGLE_FAN);
- for(int i = 0; i <= 10; i++) {
- double c = Circle[i][0], s = Circle[i][1];
- ssglVertex3v(p.Plus(u.ScaledBy(c)).Plus(v.ScaledBy(s)));
- }
- glEnd();
-}
-
-void ssglLine(const Vector &a, const Vector &b, double pixelWidth, bool maybeFat) {
- if(!maybeFat || pixelWidth <= 3.0) {
- glBegin(GL_LINES);
- ssglVertex3v(a);
- ssglVertex3v(b);
- glEnd();
- } else {
- ssglFatLine(a, b, pixelWidth / SS.GW.scale);
- }
-}
-
-void ssglPoint(Vector p, double pixelSize)
-{
- if(/*!maybeFat || */pixelSize <= 3.0) {
- glBegin(GL_LINES);
- Vector u = SS.GW.projRight.WithMagnitude(pixelSize / SS.GW.scale / 2.0);
- ssglVertex3v(p.Minus(u));
- ssglVertex3v(p.Plus(u));
- glEnd();
- } else {
- Vector u = SS.GW.projRight.WithMagnitude(pixelSize / SS.GW.scale / 2.0);
- Vector v = SS.GW.projUp.WithMagnitude(pixelSize / SS.GW.scale / 2.0);
-
- FatLineEndcap(p, u, v);
- FatLineEndcap(p, u.ScaledBy(-1.0), v);
- }
-}
-
-void ssglStippledLine(Vector a, Vector b, double width,
- int stippleType, double stippleScale, bool maybeFat)
-{
- const char *stipplePattern;
- switch(stippleType) {
- case Style::STIPPLE_CONTINUOUS: ssglLine(a, b, width, maybeFat); return;
- case Style::STIPPLE_DASH: stipplePattern = "- "; break;
- case Style::STIPPLE_LONG_DASH: stipplePattern = "_ "; break;
- case Style::STIPPLE_DASH_DOT: stipplePattern = "-."; break;
- case Style::STIPPLE_DASH_DOT_DOT: stipplePattern = "-.."; break;
- case Style::STIPPLE_DOT: stipplePattern = "."; break;
- case Style::STIPPLE_FREEHAND: stipplePattern = "~"; break;
- case Style::STIPPLE_ZIGZAG: stipplePattern = "~__"; break;
- default: oops();
- }
- ssglStippledLine(a, b, width, stipplePattern, stippleScale, maybeFat);
-}
-
-void ssglStippledLine(Vector a, Vector b, double width,
- const char *stipplePattern, double stippleScale, bool maybeFat)
-{
- if(stipplePattern == NULL || *stipplePattern == 0) oops();
-
- Vector dir = b.Minus(a);
- double len = dir.Magnitude();
- dir = dir.WithMagnitude(1.0);
-
- const char *si = stipplePattern;
- double end = len;
- double ss = stippleScale / 2.0;
- do {
- double start = end;
- switch(*si) {
- case ' ':
- end -= 1.0 * ss;
- break;
-
- case '-':
- start = max(start - 0.5 * ss, 0.0);
- end = max(start - 2.0 * ss, 0.0);
- if(start == end) break;
- ssglLine(a.Plus(dir.ScaledBy(start)), a.Plus(dir.ScaledBy(end)), width, maybeFat);
- end = max(end - 0.5 * ss, 0.0);
- break;
-
- case '_':
- end = max(end - 4.0 * ss, 0.0);
- ssglLine(a.Plus(dir.ScaledBy(start)), a.Plus(dir.ScaledBy(end)), width, maybeFat);
- break;
-
- case '.':
- end = max(end - 0.5 * ss, 0.0);
- if(end == 0.0) break;
- ssglPoint(a.Plus(dir.ScaledBy(end)), width);
- end = max(end - 0.5 * ss, 0.0);
- break;
-
- case '~': {
- Vector ab = b.Minus(a);
- Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
- Vector abn = (ab.Cross(gn)).WithMagnitude(1);
- abn = abn.Minus(gn.ScaledBy(gn.Dot(abn)));
- double pws = 2.0 * width / SS.GW.scale;
-
- end = max(end - 0.5 * ss, 0.0);
- Vector aa = a.Plus(dir.ScaledBy(start));
- Vector bb = a.Plus(dir.ScaledBy(end))
- .Plus(abn.ScaledBy(pws * (start - end) / (0.5 * ss)));
- ssglLine(aa, bb, width, maybeFat);
- if(end == 0.0) break;
-
- start = end;
- end = max(end - 1.0 * ss, 0.0);
- aa = a.Plus(dir.ScaledBy(end))
- .Plus(abn.ScaledBy(pws))
- .Minus(abn.ScaledBy(2.0 * pws * (start - end) / ss));
- ssglLine(bb, aa, width, maybeFat);
- if(end == 0.0) break;
-
- start = end;
- end = max(end - 0.5 * ss, 0.0);
- bb = a.Plus(dir.ScaledBy(end))
- .Minus(abn.ScaledBy(pws))
- .Plus(abn.ScaledBy(pws * (start - end) / (0.5 * ss)));
- ssglLine(aa, bb, width, maybeFat);
- break;
- }
-
- default: oops();
- }
- if(*(++si) == 0) si = stipplePattern;
- } while(end > 0.0);
-}
-
-void ssglFatLine(Vector a, Vector b, double width)
-{
- if(a.EqualsExactly(b)) return;
- // The half-width of the line we're drawing.
- double hw = width / 2;
- Vector ab = b.Minus(a);
- Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
- Vector abn = (ab.Cross(gn)).WithMagnitude(1);
- abn = abn.Minus(gn.ScaledBy(gn.Dot(abn)));
- // So now abn is normal to the projection of ab into the screen, so the
- // line will always have constant thickness as the view is rotated.
-
- abn = abn.WithMagnitude(hw);
- ab = gn.Cross(abn);
- ab = ab. WithMagnitude(hw);
-
- // The body of a line is a quad
- glBegin(GL_QUADS);
- ssglVertex3v(a.Minus(abn));
- ssglVertex3v(b.Minus(abn));
- ssglVertex3v(b.Plus (abn));
- ssglVertex3v(a.Plus (abn));
- glEnd();
- // And the line has two semi-circular end caps.
- FatLineEndcap(a, ab, abn);
- FatLineEndcap(b, ab.ScaledBy(-1), abn);
-}
-
-
-void ssglLockColorTo(RgbaColor rgb)
-{
- ColorLocked = false;
- glColor3d(rgb.redF(), rgb.greenF(), rgb.blueF());
- ColorLocked = true;
-}
-
-void ssglUnlockColor(void)
-{
- ColorLocked = false;
-}
-
-void ssglColorRGB(RgbaColor rgb)
-{
- // Is there a bug in some graphics drivers where this is not equivalent
- // to glColor3d? There seems to be...
- ssglColorRGBa(rgb, 1.0);
-}
-
-void ssglColorRGBa(RgbaColor rgb, double a)
-{
- if(!ColorLocked) glColor4d(rgb.redF(), rgb.greenF(), rgb.blueF(), a);
-}
-
-static void Stipple(bool forSel)
-{
- static bool Init;
- const int BYTES = (32*32)/8;
- static GLubyte HoverMask[BYTES];
- static GLubyte SelMask[BYTES];
- if(!Init) {
- int x, y;
- for(x = 0; x < 32; x++) {
- for(y = 0; y < 32; y++) {
- int i = y*4 + x/8, b = x % 8;
- int ym = y % 4, xm = x % 4;
- for(int k = 0; k < 2; k++) {
- if(xm >= 1 && xm <= 2 && ym >= 1 && ym <= 2) {
- (k == 0 ? SelMask : HoverMask)[i] |= (0x80 >> b);
- }
- ym = (ym + 2) % 4; xm = (xm + 2) % 4;
- }
- }
- }
- Init = true;
- }
-
- glEnable(GL_POLYGON_STIPPLE);
- if(forSel) {
- glPolygonStipple(SelMask);
- } else {
- glPolygonStipple(HoverMask);
- }
-}
-
-static void StippleTriangle(STriangle *tr, bool s, RgbaColor rgb)
-{
- glEnd();
- glDisable(GL_LIGHTING);
- ssglColorRGB(rgb);
- Stipple(s);
- glBegin(GL_TRIANGLES);
- ssglVertex3v(tr->a);
- ssglVertex3v(tr->b);
- ssglVertex3v(tr->c);
- glEnd();
- glEnable(GL_LIGHTING);
- glDisable(GL_POLYGON_STIPPLE);
- glBegin(GL_TRIANGLES);
-}
-
-void ssglFillMesh(bool useSpecColor, RgbaColor specColor,
- SMesh *m, uint32_t h, uint32_t s1, uint32_t s2)
-{
- RgbaColor rgbHovered = Style::Color(Style::HOVERED),
- rgbSelected = Style::Color(Style::SELECTED);
-
- glEnable(GL_NORMALIZE);
- bool hasMaterial = false;
- RgbaColor prevColor;
- glBegin(GL_TRIANGLES);
- for(int i = 0; i < m->l.n; i++) {
- STriangle *tr = &(m->l.elem[i]);
-
- RgbaColor color;
- if(useSpecColor) {
- color = specColor;
- } else {
- color = tr->meta.color;
- }
- if(!hasMaterial || !color.Equals(prevColor)) {
- GLfloat mpf[] = { color.redF(), color.greenF(), color.blueF(), color.alphaF() };
- glEnd();
- glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mpf);
- prevColor = color;
- hasMaterial = true;
- glBegin(GL_TRIANGLES);
- }
-
- if(tr->an.EqualsExactly(Vector::From(0, 0, 0))) {
- // Compute the normal from the vertices
- Vector n = tr->Normal();
- glNormal3d(n.x, n.y, n.z);
- ssglVertex3v(tr->a);
- ssglVertex3v(tr->b);
- ssglVertex3v(tr->c);
- } else {
- // Use the exact normals that are specified
- glNormal3d((tr->an).x, (tr->an).y, (tr->an).z);
- ssglVertex3v(tr->a);
-
- glNormal3d((tr->bn).x, (tr->bn).y, (tr->bn).z);
- ssglVertex3v(tr->b);
-
- glNormal3d((tr->cn).x, (tr->cn).y, (tr->cn).z);
- ssglVertex3v(tr->c);
- }
-
- if((s1 != 0 && tr->meta.face == s1) ||
- (s2 != 0 && tr->meta.face == s2))
- {
- StippleTriangle(tr, true, rgbSelected);
- }
- if(h != 0 && tr->meta.face == h) {
- StippleTriangle(tr, false, rgbHovered);
- }
- }
- glEnd();
-}
-
-static void SSGL_CALLBACK Vertex(Vector *p)
-{
- ssglVertex3v(*p);
-}
-void ssglFillPolygon(SPolygon *p)
-{
- GLUtesselator *gt = gluNewTess();
- gluTessCallback(gt, GLU_TESS_BEGIN, (ssglCallbackFptr *)glBegin);
- gluTessCallback(gt, GLU_TESS_END, (ssglCallbackFptr *)glEnd);
- gluTessCallback(gt, GLU_TESS_VERTEX, (ssglCallbackFptr *)Vertex);
-
- ssglTesselatePolygon(gt, p);
-
- gluDeleteTess(gt);
-}
-
-static void SSGL_CALLBACK Combine(double coords[3], void *vertexData[4],
- float weight[4], void **outData)
-{
- Vector *n = (Vector *)AllocTemporary(sizeof(Vector));
- n->x = coords[0];
- n->y = coords[1];
- n->z = coords[2];
-
- *outData = n;
-}
-void ssglTesselatePolygon(GLUtesselator *gt, SPolygon *p)
-{
- int i, j;
-
- gluTessCallback(gt, GLU_TESS_COMBINE, (ssglCallbackFptr *)Combine);
- gluTessProperty(gt, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
-
- Vector normal = p->normal;
- glNormal3d(normal.x, normal.y, normal.z);
- gluTessNormal(gt, normal.x, normal.y, normal.z);
-
- gluTessBeginPolygon(gt, NULL);
- for(i = 0; i < p->l.n; i++) {
- SContour *sc = &(p->l.elem[i]);
- gluTessBeginContour(gt);
- for(j = 0; j < (sc->l.n-1); j++) {
- SPoint *sp = &(sc->l.elem[j]);
- double ap[3];
- ap[0] = sp->p.x;
- ap[1] = sp->p.y;
- ap[2] = sp->p.z;
- gluTessVertex(gt, ap, &(sp->p));
- }
- gluTessEndContour(gt);
- }
- gluTessEndPolygon(gt);
-}
-
-void ssglDebugPolygon(SPolygon *p)
-{
- int i, j;
- ssglLineWidth(2);
- glPointSize(7);
- glDisable(GL_DEPTH_TEST);
- for(i = 0; i < p->l.n; i++) {
- SContour *sc = &(p->l.elem[i]);
- for(j = 0; j < (sc->l.n-1); j++) {
- Vector a = (sc->l.elem[j]).p;
- Vector b = (sc->l.elem[j+1]).p;
-
- ssglLockColorTo(RGBi(0, 0, 255));
- Vector d = (a.Minus(b)).WithMagnitude(-0);
- glBegin(GL_LINES);
- ssglVertex3v(a.Plus(d));
- ssglVertex3v(b.Minus(d));
- glEnd();
- ssglLockColorTo(RGBi(255, 0, 0));
- glBegin(GL_POINTS);
- ssglVertex3v(a.Plus(d));
- ssglVertex3v(b.Minus(d));
- glEnd();
- }
- }
-}
-
-void ssglDrawEdges(SEdgeList *el, bool endpointsToo, hStyle hs)
-{
- double lineWidth = Style::Width(hs);
- int stippleType = Style::PatternType(hs);
- double stippleScale = Style::StippleScaleMm(hs);
- ssglLineWidth(float(lineWidth));
- ssglColorRGB(Style::Color(hs));
-
- SEdge *se;
- for(se = el->l.First(); se; se = el->l.NextAfter(se)) {
- ssglStippledLine(se->a, se->b, lineWidth, stippleType, stippleScale,
- /*maybeFat=*/true);
- }
-
- if(endpointsToo) {
- glPointSize(12);
- glBegin(GL_POINTS);
- for(se = el->l.First(); se; se = el->l.NextAfter(se)) {
- ssglVertex3v(se->a);
- ssglVertex3v(se->b);
- }
- glEnd();
- }
-}
-
-void ssglDrawOutlines(SOutlineList *sol, Vector projDir, hStyle hs)
-{
- double lineWidth = Style::Width(hs);
- int stippleType = Style::PatternType(hs);
- double stippleScale = Style::StippleScaleMm(hs);
- ssglLineWidth((float)lineWidth);
- ssglColorRGB(Style::Color(hs));
-
- sol->FillOutlineTags(projDir);
- for(SOutline *so = sol->l.First(); so; so = sol->l.NextAfter(so)) {
- if(!so->tag) continue;
- ssglStippledLine(so->a, so->b, lineWidth, stippleType, stippleScale,
- /*maybeFat=*/true);
- }
-}
-
-void ssglDebugMesh(SMesh *m)
-{
- int i;
- ssglLineWidth(1);
- glPointSize(7);
- ssglDepthRangeOffset(1);
- ssglUnlockColor();
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- ssglColorRGBa(RGBi(0, 255, 0), 1.0);
- glBegin(GL_TRIANGLES);
- for(i = 0; i < m->l.n; i++) {
- STriangle *t = &(m->l.elem[i]);
- if(t->tag) continue;
-
- ssglVertex3v(t->a);
- ssglVertex3v(t->b);
- ssglVertex3v(t->c);
- }
- glEnd();
- ssglDepthRangeOffset(0);
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-}
-
-void ssglMarkPolygonNormal(SPolygon *p)
-{
- Vector tail = Vector::From(0, 0, 0);
- int i, j, cnt = 0;
- // Choose some reasonable center point.
- for(i = 0; i < p->l.n; i++) {
- SContour *sc = &(p->l.elem[i]);
- for(j = 0; j < (sc->l.n-1); j++) {
- SPoint *sp = &(sc->l.elem[j]);
- tail = tail.Plus(sp->p);
- cnt++;
- }
- }
- if(cnt == 0) return;
- tail = tail.ScaledBy(1.0/cnt);
-
- Vector gn = SS.GW.projRight.Cross(SS.GW.projUp);
- Vector tip = tail.Plus((p->normal).WithMagnitude(40/SS.GW.scale));
- Vector arrow = (p->normal).WithMagnitude(15/SS.GW.scale);
-
- glColor3d(1, 1, 0);
- glBegin(GL_LINES);
- ssglVertex3v(tail);
- ssglVertex3v(tip);
- ssglVertex3v(tip);
- ssglVertex3v(tip.Minus(arrow.RotatedAbout(gn, 0.6)));
- ssglVertex3v(tip);
- ssglVertex3v(tip.Minus(arrow.RotatedAbout(gn, -0.6)));
- glEnd();
- glEnable(GL_LIGHTING);
-}
-
-void ssglDepthRangeOffset(int units)
-{
- if(!DepthOffsetLocked) {
- // The size of this step depends on the resolution of the Z buffer; for
- // a 16-bit buffer, this should be fine.
- double d = units/60000.0;
- glDepthRange(0.1-d, 1-d);
- }
-}
-
-void ssglDepthRangeLockToFront(bool yes)
-{
- if(yes) {
- DepthOffsetLocked = true;
- glDepthRange(0, 0);
- } else {
- DepthOffsetLocked = false;
- ssglDepthRangeOffset(0);
- }
-}
-
-const int BitmapFontChunkSize = 64 * 64;
-static bool BitmapFontChunkInitialized[0x10000 / BitmapFontChunkSize];
-static int BitmapFontCurrentChunk = -1;
-
-static void CreateBitmapFontChunk(const uint8_t *source, size_t sourceLength,
- int textureIndex)
-{
- // Place the font in our texture in a two-dimensional grid.
- // The maximum texture size that is reasonably supported is 1024x1024.
- const size_t fontTextureSize = BitmapFontChunkSize*16*16;
- uint8_t *fontTexture = (uint8_t *)malloc(fontTextureSize),
- *mappedTexture = (uint8_t *)malloc(fontTextureSize);
-
- z_stream stream;
- stream.zalloc = Z_NULL;
- stream.zfree = Z_NULL;
- stream.opaque = Z_NULL;
- if(inflateInit(&stream) != Z_OK)
- oops();
-
- stream.next_in = (Bytef *)source;
- stream.avail_in = sourceLength;
- stream.next_out = fontTexture;
- stream.avail_out = fontTextureSize;
- if(inflate(&stream, Z_NO_FLUSH) != Z_STREAM_END)
- oops();
- if(stream.avail_out != 0)
- oops();
-
- inflateEnd(&stream);
-
- for(int a = 0; a < BitmapFontChunkSize; a++) {
- int row = a / 64, col = a % 64;
-
- for(int i = 0; i < 16; i++) {
- memcpy(mappedTexture + row*64*16*16 + col*16 + i*64*16,
- fontTexture + a*16*16 + i*16,
- 16);
- }
- }
-
- free(fontTexture);
-
- glBindTexture(GL_TEXTURE_2D, textureIndex);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA,
- 16*64, 64*16,
- 0,
- GL_ALPHA, GL_UNSIGNED_BYTE,
- mappedTexture);
-
- free(mappedTexture);
-}
-
-static void SwitchToBitmapFontChunkFor(char32_t chr)
-{
- int plane = chr / BitmapFontChunkSize,
- textureIndex = TEXTURE_BITMAP_FONT + plane;
-
- if(BitmapFontCurrentChunk != textureIndex) {
- glEnd();
-
- if(!BitmapFontChunkInitialized[plane]) {
- CreateBitmapFontChunk(CompressedFontTexture[plane].data,
- CompressedFontTexture[plane].length,
- textureIndex);
- BitmapFontChunkInitialized[plane] = true;
- } else {
- glBindTexture(GL_TEXTURE_2D, textureIndex);
- }
-
- BitmapFontCurrentChunk = textureIndex;
-
- glBegin(GL_QUADS);
- }
-}
-
-void ssglInitializeBitmapFont()
-{
- memset(BitmapFontChunkInitialized, 0, sizeof(BitmapFontChunkInitialized));
- BitmapFontCurrentChunk = -1;
-}
-
-int ssglBitmapCharWidth(char32_t chr) {
- if(!CodepointProperties[chr].exists)
- chr = 0xfffd; // replacement character
-
- return CodepointProperties[chr].isWide ? 2 : 1;
-}
-
-void ssglBitmapCharQuad(char32_t chr, double x, double y)
-{
- int w, h;
-
- if(!CodepointProperties[chr].exists)
- chr = 0xfffd; // replacement character
-
- h = 16;
- if(chr >= 0xe000 && chr <= 0xefff) {
- // Special character, like a checkbox or a radio button
- w = 16;
- x -= 3;
- } else if(CodepointProperties[chr].isWide) {
- // Wide (usually CJK or reserved) character
- w = 16;
- } else {
- // Normal character
- w = 8;
- }
-
- if(chr != ' ' && chr != 0) {
- int n = chr % BitmapFontChunkSize;
- int row = n / 64, col = n % 64;
- double s0 = col/64.0,
- s1 = (col+1)/64.0,
- t0 = row/64.0,
- t1 = t0 + (w/16.0)/64;
-
- SwitchToBitmapFontChunkFor(chr);
-
- glTexCoord2d(s1, t0);
- glVertex2d(x, y);
-
- glTexCoord2d(s1, t1);
- glVertex2d(x + w, y);
-
- glTexCoord2d(s0, t1);
- glVertex2d(x + w, y - h);
-
- glTexCoord2d(s0, t0);
- glVertex2d(x, y - h);
- }
-}
-
-void ssglBitmapText(const std::string &str, Vector p)
-{
- glEnable(GL_TEXTURE_2D);
- glBegin(GL_QUADS);
- for(char32_t chr : ReadUTF8(str)) {
- ssglBitmapCharQuad(chr, p.x, p.y);
- p.x += 8 * ssglBitmapCharWidth(chr);
- }
- glEnd();
- glDisable(GL_TEXTURE_2D);
-}
-
-void ssglDrawPixelsWithTexture(uint8_t *data, int w, int h)
-{
-#define MAX_DIM 32
- static uint8_t Texture[MAX_DIM*MAX_DIM*3];
- int i, j;
- if(w > MAX_DIM || h > MAX_DIM) oops();
-
- for(i = 0; i < w; i++) {
- for(j = 0; j < h; j++) {
- Texture[(j*MAX_DIM + i)*3 + 0] = data[(j*w + i)*3 + 0];
- Texture[(j*MAX_DIM + i)*3 + 1] = data[(j*w + i)*3 + 1];
- Texture[(j*MAX_DIM + i)*3 + 2] = data[(j*w + i)*3 + 2];
- }
- }
-
- glBindTexture(GL_TEXTURE_2D, TEXTURE_DRAW_PIXELS);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, MAX_DIM, MAX_DIM, 0,
- GL_RGB, GL_UNSIGNED_BYTE, Texture);
-
- glEnable(GL_TEXTURE_2D);
- glBegin(GL_QUADS);
- glTexCoord2d(0, 0);
- glVertex2d(0, h);
-
- glTexCoord2d(((double)w)/MAX_DIM, 0);
- glVertex2d(w, h);
-
- glTexCoord2d(((double)w)/MAX_DIM, ((double)h)/MAX_DIM);
- glVertex2d(w, 0);
-
- glTexCoord2d(0, ((double)h)/MAX_DIM);
- glVertex2d(0, 0);
- glEnd();
- glDisable(GL_TEXTURE_2D);
-}
-
-};
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
#include "solvespace.h"
-#include "config.h"
+
+typedef void MenuHandler(Command id);
+using MenuKind = Platform::MenuItem::Indicator;
+struct MenuEntry {
+ int level; // 0 == on menu bar, 1 == one level down
+ const char *label; // or NULL for a separator
+ Command cmd; // command ID
+ int accel; // keyboard accelerator
+ MenuKind kind;
+ MenuHandler *fn;
+};
#define mView (&GraphicsWindow::MenuView)
#define mEdit (&GraphicsWindow::MenuEdit)
#define mGrp (&Group::MenuGroup)
#define mAna (&SolveSpaceUI::MenuAnalyze)
#define mHelp (&SolveSpaceUI::MenuHelp)
-#define DEL DELETE_KEY
-#define ESC ESCAPE_KEY
+#define SHIFT_MASK 0x100
+#define CTRL_MASK 0x200
+#define FN_MASK 0x400
+
#define S SHIFT_MASK
#define C CTRL_MASK
-#define F(k) (FUNCTION_KEY_BASE+(k))
-#define TN MENU_ITEM_NORMAL
-#define TC MENU_ITEM_CHECK
-#define TR MENU_ITEM_RADIO
-const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
-//level
-// label id accel ty fn
-{ 0, "&File", 0, 0, TN, NULL },
-{ 1, "&New", MNU_NEW, C|'N', TN, mFile },
-{ 1, "&Open...", MNU_OPEN, C|'O', TN, mFile },
-{ 1, "Open &Recent", MNU_OPEN_RECENT, 0, TN, mFile },
-{ 1, "&Save", MNU_SAVE, C|'S', TN, mFile },
-{ 1, "Save &As...", MNU_SAVE_AS, 0, TN, mFile },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "Export &Image...", MNU_EXPORT_PNG, 0, TN, mFile },
-{ 1, "Export 2d &View...", MNU_EXPORT_VIEW, 0, TN, mFile },
-{ 1, "Export 2d &Section...", MNU_EXPORT_SECTION, 0, TN, mFile },
-{ 1, "Export 3d &Wireframe...", MNU_EXPORT_WIREFRAME, 0, TN, mFile },
-{ 1, "Export Triangle &Mesh...", MNU_EXPORT_MESH, 0, TN, mFile },
-{ 1, "Export &Surfaces...", MNU_EXPORT_SURFACES,0, TN, mFile },
-{ 1, "Im&port...", MNU_IMPORT ,0, TN, mFile },
+#define F FN_MASK
+#define KN MenuKind::NONE
+#define KC MenuKind::CHECK_MARK
+#define KR MenuKind::RADIO_MARK
+const MenuEntry Menu[] = {
+//lv label cmd accel kind
+{ 0, N_("&File"), Command::NONE, 0, KN, NULL },
+{ 1, N_("&New"), Command::NEW, C|'n', KN, mFile },
+{ 1, N_("&Open..."), Command::OPEN, C|'o', KN, mFile },
+{ 1, N_("Open &Recent"), Command::OPEN_RECENT, 0, KN, mFile },
+{ 1, N_("&Save"), Command::SAVE, C|'s', KN, mFile },
+{ 1, N_("Save &As..."), Command::SAVE_AS, 0, KN, mFile },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("Export &Image..."), Command::EXPORT_IMAGE, 0, KN, mFile },
+{ 1, N_("Export 2d &View..."), Command::EXPORT_VIEW, 0, KN, mFile },
+{ 1, N_("Export 2d &Section..."), Command::EXPORT_SECTION, 0, KN, mFile },
+{ 1, N_("Export 3d &Wireframe..."), Command::EXPORT_WIREFRAME, 0, KN, mFile },
+{ 1, N_("Export Triangle &Mesh..."), Command::EXPORT_MESH, 0, KN, mFile },
+{ 1, N_("Export &Surfaces..."), Command::EXPORT_SURFACES, 0, KN, mFile },
+{ 1, N_("Im&port..."), Command::IMPORT, 0, KN, mFile },
#ifndef __APPLE__
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "E&xit", MNU_EXIT, C|'Q', TN, mFile },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("E&xit"), Command::EXIT, C|'q', KN, mFile },
#endif
-{ 0, "&Edit", 0, 0, TN, NULL },
-{ 1, "&Undo", MNU_UNDO, C|'Z', TN, mEdit },
-{ 1, "&Redo", MNU_REDO, C|'Y', TN, mEdit },
-{ 1, "Re&generate All", MNU_REGEN_ALL, ' ', TN, mEdit },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "Snap Selection to &Grid", MNU_SNAP_TO_GRID, '.', TN, mEdit },
-{ 1, "Rotate Imported &90°", MNU_ROTATE_90, '9', TN, mEdit },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "Cu&t", MNU_CUT, C|'X', TN, mClip },
-{ 1, "&Copy", MNU_COPY, C|'C', TN, mClip },
-{ 1, "&Paste", MNU_PASTE, C|'V', TN, mClip },
-{ 1, "Paste &Transformed...", MNU_PASTE_TRANSFORM,C|'T', TN, mClip },
-{ 1, "&Delete", MNU_DELETE, DEL, TN, mClip },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "Select &Edge Chain", MNU_SELECT_CHAIN, C|'E', TN, mEdit },
-{ 1, "Select &All", MNU_SELECT_ALL, C|'A', TN, mEdit },
-{ 1, "&Unselect All", MNU_UNSELECT_ALL, ESC, TN, mEdit },
-
-{ 0, "&View", 0, 0, TN, NULL },
-{ 1, "Zoom &In", MNU_ZOOM_IN, '+', TN, mView },
-{ 1, "Zoom &Out", MNU_ZOOM_OUT, '-', TN, mView },
-{ 1, "Zoom To &Fit", MNU_ZOOM_TO_FIT, 'F', TN, mView },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "Align View to &Workplane", MNU_ONTO_WORKPLANE, 'W', TN, mView },
-{ 1, "Nearest &Ortho View", MNU_NEAREST_ORTHO, F(2), TN, mView },
-{ 1, "Nearest &Isometric View", MNU_NEAREST_ISO, F(3), TN, mView },
-{ 1, "&Center View At Point", MNU_CENTER_VIEW, F(4), TN, mView },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "Show Snap &Grid", MNU_SHOW_GRID, '>', TC, mView },
-{ 1, "Use &Perspective Projection", MNU_PERSPECTIVE_PROJ,'`', TC, mView },
-{ 1, NULL, 0, 0, TN, NULL },
-#if defined(__APPLE__)
-{ 1, "Show Menu &Bar", MNU_SHOW_MENU_BAR, C|F(12), TC, mView },
-#endif
-{ 1, "Show &Toolbar", MNU_SHOW_TOOLBAR, 0, TC, mView },
-{ 1, "Show Property Bro&wser", MNU_SHOW_TEXT_WND, '\t', TC, mView },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "Dimensions in &Inches", MNU_UNITS_INCHES, 0, TR, mView },
-{ 1, "Dimensions in &Millimeters", MNU_UNITS_MM, 0, TR, mView },
-#if defined(HAVE_GTK) || defined(__APPLE__)
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "&Full Screen", MNU_FULL_SCREEN, C|F(11), TC, mView },
+{ 0, N_("&Edit"), Command::NONE, 0, KN, NULL },
+{ 1, N_("&Undo"), Command::UNDO, C|'z', KN, mEdit },
+{ 1, N_("&Redo"), Command::REDO, C|'y', KN, mEdit },
+{ 1, N_("Re&generate All"), Command::REGEN_ALL, ' ', KN, mEdit },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("Snap Selection to &Grid"), Command::SNAP_TO_GRID, '.', KN, mEdit },
+{ 1, N_("Rotate Imported &90°"), Command::ROTATE_90, '9', KN, mEdit },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("Cu&t"), Command::CUT, C|'x', KN, mClip },
+{ 1, N_("&Copy"), Command::COPY, C|'c', KN, mClip },
+{ 1, N_("&Paste"), Command::PASTE, C|'v', KN, mClip },
+{ 1, N_("Paste &Transformed..."), Command::PASTE_TRANSFORM, C|'t', KN, mClip },
+{ 1, N_("&Delete"), Command::DELETE, '\x7f', KN, mClip },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("Select &Edge Chain"), Command::SELECT_CHAIN, C|'e', KN, mEdit },
+{ 1, N_("Select &All"), Command::SELECT_ALL, C|'a', KN, mEdit },
+{ 1, N_("&Unselect All"), Command::UNSELECT_ALL, '\x1b', KN, mEdit },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("&Line Styles..."), Command::EDIT_LINE_STYLES, 0, KN, mEdit },
+{ 1, N_("&View Projection..."), Command::VIEW_PROJECTION, 0, KN, mEdit },
+#ifndef __APPLE__
+{ 1, N_("Con&figuration..."), Command::CONFIGURATION, 0, KN, mEdit },
#endif
-{ 0, "&New Group", 0, 0, TN, NULL },
-{ 1, "Sketch In &3d", MNU_GROUP_3D, S|'3', TN, mGrp },
-{ 1, "Sketch In New &Workplane", MNU_GROUP_WRKPL, S|'W', TN, mGrp },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "Step &Translating", MNU_GROUP_TRANS, S|'T', TN, mGrp },
-{ 1, "Step &Rotating", MNU_GROUP_ROT, S|'R', TN, mGrp },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "E&xtrude", MNU_GROUP_EXTRUDE, S|'X', TN, mGrp },
-{ 1, "&Lathe", MNU_GROUP_LATHE, S|'L', TN, mGrp },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "Link / Assemble...", MNU_GROUP_LINK, S|'I', TN, mGrp },
-{ 1, "Link Recent", MNU_GROUP_RECENT, 0, TN, mGrp },
-
-{ 0, "&Sketch", 0, 0, TN, NULL },
-{ 1, "In &Workplane", MNU_SEL_WORKPLANE, '2', TR, mReq },
-{ 1, "Anywhere In &3d", MNU_FREE_IN_3D, '3', TR, mReq },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "Datum &Point", MNU_DATUM_POINT, 'P', TN, mReq },
-{ 1, "&Workplane", MNU_WORKPLANE, 0, TN, mReq },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "Line &Segment", MNU_LINE_SEGMENT, 'S', TN, mReq },
-{ 1, "C&onstruction Line Segment", MNU_CONSTR_SEGMENT, S|'S', TN, mReq },
-{ 1, "&Rectangle", MNU_RECTANGLE, 'R', TN, mReq },
-{ 1, "&Circle", MNU_CIRCLE, 'C', TN, mReq },
-{ 1, "&Arc of a Circle", MNU_ARC, 'A', TN, mReq },
-{ 1, "&Bezier Cubic Spline", MNU_CUBIC, 'B', TN, mReq },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "&Text in TrueType Font", MNU_TTF_TEXT, 'T', TN, mReq },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "To&ggle Construction", MNU_CONSTRUCTION, 'G', TN, mReq },
-{ 1, "Tangent &Arc at Point", MNU_TANGENT_ARC, S|'A', TN, mReq },
-{ 1, "Split Curves at &Intersection", MNU_SPLIT_CURVES, 'I', TN, mReq },
-
-{ 0, "&Constrain", 0, 0, TN, NULL },
-{ 1, "&Distance / Diameter", MNU_DISTANCE_DIA, 'D', TN, mCon },
-{ 1, "Re&ference Dimension", MNU_REF_DISTANCE, S|'D', TN, mCon },
-{ 1, "A&ngle", MNU_ANGLE, 'N', TN, mCon },
-{ 1, "Reference An&gle", MNU_REF_ANGLE, S|'N', TN, mCon },
-{ 1, "Other S&upplementary Angle", MNU_OTHER_ANGLE, 'U', TN, mCon },
-{ 1, "Toggle R&eference Dim", MNU_REFERENCE, 'E', TN, mCon },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "&Horizontal", MNU_HORIZONTAL, 'H', TN, mCon },
-{ 1, "&Vertical", MNU_VERTICAL, 'V', TN, mCon },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "&On Point / Curve / Plane", MNU_ON_ENTITY, 'O', TN, mCon },
-{ 1, "E&qual Length / Radius / Angle", MNU_EQUAL, 'Q', TN, mCon },
-{ 1, "Length Ra&tio", MNU_RATIO, 'Z', TN, mCon },
-{ 1, "Length Diff&erence", MNU_DIFFERENCE, 'J', TN, mCon },
-{ 1, "At &Midpoint", MNU_AT_MIDPOINT, 'M', TN, mCon },
-{ 1, "S&ymmetric", MNU_SYMMETRIC, 'Y', TN, mCon },
-{ 1, "Para&llel / Tangent", MNU_PARALLEL, 'L', TN, mCon },
-{ 1, "&Perpendicular", MNU_PERPENDICULAR, '[', TN, mCon },
-{ 1, "Same Orient&ation", MNU_ORIENTED_SAME, 'X', TN, mCon },
-{ 1, "Lock Point Where &Dragged", MNU_WHERE_DRAGGED, ']', TN, mCon },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "Comment", MNU_COMMENT, ';', TN, mCon },
-
-{ 0, "&Analyze", 0, 0, TN, NULL },
-{ 1, "Measure &Volume", MNU_VOLUME, C|S|'V', TN, mAna },
-{ 1, "Measure &Area", MNU_AREA, C|S|'A', TN, mAna },
-{ 1, "Show &Interfering Parts", MNU_INTERFERENCE, C|S|'I', TN, mAna },
-{ 1, "Show &Naked Edges", MNU_NAKED_EDGES, C|S|'N', TN, mAna },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "Show Degrees of &Freedom", MNU_SHOW_DOF, C|S|'F', TN, mAna },
-{ 1, NULL, 0, 0, TN, NULL },
-{ 1, "&Trace Point", MNU_TRACE_PT, C|S|'T', TN, mAna },
-{ 1, "&Stop Tracing...", MNU_STOP_TRACING, C|S|'S', TN, mAna },
-{ 1, "Step &Dimension...", MNU_STEP_DIM, C|S|'D', TN, mAna },
-
-{ 0, "&Help", 0, 0, TN, NULL },
-{ 1, "&Website / Manual", MNU_WEBSITE, 0, TN, mHelp },
+{ 0, N_("&View"), Command::NONE, 0, KN, mView },
+{ 1, N_("Zoom &In"), Command::ZOOM_IN, '+', KN, mView },
+{ 1, N_("Zoom &Out"), Command::ZOOM_OUT, '-', KN, mView },
+{ 1, N_("Zoom To &Fit"), Command::ZOOM_TO_FIT, 'f', KN, mView },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("Align View to &Workplane"), Command::ONTO_WORKPLANE, 'w', KN, mView },
+{ 1, N_("Nearest &Ortho View"), Command::NEAREST_ORTHO, F|2, KN, mView },
+{ 1, N_("Nearest &Isometric View"), Command::NEAREST_ISO, F|3, KN, mView },
+{ 1, N_("&Center View At Point"), Command::CENTER_VIEW, F|4, KN, mView },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("Show Snap &Grid"), Command::SHOW_GRID, '>', KC, mView },
+{ 1, N_("Use &Perspective Projection"), Command::PERSPECTIVE_PROJ, '`', KC, mView },
+{ 1, N_("Dimension &Units"), Command::NONE, 0, KN, NULL },
+{ 2, N_("Dimensions in &Millimeters"), Command::UNITS_MM, 0, KR, mView },
+{ 2, N_("Dimensions in M&eters"), Command::UNITS_METERS, 0, KR, mView },
+{ 2, N_("Dimensions in &Inches"), Command::UNITS_INCHES, 0, KR, mView },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("Show &Toolbar"), Command::SHOW_TOOLBAR, 0, KC, mView },
+{ 1, N_("Show Property Bro&wser"), Command::SHOW_TEXT_WND, '\t', KC, mView },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("&Full Screen"), Command::FULL_SCREEN, C|F|11, KC, mView },
+
+{ 0, N_("&New Group"), Command::NONE, 0, KN, mGrp },
+{ 1, N_("Sketch In &3d"), Command::GROUP_3D, S|'3', KN, mGrp },
+{ 1, N_("Sketch In New &Workplane"), Command::GROUP_WRKPL, S|'w', KN, mGrp },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("Step &Translating"), Command::GROUP_TRANS, S|'t', KN, mGrp },
+{ 1, N_("Step &Rotating"), Command::GROUP_ROT, S|'r', KN, mGrp },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("E&xtrude"), Command::GROUP_EXTRUDE, S|'x', KN, mGrp },
+{ 1, N_("&Helix"), Command::GROUP_HELIX, S|'h', KN, mGrp },
+{ 1, N_("&Lathe"), Command::GROUP_LATHE, S|'l', KN, mGrp },
+{ 1, N_("Re&volve"), Command::GROUP_REVOLVE, S|'v', KN, mGrp },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("Link / Assemble..."), Command::GROUP_LINK, S|'i', KN, mGrp },
+{ 1, N_("Link Recent"), Command::GROUP_RECENT, 0, KN, mGrp },
+
+{ 0, N_("&Sketch"), Command::NONE, 0, KN, mReq },
+{ 1, N_("In &Workplane"), Command::SEL_WORKPLANE, '2', KR, mReq },
+{ 1, N_("Anywhere In &3d"), Command::FREE_IN_3D, '3', KR, mReq },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("Datum &Point"), Command::DATUM_POINT, 'p', KN, mReq },
+{ 1, N_("&Workplane"), Command::WORKPLANE, 0, KN, mReq },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("Line &Segment"), Command::LINE_SEGMENT, 's', KN, mReq },
+{ 1, N_("C&onstruction Line Segment"), Command::CONSTR_SEGMENT, S|'s', KN, mReq },
+{ 1, N_("&Rectangle"), Command::RECTANGLE, 'r', KN, mReq },
+{ 1, N_("&Circle"), Command::CIRCLE, 'c', KN, mReq },
+{ 1, N_("&Arc of a Circle"), Command::ARC, 'a', KN, mReq },
+{ 1, N_("&Bezier Cubic Spline"), Command::CUBIC, 'b', KN, mReq },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("&Text in TrueType Font"), Command::TTF_TEXT, 't', KN, mReq },
+{ 1, N_("&Image"), Command::IMAGE, 0, KN, mReq },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("To&ggle Construction"), Command::CONSTRUCTION, 'g', KN, mReq },
+{ 1, N_("Tangent &Arc at Point"), Command::TANGENT_ARC, S|'a', KN, mReq },
+{ 1, N_("Split Curves at &Intersection"), Command::SPLIT_CURVES, 'i', KN, mReq },
+
+{ 0, N_("&Constrain"), Command::NONE, 0, KN, mCon },
+{ 1, N_("&Distance / Diameter"), Command::DISTANCE_DIA, 'd', KN, mCon },
+{ 1, N_("Re&ference Dimension"), Command::REF_DISTANCE, S|'d', KN, mCon },
+{ 1, N_("A&ngle"), Command::ANGLE, 'n', KN, mCon },
+{ 1, N_("Reference An&gle"), Command::REF_ANGLE, S|'n', KN, mCon },
+{ 1, N_("Other S&upplementary Angle"), Command::OTHER_ANGLE, 'u', KN, mCon },
+{ 1, N_("Toggle R&eference Dim"), Command::REFERENCE, 'e', KN, mCon },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("&Horizontal"), Command::HORIZONTAL, 'h', KN, mCon },
+{ 1, N_("&Vertical"), Command::VERTICAL, 'v', KN, mCon },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("&On Point / Curve / Plane"), Command::ON_ENTITY, 'o', KN, mCon },
+{ 1, N_("E&qual Length / Radius / Angle"), Command::EQUAL, 'q', KN, mCon },
+{ 1, N_("Length Ra&tio"), Command::RATIO, 'z', KN, mCon },
+{ 1, N_("Length Diff&erence"), Command::DIFFERENCE, 'j', KN, mCon },
+{ 1, N_("At &Midpoint"), Command::AT_MIDPOINT, 'm', KN, mCon },
+{ 1, N_("S&ymmetric"), Command::SYMMETRIC, 'y', KN, mCon },
+{ 1, N_("Para&llel / Tangent"), Command::PARALLEL, 'l', KN, mCon },
+{ 1, N_("&Perpendicular"), Command::PERPENDICULAR, '[', KN, mCon },
+{ 1, N_("Same Orient&ation"), Command::ORIENTED_SAME, 'x', KN, mCon },
+{ 1, N_("Lock Point Where &Dragged"), Command::WHERE_DRAGGED, ']', KN, mCon },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("Comment"), Command::COMMENT, ';', KN, mCon },
+
+{ 0, N_("&Analyze"), Command::NONE, 0, KN, mAna },
+{ 1, N_("Measure &Volume"), Command::VOLUME, C|S|'v', KN, mAna },
+{ 1, N_("Measure A&rea"), Command::AREA, C|S|'a', KN, mAna },
+{ 1, N_("Measure &Perimeter"), Command::PERIMETER, C|S|'p', KN, mAna },
+{ 1, N_("Show &Interfering Parts"), Command::INTERFERENCE, C|S|'i', KN, mAna },
+{ 1, N_("Show &Naked Edges"), Command::NAKED_EDGES, C|S|'n', KN, mAna },
+{ 1, N_("Show &Center of Mass"), Command::CENTER_OF_MASS, C|S|'c', KN, mAna },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("Show &Underconstrained Points"), Command::SHOW_DOF, C|S|'f', KN, mAna },
+{ 1, NULL, Command::NONE, 0, KN, NULL },
+{ 1, N_("&Trace Point"), Command::TRACE_PT, C|S|'t', KN, mAna },
+{ 1, N_("&Stop Tracing..."), Command::STOP_TRACING, C|S|'s', KN, mAna },
+{ 1, N_("Step &Dimension..."), Command::STEP_DIM, C|S|'d', KN, mAna },
+
+{ 0, N_("&Help"), Command::NONE, 0, KN, mHelp },
+{ 1, N_("&Language"), Command::LOCALE, 0, KN, mHelp },
+{ 1, N_("&Website / Manual"), Command::WEBSITE, 0, KN, mHelp },
#ifndef __APPLE__
-{ 1, "&About", MNU_ABOUT, 0, TN, mHelp },
+{ 1, N_("&About"), Command::ABOUT, 0, KN, mHelp },
#endif
-
-{ -1, 0, 0, 0, TN, 0 }
+{ -1, 0, Command::NONE, 0, KN, NULL }
};
-
-#undef DEL
-#undef ESC
#undef S
#undef C
#undef F
-#undef TN
-#undef TC
-#undef TR
+#undef KN
+#undef KC
+#undef KR
+
+void GraphicsWindow::ActivateCommand(Command cmd) {
+ for(int i = 0; Menu[i].level >= 0; i++) {
+ if(cmd == Menu[i].cmd) {
+ (Menu[i].fn)((Command)Menu[i].cmd);
+ break;
+ }
+ }
+}
-std::string SolveSpace::MakeAcceleratorLabel(int accel) {
- if(!accel) return "";
+Platform::KeyboardEvent GraphicsWindow::AcceleratorForCommand(Command cmd) {
+ int rawAccel = 0;
+ for(int i = 0; Menu[i].level >= 0; i++) {
+ if(cmd == Menu[i].cmd) {
+ rawAccel = Menu[i].accel;
+ break;
+ }
+ }
- std::string label;
- if(accel & GraphicsWindow::CTRL_MASK) {
- label += "Ctrl+";
+ Platform::KeyboardEvent accel = {};
+ accel.type = Platform::KeyboardEvent::Type::PRESS;
+ if(rawAccel & SHIFT_MASK) {
+ accel.shiftDown = true;
}
- if(accel & GraphicsWindow::SHIFT_MASK) {
- label += "Shift+";
+ if(rawAccel & CTRL_MASK) {
+ accel.controlDown = true;
}
- if(accel >= GraphicsWindow::FUNCTION_KEY_BASE + 1 &&
- accel <= GraphicsWindow::FUNCTION_KEY_BASE + 12) {
- label += ssprintf("F%d", accel - GraphicsWindow::FUNCTION_KEY_BASE);
- } else if(accel == '\t') {
- label += "Tab";
- } else if(accel == ' ') {
- label += "Space";
- } else if(accel == GraphicsWindow::ESCAPE_KEY) {
- label += "Esc";
- } else if(accel == GraphicsWindow::DELETE_KEY) {
- label += "Del";
+ if(rawAccel & FN_MASK) {
+ accel.key = Platform::KeyboardEvent::Key::FUNCTION;
+ accel.num = rawAccel & 0xff;
} else {
- label += (char)(accel & 0xff);
+ accel.key = Platform::KeyboardEvent::Key::CHARACTER;
+ accel.chr = (char)(rawAccel & 0xff);
}
- return label;
+
+ return accel;
}
-void GraphicsWindow::Init(void) {
- scale = 5;
+bool GraphicsWindow::KeyboardEvent(Platform::KeyboardEvent event) {
+ using Platform::KeyboardEvent;
+
+ if(event.type == KeyboardEvent::Type::RELEASE)
+ return true;
+
+ if(event.key == KeyboardEvent::Key::CHARACTER) {
+ if(event.chr == '\b') {
+ // Treat backspace identically to escape.
+ MenuEdit(Command::UNSELECT_ALL);
+ return true;
+ } else if(event.chr == '=') {
+ // Treat = as +. This is specific to US (and US-compatible) keyboard layouts,
+ // but makes zooming from keyboard much more usable on these.
+ // Ideally we'd have a platform-independent way of binding to a particular
+ // physical key regardless of shift status...
+ MenuView(Command::ZOOM_IN);
+ return true;
+ }
+ }
+
+ // On some platforms, the OS does not handle some or all keyboard accelerators,
+ // so handle them here.
+ for(int i = 0; Menu[i].level >= 0; i++) {
+ if(AcceleratorForCommand(Menu[i].cmd).Equals(event)) {
+ ActivateCommand(Menu[i].cmd);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void GraphicsWindow::PopulateMainMenu() {
+ bool unique = false;
+ Platform::MenuBarRef mainMenu = Platform::GetOrCreateMainMenu(&unique);
+ if(unique) mainMenu->Clear();
+
+ Platform::MenuRef currentSubMenu;
+ std::vector<Platform::MenuRef> subMenuStack;
+ for(int i = 0; Menu[i].level >= 0; i++) {
+ while(Menu[i].level > 0 && Menu[i].level <= (int)subMenuStack.size()) {
+ currentSubMenu = subMenuStack.back();
+ subMenuStack.pop_back();
+ }
+
+ if(Menu[i].label == NULL) {
+ currentSubMenu->AddSeparator();
+ continue;
+ }
+
+ std::string label = Translate(Menu[i].label);
+ if(Menu[i].level == 0) {
+ currentSubMenu = mainMenu->AddSubMenu(label);
+ } else if(Menu[i].cmd == Command::OPEN_RECENT) {
+ openRecentMenu = currentSubMenu->AddSubMenu(label);
+ } else if(Menu[i].cmd == Command::GROUP_RECENT) {
+ linkRecentMenu = currentSubMenu->AddSubMenu(label);
+ } else if(Menu[i].cmd == Command::LOCALE) {
+ Platform::MenuRef localeMenu = currentSubMenu->AddSubMenu(label);
+ for(const Locale &locale : Locales()) {
+ localeMenu->AddItem(locale.displayName, [&]() {
+ SetLocale(locale.Culture());
+ Platform::GetSettings()->FreezeString("Locale", locale.Culture());
+
+ SS.UpdateWindowTitles();
+ PopulateMainMenu();
+ EnsureValidActives();
+ });
+ }
+ } else if(Menu[i].fn == NULL) {
+ subMenuStack.push_back(currentSubMenu);
+ currentSubMenu = currentSubMenu->AddSubMenu(label);
+ } else {
+ Platform::MenuItemRef menuItem = currentSubMenu->AddItem(label);
+ menuItem->SetIndicator(Menu[i].kind);
+ if(Menu[i].accel != 0) {
+ menuItem->SetAccelerator(AcceleratorForCommand(Menu[i].cmd));
+ }
+ menuItem->onTrigger = std::bind(Menu[i].fn, Menu[i].cmd);
+
+ if(Menu[i].cmd == Command::SHOW_GRID) {
+ showGridMenuItem = menuItem;
+ } else if(Menu[i].cmd == Command::PERSPECTIVE_PROJ) {
+ perspectiveProjMenuItem = menuItem;
+ } else if(Menu[i].cmd == Command::SHOW_TOOLBAR) {
+ showToolbarMenuItem = menuItem;
+ } else if(Menu[i].cmd == Command::SHOW_TEXT_WND) {
+ showTextWndMenuItem = menuItem;
+ } else if(Menu[i].cmd == Command::FULL_SCREEN) {
+ fullScreenMenuItem = menuItem;
+ } else if(Menu[i].cmd == Command::UNITS_MM) {
+ unitsMmMenuItem = menuItem;
+ } else if(Menu[i].cmd == Command::UNITS_METERS) {
+ unitsMetersMenuItem = menuItem;
+ } else if(Menu[i].cmd == Command::UNITS_INCHES) {
+ unitsInchesMenuItem = menuItem;
+ } else if(Menu[i].cmd == Command::SEL_WORKPLANE) {
+ inWorkplaneMenuItem = menuItem;
+ } else if(Menu[i].cmd == Command::FREE_IN_3D) {
+ in3dMenuItem = menuItem;
+ } else if(Menu[i].cmd == Command::UNDO) {
+ undoMenuItem = menuItem;
+ } else if(Menu[i].cmd == Command::REDO) {
+ redoMenuItem = menuItem;
+ }
+ }
+ }
+
+ PopulateRecentFiles();
+ SS.UndoEnableMenus();
+
+ window->SetMenuBar(mainMenu);
+}
+
+static void PopulateMenuWithPathnames(Platform::MenuRef menu,
+ std::vector<Platform::Path> pathnames,
+ std::function<void(const Platform::Path &)> onTrigger) {
+ menu->Clear();
+ if(pathnames.empty()) {
+ Platform::MenuItemRef menuItem = menu->AddItem(_("(no recent files)"));
+ menuItem->SetEnabled(false);
+ } else {
+ for(Platform::Path pathname : pathnames) {
+ Platform::MenuItemRef menuItem = menu->AddItem(pathname.raw, [=]() {
+ if(FileExists(pathname)) {
+ onTrigger(pathname);
+ } else {
+ Error(_("File '%s' does not exist."), pathname.raw.c_str());
+ }
+ }, /*mnemonics=*/false);
+ }
+ }
+}
+
+void GraphicsWindow::PopulateRecentFiles() {
+ PopulateMenuWithPathnames(openRecentMenu, SS.recentFiles, [](const Platform::Path &path) {
+ if(!SS.OkayToStartNewFile()) return;
+ SS.Load(path);
+ });
+
+ PopulateMenuWithPathnames(linkRecentMenu, SS.recentFiles, [](const Platform::Path &path) {
+ Group::MenuGroup(Command::GROUP_LINK, path);
+ });
+}
+
+void GraphicsWindow::Init() {
+ scale = 5;
offset = Vector::From(0, 0, 0);
projRight = Vector::From(1, 0, 0);
projUp = Vector::From(0, 1, 0);
orig.projUp = projUp;
// And with the last group active
- activeGroup = SK.groupOrder.elem[SK.groupOrder.n - 1];
+ ssassert(!SK.groupOrder.IsEmpty(),
+ "Group order can't be empty since we will activate the last group.");
+ activeGroup = *SK.groupOrder.Last();
SK.GetGroup(activeGroup)->Activate();
showWorkplanes = false;
showNormals = true;
showPoints = true;
+ showConstruction = true;
showConstraints = true;
- showHdnLines = false;
showShaded = true;
showEdges = true;
showMesh = false;
showOutlines = false;
+ drawOccludedAs = DrawOccludedAs::INVISIBLE;
showTextWindow = true;
- ShowTextWindow(showTextWindow);
showSnapGrid = false;
context.active = false;
+ toolbarHovered = Command::NONE;
+
+ if(!window) {
+ window = Platform::CreateWindow();
+ if(window) {
+ using namespace std::placeholders;
+ // Do this first, so that if it causes an onRender event we don't try to paint without
+ // a canvas.
+ window->SetMinContentSize(720, 670);
+ window->onClose = std::bind(&SolveSpaceUI::MenuFile, Command::EXIT);
+ window->onRender = std::bind(&GraphicsWindow::Paint, this);
+ window->onKeyboardEvent = std::bind(&GraphicsWindow::KeyboardEvent, this, _1);
+ window->onMouseEvent = std::bind(&GraphicsWindow::MouseEvent, this, _1);
+ window->onSixDofEvent = std::bind(&GraphicsWindow::SixDofEvent, this, _1);
+ window->onEditingDone = std::bind(&GraphicsWindow::EditControlDone, this, _1);
+ PopulateMainMenu();
+ }
+ }
+
+ if(window) {
+ canvas = CreateRenderer();
+ if(canvas) {
+ persistentCanvas = canvas->CreateBatch();
+ persistentDirty = true;
+ }
+ }
// Do this last, so that all the menus get updated correctly.
ClearSuper();
}
-void GraphicsWindow::AnimateOntoWorkplane(void) {
+void GraphicsWindow::AnimateOntoWorkplane() {
if(!LockedInWorkplane()) return;
Entity *w = SK.GetEntity(ActiveWorkplane());
Quaternion quatf = w->Normal()->NormalGetNum();
+
+ // Get Z pointing vertical, if we're on turntable nav mode:
+ if(SS.turntableNav) {
+ Vector normalRight = quatf.RotationU();
+ Vector normalUp = quatf.RotationV();
+ Vector normal = normalRight.Cross(normalUp);
+ if(normalRight.z != 0) {
+ double theta = atan2(normalUp.z, normalRight.z);
+ theta -= atan2(1, 0);
+ normalRight = normalRight.RotatedAbout(normal, theta);
+ normalUp = normalUp.RotatedAbout(normal, theta);
+ quatf = Quaternion::From(normalRight, normalUp);
+ }
+ }
+
Vector offsetf = (SK.GetEntity(w->point[0])->PointGetNum()).ScaledBy(-1);
+ // If the view screen is open, then we need to refresh it.
+ SS.ScheduleShowTW();
+
AnimateOnto(quatf, offsetf);
}
double mo = (offset0.Minus(offsetf)).Magnitude()*scale;
// Animate transition, unless it's a tiny move.
+ int64_t t0 = GetMilliseconds();
int32_t dt = (mp < 0.01 && mo < 10) ? (-20) :
(int32_t)(100 + 1000*mp + 0.4*mo);
// Don't ever animate for longer than 2000 ms; we can get absurdly
// long translations (as measured in pixels) if the user zooms out, moves,
// and then zooms in again.
if(dt > 2000) dt = 2000;
- int64_t tn, t0 = GetMilliseconds();
- double s = 0;
Quaternion dq = quatf.Times(quat0.Inverse());
- do {
- offset = (offset0.ScaledBy(1 - s)).Plus(offsetf.ScaledBy(s));
- Quaternion quat = (dq.ToThe(s)).Times(quat0);
- quat = quat.WithMagnitude(1);
-
- projRight = quat.RotationU();
- projUp = quat.RotationV();
- PaintGraphics();
-
- tn = GetMilliseconds();
- s = (tn - t0)/((double)dt);
- } while((tn - t0) < dt);
-
- projRight = quatf.RotationU();
- projUp = quatf.RotationV();
- offset = offsetf;
- InvalidateGraphics();
- // If the view screen is open, then we need to refresh it.
- SS.ScheduleShowTW();
+
+ if(!animateTimer) {
+ animateTimer = Platform::CreateTimer();
+ }
+ animateTimer->onTimeout = [=] {
+ int64_t tn = GetMilliseconds();
+ if((tn - t0) < dt) {
+ animateTimer->RunAfterNextFrame();
+
+ double s = (tn - t0)/((double)dt);
+ offset = (offset0.ScaledBy(1 - s)).Plus(offsetf.ScaledBy(s));
+ Quaternion quat = (dq.ToThe(s)).Times(quat0).WithMagnitude(1);
+
+ projRight = quat.RotationU();
+ projUp = quat.RotationV();
+ } else {
+ projRight = quatf.RotationU();
+ projUp = quatf.RotationV();
+ offset = offsetf;
+ }
+ window->Invalidate();
+ };
+ animateTimer->RunAfterNextFrame();
}
void GraphicsWindow::HandlePointForZoomToFit(Vector p, Point2d *pmax, Point2d *pmin,
- double *wmin, bool usePerspective)
+ double *wmin, bool usePerspective,
+ const Camera &camera)
{
double w;
- Vector pp = ProjectPoint4(p, &w);
+ Vector pp = camera.ProjectPoint4(p, &w);
// If usePerspective is true, then we calculate a perspective projection of the point.
// If not, then we do a parallel projection regardless of the current
// scale factor.
*wmin = min(*wmin, w);
}
void GraphicsWindow::LoopOverPoints(const std::vector<Entity *> &entities,
+ const std::vector<Constraint *> &constraints,
const std::vector<hEntity> &faces,
Point2d *pmax, Point2d *pmin, double *wmin,
- bool usePerspective, bool includeMesh) {
+ bool usePerspective, bool includeMesh,
+ const Camera &camera) {
- int i, j;
for(Entity *e : entities) {
if(e->IsPoint()) {
- HandlePointForZoomToFit(e->PointGetNum(), pmax, pmin, wmin, usePerspective);
- } else if(e->type == Entity::CIRCLE) {
+ HandlePointForZoomToFit(e->PointGetNum(), pmax, pmin, wmin, usePerspective, camera);
+ } else if(e->type == Entity::Type::CIRCLE) {
// Lots of entities can extend outside the bbox of their points,
// but circles are particularly bad. We want to get things halfway
// reasonable without the mesh, because a zoom to fit is used to
double r = e->CircleGetRadiusNum();
Vector c = SK.GetEntity(e->point[0])->PointGetNum();
Quaternion q = SK.GetEntity(e->normal)->NormalGetNum();
- for(j = 0; j < 4; j++) {
+ for(int j = 0; j < 4; j++) {
Vector p = (j == 0) ? (c.Plus(q.RotationU().ScaledBy( r))) :
(j == 1) ? (c.Plus(q.RotationU().ScaledBy(-r))) :
(j == 2) ? (c.Plus(q.RotationV().ScaledBy( r))) :
(c.Plus(q.RotationV().ScaledBy(-r)));
- HandlePointForZoomToFit(p, pmax, pmin, wmin, usePerspective);
+ HandlePointForZoomToFit(p, pmax, pmin, wmin, usePerspective, camera);
}
} else {
- // We have to iterate children points, because we can select entites without points
+ // We have to iterate children points, because we can select entities without points
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
if(e->point[i].v == 0) break;
Vector p = SK.GetEntity(e->point[i])->PointGetNum();
- HandlePointForZoomToFit(p, pmax, pmin, wmin, usePerspective);
+ HandlePointForZoomToFit(p, pmax, pmin, wmin, usePerspective, camera);
}
}
}
+ for(Constraint *c : constraints) {
+ std::vector<Vector> refs;
+ c->GetReferencePoints(camera, &refs);
+ for(Vector p : refs) {
+ HandlePointForZoomToFit(p, pmax, pmin, wmin, usePerspective, camera);
+ }
+ }
+
if(!includeMesh && faces.empty()) return;
Group *g = SK.GetGroup(activeGroup);
g->GenerateDisplayItems();
- for(i = 0; i < g->displayMesh.l.n; i++) {
- STriangle *tr = &(g->displayMesh.l.elem[i]);
+ for(int i = 0; i < g->displayMesh.l.n; i++) {
+ STriangle *tr = &(g->displayMesh.l[i]);
if(!includeMesh) {
bool found = false;
for(const hEntity &face : faces) {
}
if(!found) continue;
}
- HandlePointForZoomToFit(tr->a, pmax, pmin, wmin, usePerspective);
- HandlePointForZoomToFit(tr->b, pmax, pmin, wmin, usePerspective);
- HandlePointForZoomToFit(tr->c, pmax, pmin, wmin, usePerspective);
+ HandlePointForZoomToFit(tr->a, pmax, pmin, wmin, usePerspective, camera);
+ HandlePointForZoomToFit(tr->b, pmax, pmin, wmin, usePerspective, camera);
+ HandlePointForZoomToFit(tr->c, pmax, pmin, wmin, usePerspective, camera);
}
if(!includeMesh) return;
- for(i = 0; i < g->polyLoops.l.n; i++) {
- SContour *sc = &(g->polyLoops.l.elem[i]);
- for(j = 0; j < sc->l.n; j++) {
- HandlePointForZoomToFit(sc->l.elem[j].p, pmax, pmin, wmin, usePerspective);
+ for(int i = 0; i < g->polyLoops.l.n; i++) {
+ SContour *sc = &(g->polyLoops.l[i]);
+ for(int j = 0; j < sc->l.n; j++) {
+ HandlePointForZoomToFit(sc->l[j].p, pmax, pmin, wmin, usePerspective, camera);
}
}
}
void GraphicsWindow::ZoomToFit(bool includingInvisibles, bool useSelection) {
+ if(!window) return;
+
+ scale = ZoomToFit(GetCamera(), includingInvisibles, useSelection);
+}
+double GraphicsWindow::ZoomToFit(const Camera &camera,
+ bool includingInvisibles, bool useSelection) {
std::vector<Entity *> entities;
+ std::vector<Constraint *> constraints;
std::vector<hEntity> faces;
if(useSelection) {
for(int i = 0; i < selection.n; i++) {
- Selection *s = &selection.elem[i];
- if(s->entity.v == 0) continue;
- Entity *e = SK.entity.FindById(s->entity);
- if(e->IsFace()) {
- faces.push_back(e->h);
- continue;
+ Selection *s = &selection[i];
+ if(s->entity.v != 0) {
+ Entity *e = SK.entity.FindById(s->entity);
+ if(e->IsFace()) {
+ faces.push_back(e->h);
+ continue;
+ }
+ entities.push_back(e);
+ }
+ if(s->constraint.v != 0) {
+ Constraint *c = SK.constraint.FindById(s->constraint);
+ constraints.push_back(c);
}
- entities.push_back(e);
}
}
- bool selectionUsed = !entities.empty() || !faces.empty();
+ bool selectionUsed = !entities.empty() || !constraints.empty() || !faces.empty();
if(!selectionUsed) {
- for(int i = 0; i<SK.entity.n; i++) {
- Entity *e = &(SK.entity.elem[i]);
+ for(Entity &e : SK.entity) {
// we don't want to handle separate points, because we will iterate them inside entities.
- if(e->IsPoint()) continue;
- if(!includingInvisibles && !e->IsVisible()) continue;
- entities.push_back(e);
+ if(e.IsPoint()) continue;
+ if(!includingInvisibles && !e.IsVisible()) continue;
+ entities.push_back(&e);
+ }
+
+ for(Constraint &c : SK.constraint) {
+ if(!c.IsVisible()) continue;
+ constraints.push_back(&c);
}
}
// On the first run, ignore perspective.
Point2d pmax = { -1e12, -1e12 }, pmin = { 1e12, 1e12 };
double wmin = 1;
- LoopOverPoints(entities, faces, &pmax, &pmin, &wmin,
- /*usePerspective=*/false, /*includeMesh=*/!selectionUsed);
+ LoopOverPoints(entities, constraints, faces, &pmax, &pmin, &wmin,
+ /*usePerspective=*/false, /*includeMesh=*/!selectionUsed,
+ camera);
double xm = (pmax.x + pmin.x)/2, ym = (pmax.y + pmin.y)/2;
double dx = pmax.x - pmin.x, dy = pmax.y - pmin.y;
projUp. ScaledBy(-ym));
// And based on this, we calculate the scale and offset
+ double scale;
if(EXACT(dx == 0 && dy == 0)) {
scale = 5;
} else {
double scalex = 1e12, scaley = 1e12;
- if(EXACT(dx != 0)) scalex = 0.9*width /dx;
- if(EXACT(dy != 0)) scaley = 0.9*height/dy;
+ if(EXACT(dx != 0)) scalex = 0.9*camera.width /dx;
+ if(EXACT(dy != 0)) scaley = 0.9*camera.height/dy;
scale = min(scalex, scaley);
scale = min(300.0, scale);
pmax.x = -1e12; pmax.y = -1e12;
pmin.x = 1e12; pmin.y = 1e12;
wmin = 1;
- LoopOverPoints(entities, faces, &pmax, &pmin, &wmin,
- /*usePerspective=*/true, /*includeMesh=*/!selectionUsed);
+ LoopOverPoints(entities, constraints, faces, &pmax, &pmin, &wmin,
+ /*usePerspective=*/true, /*includeMesh=*/!selectionUsed,
+ camera);
// Adjust the scale so that no points are behind the camera
if(wmin < 0.1) {
- double k = SS.CameraTangent();
+ double k = camera.tangent;
// w = 1+k*scale*z
double zmin = (wmin - 1)/(k*scale);
// 0.1 = 1 + k*scale*zmin
// (0.1 - 1)/(k*zmin) = scale
scale = min(scale, (0.1 - 1)/(k*zmin));
}
+
+ return scale;
}
-void GraphicsWindow::MenuView(int id) {
+void GraphicsWindow::MenuView(Command id) {
switch(id) {
- case MNU_ZOOM_IN:
+ case Command::ZOOM_IN:
SS.GW.scale *= 1.2;
SS.ScheduleShowTW();
break;
- case MNU_ZOOM_OUT:
+ case Command::ZOOM_OUT:
SS.GW.scale /= 1.2;
SS.ScheduleShowTW();
break;
- case MNU_ZOOM_TO_FIT:
+ case Command::ZOOM_TO_FIT:
SS.GW.ZoomToFit(/*includingInvisibles=*/false, /*useSelection=*/true);
SS.ScheduleShowTW();
break;
- case MNU_SHOW_GRID:
+ case Command::SHOW_GRID:
SS.GW.showSnapGrid = !SS.GW.showSnapGrid;
+ SS.GW.EnsureValidActives();
+ SS.GW.Invalidate();
if(SS.GW.showSnapGrid && !SS.GW.LockedInWorkplane()) {
- Message("No workplane is active, so the grid will not "
- "appear.");
+ Message(_("No workplane is active, so the grid will not appear."));
}
- SS.GW.EnsureValidActives();
- InvalidateGraphics();
break;
- case MNU_PERSPECTIVE_PROJ:
+ case Command::PERSPECTIVE_PROJ:
SS.usePerspectiveProj = !SS.usePerspectiveProj;
+ SS.GW.EnsureValidActives();
+ SS.GW.Invalidate();
if(SS.cameraTangent < 1e-6) {
- Error("The perspective factor is set to zero, so the view will "
- "always be a parallel projection.\n\n"
- "For a perspective projection, modify the perspective "
- "factor in the configuration screen. A value around 0.3 "
- "is typical.");
+ Error(_("The perspective factor is set to zero, so the view will "
+ "always be a parallel projection.\n\n"
+ "For a perspective projection, modify the perspective "
+ "factor in the configuration screen. A value around 0.3 "
+ "is typical."));
}
- SS.GW.EnsureValidActives();
- InvalidateGraphics();
break;
- case MNU_ONTO_WORKPLANE:
+ case Command::ONTO_WORKPLANE:
if(SS.GW.LockedInWorkplane()) {
SS.GW.AnimateOntoWorkplane();
- SS.ScheduleShowTW();
break;
- } // if not in 2d mode fall through and use ORTHO logic
- case MNU_NEAREST_ORTHO:
- case MNU_NEAREST_ISO: {
+ } // if not in 2d mode use ORTHO logic
+ // fallthrough
+ case Command::NEAREST_ORTHO:
+ case Command::NEAREST_ISO: {
static const Vector ortho[3] = {
Vector::From(1, 0, 0),
Vector::From(0, 1, 0),
Vector on = ou.Cross(ov);
Vector u, v;
- if(id == MNU_NEAREST_ORTHO || id == MNU_ONTO_WORKPLANE) {
+ if(id == Command::NEAREST_ORTHO || id == Command::ONTO_WORKPLANE) {
u = ou;
v = ov;
} else {
break;
}
- case MNU_CENTER_VIEW:
+ case Command::CENTER_VIEW:
SS.GW.GroupSelection();
if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
Quaternion quat0;
// Offset is the selected point, quaternion is same as before
Vector pt = SK.GetEntity(SS.GW.gs.point[0])->PointGetNum();
quat0 = Quaternion::From(SS.GW.projRight, SS.GW.projUp);
- SS.GW.AnimateOnto(quat0, pt.ScaledBy(-1));
SS.GW.ClearSelection();
+ SS.GW.AnimateOnto(quat0, pt.ScaledBy(-1));
} else {
- Error("Select a point; this point will become the center "
- "of the view on screen.");
+ Error(_("Select a point; this point will become the center "
+ "of the view on screen."));
}
break;
- case MNU_SHOW_MENU_BAR:
- ToggleMenuBar();
+ case Command::SHOW_TOOLBAR:
+ SS.showToolbar = !SS.showToolbar;
SS.GW.EnsureValidActives();
- InvalidateGraphics();
+ SS.GW.Invalidate();
break;
- case MNU_SHOW_TOOLBAR:
- SS.showToolbar = !SS.showToolbar;
+ case Command::SHOW_TEXT_WND:
+ SS.GW.showTextWindow = !SS.GW.showTextWindow;
SS.GW.EnsureValidActives();
- InvalidateGraphics();
break;
- case MNU_SHOW_TEXT_WND:
- SS.GW.showTextWindow = !SS.GW.showTextWindow;
+ case Command::UNITS_INCHES:
+ SS.viewUnits = Unit::INCHES;
+ SS.ScheduleShowTW();
SS.GW.EnsureValidActives();
break;
- case MNU_UNITS_INCHES:
- SS.viewUnits = SolveSpaceUI::UNIT_INCHES;
+ case Command::UNITS_MM:
+ SS.viewUnits = Unit::MM;
SS.ScheduleShowTW();
SS.GW.EnsureValidActives();
break;
- case MNU_UNITS_MM:
- SS.viewUnits = SolveSpaceUI::UNIT_MM;
+ case Command::UNITS_METERS:
+ SS.viewUnits = Unit::METERS;
SS.ScheduleShowTW();
SS.GW.EnsureValidActives();
break;
- case MNU_FULL_SCREEN:
- ToggleFullScreen();
+ case Command::FULL_SCREEN:
+ SS.GW.window->SetFullScreen(!SS.GW.window->IsFullScreen());
SS.GW.EnsureValidActives();
break;
- default: oops();
+ default: ssassert(false, "Unexpected menu ID");
}
- InvalidateGraphics();
+ SS.GW.Invalidate();
}
-void GraphicsWindow::EnsureValidActives(void) {
+void GraphicsWindow::EnsureValidActives() {
bool change = false;
// The active group must exist, and not be the references.
Group *g = SK.group.FindByIdNoOops(activeGroup);
- if((!g) || (g->h.v == Group::HGROUP_REFERENCES.v)) {
+ if((!g) || (g->h == Group::HGROUP_REFERENCES)) {
+ // Not using range-for because this is used to find an index.
int i;
for(i = 0; i < SK.groupOrder.n; i++) {
- if(SK.groupOrder.elem[i].v != Group::HGROUP_REFERENCES.v) {
+ if(SK.groupOrder[i] != Group::HGROUP_REFERENCES) {
break;
}
}
activeGroup = SS.CreateDefaultDrawingGroup();
// We've created the default group, but not the workplane entity;
// do it now so that drawing mode isn't switched to "Free in 3d".
- SS.GenerateAll(SolveSpaceUI::GENERATE_ALL);
+ SS.GenerateAll(SolveSpaceUI::Generate::ALL);
} else {
- activeGroup = SK.groupOrder.elem[i];
+ activeGroup = SK.groupOrder[i];
}
SK.GetGroup(activeGroup)->Activate();
change = true;
Entity *e = SK.entity.FindByIdNoOops(ActiveWorkplane());
if(e) {
hGroup hgw = e->group;
- if(hgw.v != activeGroup.v && SS.GroupsInOrder(activeGroup, hgw)) {
+ if(hgw != activeGroup && SS.GroupsInOrder(activeGroup, hgw)) {
// The active workplane is in a group that comes after the
// active group; so any request or constraint will fail.
SetWorkplaneFreeIn3d();
}
}
+ if(!window) return;
+
// And update the checked state for various menus
bool locked = LockedInWorkplane();
- RadioMenuById(MNU_FREE_IN_3D, !locked);
- RadioMenuById(MNU_SEL_WORKPLANE, locked);
+ in3dMenuItem->SetActive(!locked);
+ inWorkplaneMenuItem->SetActive(locked);
SS.UndoEnableMenus();
switch(SS.viewUnits) {
- case SolveSpaceUI::UNIT_MM:
- case SolveSpaceUI::UNIT_INCHES:
+ case Unit::MM:
+ case Unit::METERS:
+ case Unit::INCHES:
break;
default:
- SS.viewUnits = SolveSpaceUI::UNIT_MM;
+ SS.viewUnits = Unit::MM;
break;
}
- RadioMenuById(MNU_UNITS_MM, SS.viewUnits == SolveSpaceUI::UNIT_MM);
- RadioMenuById(MNU_UNITS_INCHES, SS.viewUnits == SolveSpaceUI::UNIT_INCHES);
+ unitsMmMenuItem->SetActive(SS.viewUnits == Unit::MM);
+ unitsMetersMenuItem->SetActive(SS.viewUnits == Unit::METERS);
+ unitsInchesMenuItem->SetActive(SS.viewUnits == Unit::INCHES);
- ShowTextWindow(SS.GW.showTextWindow);
- CheckMenuById(MNU_SHOW_TEXT_WND, SS.GW.showTextWindow);
+ if(SS.TW.window) SS.TW.window->SetVisible(SS.GW.showTextWindow);
+ showTextWndMenuItem->SetActive(SS.GW.showTextWindow);
-#if defined(__APPLE__)
- CheckMenuById(MNU_SHOW_MENU_BAR, MenuBarIsVisible());
-#endif
- CheckMenuById(MNU_SHOW_TOOLBAR, SS.showToolbar);
- CheckMenuById(MNU_PERSPECTIVE_PROJ, SS.usePerspectiveProj);
- CheckMenuById(MNU_SHOW_GRID, SS.GW.showSnapGrid);
-#if defined(HAVE_GTK) || defined(__APPLE__)
- CheckMenuById(MNU_FULL_SCREEN, FullScreenIsActive());
-#endif
+ showGridMenuItem->SetActive(SS.GW.showSnapGrid);
+ perspectiveProjMenuItem->SetActive(SS.usePerspectiveProj);
+ showToolbarMenuItem->SetActive(SS.showToolbar);
+ fullScreenMenuItem->SetActive(SS.GW.window->IsFullScreen());
if(change) SS.ScheduleShowTW();
}
-void GraphicsWindow::SetWorkplaneFreeIn3d(void) {
+void GraphicsWindow::SetWorkplaneFreeIn3d() {
SK.GetGroup(activeGroup)->activeWorkplane = Entity::FREE_IN_3D;
}
-hEntity GraphicsWindow::ActiveWorkplane(void) {
+hEntity GraphicsWindow::ActiveWorkplane() {
Group *g = SK.group.FindByIdNoOops(activeGroup);
if(g) {
return g->activeWorkplane;
return Entity::FREE_IN_3D;
}
}
-bool GraphicsWindow::LockedInWorkplane(void) {
- return (SS.GW.ActiveWorkplane().v != Entity::FREE_IN_3D.v);
+bool GraphicsWindow::LockedInWorkplane() {
+ return (SS.GW.ActiveWorkplane() != Entity::FREE_IN_3D);
}
-void GraphicsWindow::ForceTextWindowShown(void) {
+void GraphicsWindow::ForceTextWindowShown() {
if(!showTextWindow) {
showTextWindow = true;
- CheckMenuById(MNU_SHOW_TEXT_WND, true);
- ShowTextWindow(true);
+ showTextWndMenuItem->SetActive(true);
+ SS.TW.window->SetVisible(true);
}
}
-void GraphicsWindow::DeleteTaggedRequests(void) {
+void GraphicsWindow::DeleteTaggedRequests() {
+ Request *r;
+ // Delete any requests that were affected by this deletion.
+ for(r = SK.request.First(); r; r = SK.request.NextAfter(r)) {
+ if(r->workplane == Entity::FREE_IN_3D) continue;
+ if(!r->workplane.isFromRequest()) continue;
+ Request *wrkpl = SK.GetRequest(r->workplane.request());
+ if(wrkpl->tag)
+ r->tag = 1;
+ }
// Rewrite any point-coincident constraints that were affected by this
// deletion.
- Request *r;
for(r = SK.request.First(); r; r = SK.request.NextAfter(r)) {
if(!r->tag) continue;
FixConstraintsForRequestBeingDeleted(r->h);
// An edit might be in progress for the just-deleted item. So
// now it's not.
- HideGraphicsEditControl();
+ window->HideEditor();
SS.TW.HideEditControl();
// And clear out the selection, which could contain that item.
ClearSuper();
// And regenerate to get rid of what it generates, plus anything
// that references it (since the regen code checks for that).
- SS.GenerateAll(SolveSpaceUI::GENERATE_ALL);
+ SS.GenerateAll(SolveSpaceUI::Generate::ALL);
EnsureValidActives();
SS.ScheduleShowTW();
}
return pp.ScaleOutOfCsys(wu, wv, wn).Plus(wo);
}
-void GraphicsWindow::MenuEdit(int id) {
+void GraphicsWindow::MenuEdit(Command id) {
switch(id) {
- case MNU_UNSELECT_ALL:
+ case Command::UNSELECT_ALL:
SS.GW.GroupSelection();
// If there's nothing selected to de-select, and no operation
// to cancel, then perhaps they want to return to the home
// screen in the text window.
if(SS.GW.gs.n == 0 &&
SS.GW.gs.constraints == 0 &&
- SS.GW.pending.operation == 0)
+ SS.GW.pending.operation == Pending::NONE)
{
- if(!(TextEditControlIsVisible() ||
- GraphicsEditControlIsVisible()))
+ if(!(SS.TW.window->IsEditorVisible() ||
+ SS.GW.window->IsEditorVisible()))
{
- if(SS.TW.shown.screen == TextWindow::SCREEN_STYLE_INFO) {
- SS.TW.GoToScreen(TextWindow::SCREEN_LIST_OF_STYLES);
+ if(SS.TW.shown.screen == TextWindow::Screen::STYLE_INFO) {
+ SS.TW.GoToScreen(TextWindow::Screen::LIST_OF_STYLES);
} else {
SS.TW.ClearSuper();
}
SS.TW.HideEditControl();
SS.nakedEdges.Clear();
SS.justExportedInfo.draw = false;
+ SS.centerOfMass.draw = false;
// This clears the marks drawn to indicate which points are
// still free to drag.
Param *p;
}
if(SS.exportMode) {
SS.exportMode = false;
- SS.GenerateAll(SolveSpaceUI::GENERATE_ALL);
+ SS.GenerateAll(SolveSpaceUI::Generate::ALL);
}
+ SS.GW.persistentDirty = true;
break;
- case MNU_SELECT_ALL: {
+ case Command::SELECT_ALL: {
Entity *e;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
- if(e->group.v != SS.GW.activeGroup.v) continue;
+ if(e->group != SS.GW.activeGroup) continue;
if(e->IsFace() || e->IsDistance()) continue;
if(!e->IsVisible()) continue;
SS.GW.MakeSelected(e->h);
}
- InvalidateGraphics();
+ SS.GW.Invalidate();
SS.ScheduleShowTW();
break;
}
- case MNU_SELECT_CHAIN: {
+ case Command::SELECT_CHAIN: {
Entity *e;
int newlySelected = 0;
bool didSomething;
do {
didSomething = false;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
- if(e->group.v != SS.GW.activeGroup.v) continue;
+ if(e->group != SS.GW.activeGroup) continue;
if(!e->HasEndpoints()) continue;
if(!e->IsVisible()) continue;
List<Selection> *ls = &(SS.GW.selection);
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
if(!s->entity.v) continue;
- if(s->entity.v == e->h.v) {
+ if(s->entity == e->h) {
alreadySelected = true;
continue;
}
}
}
} while(didSomething);
+ SS.GW.Invalidate();
+ SS.ScheduleShowTW();
if(newlySelected == 0) {
- Error("No additional entities share endpoints with the "
- "selected entities.");
+ Error(_("No additional entities share endpoints with the selected entities."));
}
- InvalidateGraphics();
- SS.ScheduleShowTW();
break;
}
- case MNU_ROTATE_90: {
+ case Command::ROTATE_90: {
SS.GW.GroupSelection();
Entity *e = NULL;
if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
hGroup hg = e ? e->group : SS.GW.activeGroup;
Group *g = SK.GetGroup(hg);
- if(g->type != Group::LINKED) {
- Error("To use this command, select a point or other "
- "entity from an linked part, or make a link "
- "group the active group.");
+ if(g->type != Group::Type::LINKED) {
+ Error(_("To use this command, select a point or other "
+ "entity from an linked part, or make a link "
+ "group the active group."));
break;
}
-
SS.UndoRemember();
// Rotate by ninety degrees about the coordinate axis closest
// to the screen normal.
// and regenerate as necessary.
SS.MarkGroupDirty(hg);
- SS.GenerateAll();
break;
}
- case MNU_SNAP_TO_GRID: {
+ case Command::SNAP_TO_GRID: {
if(!SS.GW.LockedInWorkplane()) {
- Error("No workplane is active. Select a workplane to define "
- "the plane for the snap grid.");
+ Error(_("No workplane is active. Activate a workplane "
+ "(with Sketch -> In Workplane) to define the plane "
+ "for the snap grid."));
break;
}
SS.GW.GroupSelection();
if(SS.GW.gs.points == 0 && SS.GW.gs.constraintLabels == 0) {
- Error("Can't snap these items to grid; select points, "
- "text comments, or constraints with a label. "
- "To snap a line, select its endpoints.");
+ Error(_("Can't snap these items to grid; select points, "
+ "text comments, or constraints with a label. "
+ "To snap a line, select its endpoints."));
break;
}
SS.UndoRemember();
SS.MarkGroupDirty(ep->group);
} else if(s->constraint.v) {
Constraint *c = SK.GetConstraint(s->constraint);
- Vector refp = c->GetReferencePos();
- c->disp.offset = c->disp.offset.Plus(SS.GW.SnapToGrid(refp).Minus(refp));
+ std::vector<Vector> refs;
+ c->GetReferencePoints(SS.GW.GetCamera(), &refs);
+ c->disp.offset = c->disp.offset.Plus(SS.GW.SnapToGrid(refs[0]).Minus(refs[0]));
}
}
// Regenerate, with these points marked as dragged so that they
// get placed as close as possible to our snap grid.
- SS.GenerateAll();
SS.GW.ClearPending();
SS.GW.ClearSelection();
- InvalidateGraphics();
+ SS.GW.Invalidate();
break;
}
- case MNU_UNDO:
+ case Command::UNDO:
SS.UndoUndo();
break;
- case MNU_REDO:
+ case Command::REDO:
SS.UndoRedo();
break;
- case MNU_REGEN_ALL:
- SS.ReloadAllImported();
- SS.GenerateAll(SolveSpaceUI::GENERATE_UNTIL_ACTIVE);
+ case Command::REGEN_ALL:
+ SS.images.clear();
+ SS.ReloadAllLinked(SS.saveFile);
+ SS.GenerateAll(SolveSpaceUI::Generate::UNTIL_ACTIVE);
SS.ScheduleShowTW();
break;
- default: oops();
+ case Command::EDIT_LINE_STYLES:
+ SS.TW.GoToScreen(TextWindow::Screen::LIST_OF_STYLES);
+ SS.GW.ForceTextWindowShown();
+ SS.ScheduleShowTW();
+ break;
+ case Command::VIEW_PROJECTION:
+ SS.TW.GoToScreen(TextWindow::Screen::EDIT_VIEW);
+ SS.GW.ForceTextWindowShown();
+ SS.ScheduleShowTW();
+ break;
+ case Command::CONFIGURATION:
+ SS.TW.GoToScreen(TextWindow::Screen::CONFIGURATION);
+ SS.GW.ForceTextWindowShown();
+ SS.ScheduleShowTW();
+ break;
+
+ default: ssassert(false, "Unexpected menu ID");
}
}
-void GraphicsWindow::MenuRequest(int id) {
+void GraphicsWindow::MenuRequest(Command id) {
const char *s;
switch(id) {
- case MNU_SEL_WORKPLANE: {
+ case Command::SEL_WORKPLANE: {
SS.GW.GroupSelection();
Group *g = SK.GetGroup(SS.GW.activeGroup);
if(SS.GW.gs.n == 1 && SS.GW.gs.workplanes == 1) {
// A user-selected workplane
g->activeWorkplane = SS.GW.gs.entity[0];
- } else if(g->type == Group::DRAWING_WORKPLANE) {
+ SS.GW.EnsureValidActives();
+ SS.ScheduleShowTW();
+ } else if(g->type == Group::Type::DRAWING_WORKPLANE) {
// The group's default workplane
g->activeWorkplane = g->h.entity(0);
- Message("No workplane selected. Activating default workplane "
- "for this group.");
- }
-
- if(!SS.GW.LockedInWorkplane()) {
- Error("No workplane is selected, and the active group does "
- "not have a default workplane. Try selecting a "
- "workplane, or activating a sketch-in-new-workplane "
- "group.");
- break;
+ MessageAndRun([] {
+ // Align the view with the selected workplane
+ SS.GW.ClearSuper();
+ SS.GW.AnimateOntoWorkplane();
+ }, _("No workplane selected. Activating default workplane "
+ "for this group."));
+ } else {
+ Error(_("No workplane is selected, and the active group does "
+ "not have a default workplane. Try selecting a "
+ "workplane, or activating a sketch-in-new-workplane "
+ "group."));
+ //update checkboxes in the menus
+ SS.GW.EnsureValidActives();
}
- // Align the view with the selected workplane
- SS.GW.AnimateOntoWorkplane();
- SS.GW.ClearSuper();
- SS.ScheduleShowTW();
break;
}
- case MNU_FREE_IN_3D:
+ case Command::FREE_IN_3D:
SS.GW.SetWorkplaneFreeIn3d();
SS.GW.EnsureValidActives();
SS.ScheduleShowTW();
- InvalidateGraphics();
+ SS.GW.Invalidate();
break;
- case MNU_TANGENT_ARC:
+ case Command::TANGENT_ARC:
SS.GW.GroupSelection();
if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
SS.GW.MakeTangentArc();
} else if(SS.GW.gs.n != 0) {
- Error("Bad selection for tangent arc at point. Select a "
- "single point, or select nothing to set up arc "
- "parameters.");
+ Error(_("Bad selection for tangent arc at point. Select a "
+ "single point, or select nothing to set up arc "
+ "parameters."));
} else {
- SS.TW.GoToScreen(TextWindow::SCREEN_TANGENT_ARC);
+ SS.TW.GoToScreen(TextWindow::Screen::TANGENT_ARC);
SS.GW.ForceTextWindowShown();
SS.ScheduleShowTW();
- InvalidateGraphics(); // repaint toolbar
+ SS.GW.Invalidate(); // repaint toolbar
}
break;
- case MNU_ARC: s = "click point on arc (draws anti-clockwise)"; goto c;
- case MNU_DATUM_POINT: s = "click to place datum point"; goto c;
- case MNU_LINE_SEGMENT: s = "click first point of line segment"; goto c;
- case MNU_CONSTR_SEGMENT: s = "click first point of construction line segment"; goto c;
- case MNU_CUBIC: s = "click first point of cubic segment"; goto c;
- case MNU_CIRCLE: s = "click center of circle"; goto c;
- case MNU_WORKPLANE: s = "click origin of workplane"; goto c;
- case MNU_RECTANGLE: s = "click one corner of rectangle"; goto c;
- case MNU_TTF_TEXT: s = "click top left of text"; goto c;
+ case Command::ARC: s = _("click point on arc (draws anti-clockwise)"); goto c;
+ case Command::DATUM_POINT: s = _("click to place datum point"); goto c;
+ case Command::LINE_SEGMENT: s = _("click first point of line segment"); goto c;
+ case Command::CONSTR_SEGMENT:
+ s = _("click first point of construction line segment"); goto c;
+ case Command::CUBIC: s = _("click first point of cubic segment"); goto c;
+ case Command::CIRCLE: s = _("click center of circle"); goto c;
+ case Command::WORKPLANE: s = _("click origin of workplane"); goto c;
+ case Command::RECTANGLE: s = _("click one corner of rectangle"); goto c;
+ case Command::TTF_TEXT: s = _("click top left of text"); goto c;
+ case Command::IMAGE:
+ if(!SS.ReloadLinkedImage(SS.saveFile, &SS.GW.pending.filename,
+ /*canCancel=*/true)) {
+ return;
+ }
+ s = _("click top left of image"); goto c;
c:
- SS.GW.pending.operation = id;
+ SS.GW.pending.operation = GraphicsWindow::Pending::COMMAND;
+ SS.GW.pending.command = id;
SS.GW.pending.description = s;
SS.ScheduleShowTW();
- InvalidateGraphics(); // repaint toolbar
+ SS.GW.Invalidate(); // repaint toolbar
break;
- case MNU_CONSTRUCTION: {
- SS.UndoRemember();
+ case Command::CONSTRUCTION: {
SS.GW.GroupSelection();
if(SS.GW.gs.entities == 0) {
- Error("No entities are selected. Select entities before "
- "trying to toggle their construction state.");
+ Error(_("No entities are selected. Select entities before "
+ "trying to toggle their construction state."));
+ break;
}
+ SS.UndoRemember();
int i;
for(i = 0; i < SS.GW.gs.entities; i++) {
hEntity he = SS.GW.gs.entity[i];
SS.MarkGroupDirty(r->group);
}
SS.GW.ClearSelection();
- SS.GenerateAll();
break;
}
- case MNU_SPLIT_CURVES:
+ case Command::SPLIT_CURVES:
SS.GW.SplitLinesOrCurves();
break;
- default: oops();
+ default: ssassert(false, "Unexpected menu ID");
}
}
-void GraphicsWindow::ClearSuper(void) {
- HideGraphicsEditControl();
+void GraphicsWindow::ClearSuper() {
+ if(window) window->HideEditor();
ClearPending();
ClearSelection();
hover.Clear();
// so not meaningful to show them and hide the shaded.
if(!showShaded) showFaces = false;
- // We might need to regenerate the mesh and edge list, since the edges
- // wouldn't have been generated if they were previously hidden.
- if(showEdges) (SK.GetGroup(activeGroup))->displayDirty = true;
+ // If the mesh or edges were previously hidden, they haven't been generated,
+ // and if we are going to show them, we need to generate them first.
+ Group *g = SK.GetGroup(SS.GW.activeGroup);
+ if(*v && (g->displayOutlines.l.IsEmpty() && (v == &showEdges || v == &showOutlines))) {
+ SS.GenerateAll(SolveSpaceUI::Generate::UNTIL_ACTIVE);
+ }
- SS.GenerateAll();
- InvalidateGraphics();
+ Invalidate(/*clearPersistent=*/true);
SS.ScheduleShowTW();
}
-GraphicsWindow::SuggestedConstraint GraphicsWindow::SuggestLineConstraint(hRequest request) {
- if(LockedInWorkplane()) {
- Entity *ptA = SK.GetEntity(request.entity(1)),
- *ptB = SK.GetEntity(request.entity(2));
+bool GraphicsWindow::SuggestLineConstraint(hRequest request, Constraint::Type *type) {
+ if(!(LockedInWorkplane() && SS.automaticLineConstraints))
+ return false;
- Expr *au, *av, *bu, *bv;
+ Entity *ptA = SK.GetEntity(request.entity(1)),
+ *ptB = SK.GetEntity(request.entity(2));
- ptA->PointGetExprsInWorkplane(ActiveWorkplane(), &au, &av);
- ptB->PointGetExprsInWorkplane(ActiveWorkplane(), &bu, &bv);
+ Expr *au, *av, *bu, *bv;
- double du = au->Minus(bu)->Eval();
- double dv = av->Minus(bv)->Eval();
+ ptA->PointGetExprsInWorkplane(ActiveWorkplane(), &au, &av);
+ ptB->PointGetExprsInWorkplane(ActiveWorkplane(), &bu, &bv);
- const double TOLERANCE_RATIO = 0.02;
- if(fabs(dv) > LENGTH_EPS && fabs(du / dv) < TOLERANCE_RATIO)
- return SUGGESTED_VERTICAL;
- else if(fabs(du) > LENGTH_EPS && fabs(dv / du) < TOLERANCE_RATIO)
- return SUGGESTED_HORIZONTAL;
- else
- return SUGGESTED_NONE;
- } else {
- return SUGGESTED_NONE;
+ double du = au->Minus(bu)->Eval();
+ double dv = av->Minus(bv)->Eval();
+
+ const double TOLERANCE_RATIO = 0.02;
+ if(fabs(dv) > LENGTH_EPS && fabs(du / dv) < TOLERANCE_RATIO) {
+ *type = Constraint::Type::VERTICAL;
+ return true;
+ } else if(fabs(du) > LENGTH_EPS && fabs(dv / du) < TOLERANCE_RATIO) {
+ *type = Constraint::Type::HORIZONTAL;
+ return true;
}
+ return false;
}
const hGroup Group::HGROUP_REFERENCES = { 1 };
-#define gs (SS.GW.gs)
-
//-----------------------------------------------------------------------------
// The group structure includes pointers to other dynamically-allocated
// memory. This clears and frees them all.
//-----------------------------------------------------------------------------
-void Group::Clear(void) {
+void Group::Clear() {
polyLoops.Clear();
bezierLoops.Clear();
bezierOpens.Clear();
thisShell.Clear();
runningShell.Clear();
displayMesh.Clear();
- displayEdges.Clear();
displayOutlines.Clear();
impMesh.Clear();
impShell.Clear();
impEntity.Clear();
// remap is the only one that doesn't get recreated when we regen
- remap.Clear();
+ remap.clear();
}
void Group::AddParam(IdList<Param,hParam> *param, hParam hp, double v) {
param->Add(&pa);
}
-bool Group::IsVisible(void) {
+bool Group::IsVisible() {
if(!visible) return false;
- if(SS.GroupsInOrder(SS.GW.activeGroup, h)) return false;
+ Group *active = SK.GetGroup(SS.GW.activeGroup);
+ if(order > active->order) return false;
return true;
}
-int Group::GetNumConstraints(void) {
- int num = 0;
- for(int i = 0; i < SK.constraint.n; i++) {
- Constraint *c = &SK.constraint.elem[i];
- if(c->group.v != h.v) continue;
- num++;
- }
- return num;
+size_t Group::GetNumConstraints() {
+ return std::count_if(SK.constraint.begin(), SK.constraint.end(),
+ [&](Constraint const &c) { return c.group == h; });
}
Vector Group::ExtrusionGetVector() {
SK.GetParam(h.param(2))->val = v.z;
}
-void Group::MenuGroup(int id) {
+void Group::MenuGroup(Command id) {
+ MenuGroup(id, Platform::Path());
+}
+
+void Group::MenuGroup(Command id, Platform::Path linkFile) {
+ Platform::SettingsRef settings = Platform::GetSettings();
+
Group g = {};
g.visible = true;
g.color = RGBi(100, 100, 100);
g.scale = 1;
-
- if(id >= RECENT_LINK && id < (RECENT_LINK + MAX_RECENT)) {
- g.linkFile = RecentFile[id-RECENT_LINK];
- id = GraphicsWindow::MNU_GROUP_LINK;
- }
+ g.linkFile = linkFile;
SS.GW.GroupSelection();
+ auto const &gs = SS.GW.gs;
switch(id) {
- case GraphicsWindow::MNU_GROUP_3D:
- g.type = DRAWING_3D;
- g.name = "sketch-in-3d";
+ case Command::GROUP_3D:
+ g.type = Type::DRAWING_3D;
+ g.name = C_("group-name", "sketch-in-3d");
break;
- case GraphicsWindow::MNU_GROUP_WRKPL:
- g.type = DRAWING_WORKPLANE;
- g.name = "sketch-in-plane";
+ case Command::GROUP_WRKPL:
+ g.type = Type::DRAWING_WORKPLANE;
+ g.name = C_("group-name", "sketch-in-plane");
if(gs.points == 1 && gs.n == 1) {
- g.subtype = WORKPLANE_BY_POINT_ORTHO;
+ g.subtype = Subtype::WORKPLANE_BY_POINT_ORTHO;
Vector u = SS.GW.projRight, v = SS.GW.projUp;
u = u.ClosestOrtho();
g.predef.q = Quaternion::From(u, v);
g.predef.origin = gs.point[0];
} else if(gs.points == 1 && gs.lineSegments == 2 && gs.n == 3) {
- g.subtype = WORKPLANE_BY_LINE_SEGMENTS;
+ g.subtype = Subtype::WORKPLANE_BY_LINE_SEGMENTS;
g.predef.origin = gs.point[0];
g.predef.entityB = gs.entity[0];
}
if(SS.GW.projRight.Dot(ut) < 0) g.predef.negateU = true;
if(SS.GW.projUp. Dot(vt) < 0) g.predef.negateV = true;
+ } else if(gs.workplanes == 1 && gs.n == 1) {
+ if(gs.entity[0].isFromRequest()) {
+ Entity *wrkpl = SK.GetEntity(gs.entity[0]);
+ Entity *normal = SK.GetEntity(wrkpl->normal);
+ g.subtype = Subtype::WORKPLANE_BY_POINT_ORTHO;
+ g.predef.origin = wrkpl->point[0];
+ g.predef.q = normal->NormalGetNum();
+ } else {
+ Group *wrkplg = SK.GetGroup(gs.entity[0].group());
+ g.subtype = wrkplg->subtype;
+ g.predef.origin = wrkplg->predef.origin;
+ if(wrkplg->subtype == Subtype::WORKPLANE_BY_LINE_SEGMENTS) {
+ g.predef.entityB = wrkplg->predef.entityB;
+ g.predef.entityC = wrkplg->predef.entityC;
+ g.predef.swapUV = wrkplg->predef.swapUV;
+ g.predef.negateU = wrkplg->predef.negateU;
+ g.predef.negateV = wrkplg->predef.negateV;
+ } else if(wrkplg->subtype == Subtype::WORKPLANE_BY_POINT_ORTHO) {
+ g.predef.q = wrkplg->predef.q;
+ } else ssassert(false, "Unexpected workplane subtype");
+ }
} else {
- Error("Bad selection for new sketch in workplane. This "
- "group can be created with:\n\n"
- " * a point (orthogonal to coordinate axes, "
- "through the point)\n"
- " * a point and two line segments (parallel to the "
- "lines, through the point)\n");
+ Error(_("Bad selection for new sketch in workplane. This "
+ "group can be created with:\n\n"
+ " * a point (through the point, orthogonal to coordinate axes)\n"
+ " * a point and two line segments (through the point, "
+ "parallel to the lines)\n"
+ " * a workplane (copy of the workplane)\n"));
return;
}
break;
- case GraphicsWindow::MNU_GROUP_EXTRUDE:
+ case Command::GROUP_EXTRUDE:
if(!SS.GW.LockedInWorkplane()) {
- Error("Select a workplane (Sketch -> In Workplane) before "
- "extruding. The sketch will be extruded normal to the "
- "workplane.");
+ Error(_("Activate a workplane (Sketch -> In Workplane) before "
+ "extruding. The sketch will be extruded normal to the "
+ "workplane."));
return;
}
- g.type = EXTRUDE;
+ g.type = Type::EXTRUDE;
g.opA = SS.GW.activeGroup;
g.predef.entityB = SS.GW.ActiveWorkplane();
- g.subtype = ONE_SIDED;
- g.name = "extrude";
+ g.subtype = Subtype::ONE_SIDED;
+ g.name = C_("group-name", "extrude");
break;
- case GraphicsWindow::MNU_GROUP_LATHE:
+ case Command::GROUP_LATHE:
+ if(!SS.GW.LockedInWorkplane()) {
+ Error(_("Lathe operation can only be applied to planar sketches."));
+ return;
+ }
if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) {
g.predef.origin = gs.point[0];
g.predef.entityB = gs.vector[0];
g.predef.entityB = gs.entity[0];
// since a line segment is a vector
} else {
- Error("Bad selection for new lathe group. This group can "
- "be created with:\n\n"
- " * a point and a line segment or normal "
- "(revolved about an axis parallel to line / "
- "normal, through point)\n"
- " * a line segment (revolved about line segment)\n");
+ Error(_("Bad selection for new lathe group. This group can "
+ "be created with:\n\n"
+ " * a point and a line segment or normal "
+ "(revolved about an axis parallel to line / "
+ "normal, through point)\n"
+ " * a line segment (revolved about line segment)\n"));
return;
}
- g.type = LATHE;
+ g.type = Type::LATHE;
g.opA = SS.GW.activeGroup;
- g.name = "lathe";
+ g.name = C_("group-name", "lathe");
break;
- case GraphicsWindow::MNU_GROUP_ROT: {
+ case Command::GROUP_REVOLVE:
+ if(!SS.GW.LockedInWorkplane()) {
+ Error(_("Revolve operation can only be applied to planar sketches."));
+ return;
+ }
+ if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) {
+ g.predef.origin = gs.point[0];
+ g.predef.entityB = gs.vector[0];
+ } else if(gs.lineSegments == 1 && gs.n == 1) {
+ g.predef.origin = SK.GetEntity(gs.entity[0])->point[0];
+ g.predef.entityB = gs.entity[0];
+ // since a line segment is a vector
+ } else {
+ Error(_("Bad selection for new revolve group. This group can "
+ "be created with:\n\n"
+ " * a point and a line segment or normal "
+ "(revolved about an axis parallel to line / "
+ "normal, through point)\n"
+ " * a line segment (revolved about line segment)\n"));
+ return;
+ }
+ g.type = Type::REVOLVE;
+ g.opA = SS.GW.activeGroup;
+ g.valA = 2;
+ g.subtype = Subtype::ONE_SIDED;
+ g.name = C_("group-name", "revolve");
+ break;
+
+ case Command::GROUP_HELIX:
+ if(!SS.GW.LockedInWorkplane()) {
+ Error(_("Helix operation can only be applied to planar sketches."));
+ return;
+ }
+ if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) {
+ g.predef.origin = gs.point[0];
+ g.predef.entityB = gs.vector[0];
+ } else if(gs.lineSegments == 1 && gs.n == 1) {
+ g.predef.origin = SK.GetEntity(gs.entity[0])->point[0];
+ g.predef.entityB = gs.entity[0];
+ // since a line segment is a vector
+ } else {
+ Error(_("Bad selection for new helix group. This group can "
+ "be created with:\n\n"
+ " * a point and a line segment or normal "
+ "(revolved about an axis parallel to line / "
+ "normal, through point)\n"
+ " * a line segment (revolved about line segment)\n"));
+ return;
+ }
+ g.type = Type::HELIX;
+ g.opA = SS.GW.activeGroup;
+ g.valA = 2;
+ g.subtype = Subtype::ONE_SIDED;
+ g.name = C_("group-name", "helix");
+ break;
+
+ case Command::GROUP_ROT: {
if(gs.points == 1 && gs.n == 1 && SS.GW.LockedInWorkplane()) {
g.predef.origin = gs.point[0];
Entity *w = SK.GetEntity(SS.GW.ActiveWorkplane());
g.predef.origin = gs.point[0];
g.predef.entityB = gs.vector[0];
} else {
- Error("Bad selection for new rotation. This group can "
- "be created with:\n\n"
- " * a point, while locked in workplane (rotate "
- "in plane, about that point)\n"
- " * a point and a line or a normal (rotate about "
- "an axis through the point, and parallel to "
- "line / normal)\n");
+ Error(_("Bad selection for new rotation. This group can "
+ "be created with:\n\n"
+ " * a point, while locked in workplane (rotate "
+ "in plane, about that point)\n"
+ " * a point and a line or a normal (rotate about "
+ "an axis through the point, and parallel to "
+ "line / normal)\n"));
return;
}
- g.type = ROTATE;
+ g.type = Type::ROTATE;
g.opA = SS.GW.activeGroup;
g.valA = 3;
- g.subtype = ONE_SIDED;
- g.name = "rotate";
+ g.subtype = Subtype::ONE_SIDED;
+ g.name = C_("group-name", "rotate");
break;
}
- case GraphicsWindow::MNU_GROUP_TRANS:
- g.type = TRANSLATE;
+ case Command::GROUP_TRANS:
+ g.type = Type::TRANSLATE;
g.opA = SS.GW.activeGroup;
g.valA = 3;
- g.subtype = ONE_SIDED;
+ g.subtype = Subtype::ONE_SIDED;
g.predef.entityB = SS.GW.ActiveWorkplane();
g.activeWorkplane = SS.GW.ActiveWorkplane();
- g.name = "translate";
+ g.name = C_("group-name", "translate");
break;
- case GraphicsWindow::MNU_GROUP_LINK: {
- g.type = LINKED;
- if(g.linkFile.empty()) {
- if(!GetOpenFile(&g.linkFile, "", SlvsFileFilter)) return;
+ case Command::GROUP_LINK: {
+ g.type = Type::LINKED;
+ g.meshCombine = CombineAs::ASSEMBLE;
+ if(g.linkFile.IsEmpty()) {
+ Platform::FileDialogRef dialog = Platform::CreateOpenFileDialog(SS.GW.window);
+ dialog->AddFilters(Platform::SolveSpaceLinkFileFilters);
+ dialog->ThawChoices(settings, "LinkSketch");
+ if(!dialog->RunModal()) return;
+ dialog->FreezeChoices(settings, "LinkSketch");
+ g.linkFile = dialog->GetFilename();
}
// Assign the default name of the group based on the name of
// the linked file.
- std::string groupName = g.linkFile;
- size_t pos;
-
- pos = groupName.rfind(PATH_SEP);
- if(pos != std::string::npos)
- groupName.erase(0, pos + 1);
-
- pos = groupName.rfind('.');
- if(pos != std::string::npos)
- groupName.erase(pos);
-
- for(size_t i = 0; i < groupName.length(); i++) {
- if(!(isalnum(groupName[i]) || (unsigned)groupName[i] >= 0x80)) {
+ g.name = g.linkFile.FileStem();
+ for(size_t i = 0; i < g.name.length(); i++) {
+ if(!(isalnum(g.name[i]) || (unsigned)g.name[i] >= 0x80)) {
// convert punctuation to dashes
- groupName[i] = '-';
+ g.name[i] = '-';
}
}
-
- if(groupName.length() > 0) {
- g.name = groupName;
- } else {
- g.name = "link";
- }
-
- g.meshCombine = COMBINE_AS_ASSEMBLE;
break;
}
- default: oops();
+ default: ssassert(false, "Unexpected menu ID");
}
// Copy color from the previous mesh-contributing group.
- if(g.IsMeshGroup() && SK.groupOrder.n > 0) {
+ if(g.IsMeshGroup() && !SK.groupOrder.IsEmpty()) {
Group *running = SK.GetRunningMeshGroupFor(SS.GW.activeGroup);
if(running != NULL) {
g.color = running->color;
SS.UndoRemember();
bool afterActive = false;
- for(int i = 0; i < SK.groupOrder.n; i++) {
- Group *gi = SK.GetGroup(SK.groupOrder.elem[i]);
+ for(hGroup hg : SK.groupOrder) {
+ Group *gi = SK.GetGroup(hg);
if(afterActive)
gi->order += 1;
- if(gi->h.v == SS.GW.activeGroup.v) {
+ if(gi->h == SS.GW.activeGroup) {
g.order = gi->order + 1;
afterActive = true;
}
SK.group.AddAndAssignId(&g);
Group *gg = SK.GetGroup(g.h);
- if(gg->type == LINKED) {
- SS.ReloadAllImported();
+ if(gg->type == Type::LINKED) {
+ SS.ReloadAllLinked(SS.saveFile);
}
gg->clean = false;
SS.GW.activeGroup = gg->h;
SS.GenerateAll();
- if(gg->type == DRAWING_WORKPLANE) {
+ if(gg->type == Type::DRAWING_WORKPLANE) {
// Can't set the active workplane for this one until after we've
// regenerated, because the workplane doesn't exist until then.
gg->activeWorkplane = gg->h.entity(0);
}
gg->Activate();
- SS.GW.AnimateOntoWorkplane();
TextWindow::ScreenSelectGroup(0, gg->h.v);
- SS.ScheduleShowTW();
+ SS.GW.AnimateOntoWorkplane();
}
void Group::TransformImportedBy(Vector t, Quaternion q) {
- if(type != LINKED) oops();
+ ssassert(type == Type::LINKED, "Expected a linked group");
hParam tx, ty, tz, qw, qx, qy, qz;
tx = h.param(0);
SK.GetParam(qz)->val = qg.vz;
}
-std::string Group::DescriptionString(void) {
+bool Group::IsForcedToMeshBySource() const {
+ const Group *srcg = this;
+ if(type == Type::TRANSLATE || type == Type::ROTATE) {
+ // A step and repeat gets merged against the group's previous group,
+ // not our own previous group.
+ srcg = SK.GetGroup(opA);
+ if(srcg->forceToMesh) return true;
+ }
+ Group *g = srcg->RunningMeshGroup();
+ if(g == NULL) return false;
+ return g->forceToMesh || g->IsForcedToMeshBySource();
+}
+
+bool Group::IsForcedToMesh() const {
+ return forceToMesh || IsForcedToMeshBySource();
+}
+
+std::string Group::DescriptionString() {
if(name.empty()) {
- return ssprintf("g%03x-(unnamed)", h.v);
+ return ssprintf("g%03x-%s", h.v, _("(unnamed)"));
} else {
return ssprintf("g%03x-%s", h.v, name.c_str());
}
}
-void Group::Activate(void) {
- if(type == EXTRUDE || type == LINKED || type == LATHE || type == TRANSLATE || type == ROTATE) {
+void Group::Activate() {
+ if(type == Type::EXTRUDE || type == Type::LINKED || type == Type::LATHE ||
+ type == Type::REVOLVE || type == Type::HELIX || type == Type::TRANSLATE || type == Type::ROTATE) {
SS.GW.showFaces = true;
} else {
SS.GW.showFaces = false;
}
SS.MarkGroupDirty(h); // for good measure; shouldn't be needed
- SS.ScheduleGenerateAll();
SS.ScheduleShowTW();
}
gp = gp.WithMagnitude(200/SS.GW.scale);
int a, i;
switch(type) {
- case DRAWING_3D:
- break;
+ case Type::DRAWING_3D:
+ return;
- case DRAWING_WORKPLANE: {
+ case Type::DRAWING_WORKPLANE: {
Quaternion q;
- if(subtype == WORKPLANE_BY_LINE_SEGMENTS) {
+ if(subtype == Subtype::WORKPLANE_BY_LINE_SEGMENTS) {
Vector u = SK.GetEntity(predef.entityB)->VectorGetNum();
Vector v = SK.GetEntity(predef.entityC)->VectorGetNum();
u = u.WithMagnitude(1);
if(predef.negateU) u = u.ScaledBy(-1);
if(predef.negateV) v = v.ScaledBy(-1);
q = Quaternion::From(u, v);
- } else if(subtype == WORKPLANE_BY_POINT_ORTHO) {
+ } else if(subtype == Subtype::WORKPLANE_BY_POINT_ORTHO) {
// Already given, numerically.
q = predef.q;
- } else oops();
+ } else ssassert(false, "Unexpected workplane subtype");
Entity normal = {};
- normal.type = Entity::NORMAL_N_COPY;
+ normal.type = Entity::Type::NORMAL_N_COPY;
normal.numNormal = q;
normal.point[0] = h.entity(2);
normal.group = h;
entity->Add(&normal);
Entity point = {};
- point.type = Entity::POINT_N_COPY;
+ point.type = Entity::Type::POINT_N_COPY;
point.numPoint = SK.GetEntity(predef.origin)->PointGetNum();
+ point.construction = true;
point.group = h;
point.h = h.entity(2);
entity->Add(&point);
Entity wp = {};
- wp.type = Entity::WORKPLANE;
+ wp.type = Entity::Type::WORKPLANE;
wp.normal = normal.h;
wp.point[0] = point.h;
wp.group = h;
wp.h = h.entity(0);
entity->Add(&wp);
- break;
+ return;
}
- case EXTRUDE: {
+ case Type::EXTRUDE: {
AddParam(param, h.param(0), gn.x);
AddParam(param, h.param(1), gn.y);
AddParam(param, h.param(2), gn.z);
int ai, af;
- if(subtype == ONE_SIDED) {
+ if(subtype == Subtype::ONE_SIDED) {
ai = 0; af = 2;
- } else if(subtype == TWO_SIDED) {
+ } else if(subtype == Subtype::TWO_SIDED) {
ai = -1; af = 1;
- } else oops();
+ } else ssassert(false, "Unexpected extrusion subtype");
// Get some arbitrary point in the sketch, that will be used
// as a reference when defining top and bottom faces.
hEntity pt = { 0 };
+ // Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) {
- Entity *e = &(entity->elem[i]);
- if(e->group.v != opA.v) continue;
+ Entity *e = &(entity->Get(i));
+ if(e->group != opA) continue;
if(e->IsPoint()) pt = e->h;
- e->CalculateNumerical(false);
+ e->CalculateNumerical(/*forExport=*/false);
hEntity he = e->h; e = NULL;
// As soon as I call CopyEntity, e may become invalid! That
// adds entities, which may cause a realloc.
CopyEntity(entity, SK.GetEntity(he), ai, REMAP_BOTTOM,
h.param(0), h.param(1), h.param(2),
- NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
- true, false);
+ NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
+ CopyAs::N_TRANS);
CopyEntity(entity, SK.GetEntity(he), af, REMAP_TOP,
h.param(0), h.param(1), h.param(2),
- NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
- true, false);
+ NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
+ CopyAs::N_TRANS);
MakeExtrusionLines(entity, he);
}
// Remapped versions of that arbitrary point will be used to
// provide points on the plane faces.
MakeExtrusionTopBottomFaces(entity, pt);
- break;
+ return;
}
- case LATHE: {
+ case Type::LATHE: {
Vector axis_pos = SK.GetEntity(predef.origin)->PointGetNum();
Vector axis_dir = SK.GetEntity(predef.entityB)->VectorGetNum();
- AddParam(param, h.param(0), axis_dir.x);
- AddParam(param, h.param(1), axis_dir.y);
- AddParam(param, h.param(2), axis_dir.z);
-
- // Remapped entity index.
- int ai = 1;
-
+ // Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) {
- Entity *e = &(entity->elem[i]);
- if(e->group.v != opA.v) continue;
+ Entity *e = &(entity->Get(i));
+ if(e->group != opA) continue;
- e->CalculateNumerical(false);
+ e->CalculateNumerical(/*forExport=*/false);
hEntity he = e->h;
// As soon as I call CopyEntity, e may become invalid! That
// adds entities, which may cause a realloc.
- CopyEntity(entity, SK.GetEntity(predef.origin), 0, ai,
- h.param(0), h.param(1), h.param(2),
- NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
- true, false);
+ // this is the regular copy of all entities
CopyEntity(entity, SK.GetEntity(he), 0, REMAP_LATHE_START,
- h.param(0), h.param(1), h.param(2),
- NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
- true, false);
+ NO_PARAM, NO_PARAM, NO_PARAM,
+ NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
+ CopyAs::NUMERIC);
+
+ e = &(entity->Get(i)); // because we copied.
+ if (e->IsPoint()) {
+ // for points this copy is used for the circle centers
+ CopyEntity(entity, SK.GetEntity(he), 0, REMAP_LATHE_ARC_CENTER,
+ NO_PARAM, NO_PARAM, NO_PARAM,
+ NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
+ CopyAs::NUMERIC);
+ MakeLatheCircles(entity, param, he, axis_pos, axis_dir);
+ };
+ MakeLatheSurfacesSelectable(entity, he, axis_dir);
+ }
+ return;
+ }
- CopyEntity(entity, SK.GetEntity(he), 0, REMAP_LATHE_END,
- h.param(0), h.param(1), h.param(2),
- NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
- true, false);
+ case Type::REVOLVE: {
+ // this was borrowed from LATHE and ROTATE
+ Vector axis_pos = SK.GetEntity(predef.origin)->PointGetNum();
+ Vector axis_dir = SK.GetEntity(predef.entityB)->VectorGetNum();
+
+ // The center of rotation
+ AddParam(param, h.param(0), axis_pos.x);
+ AddParam(param, h.param(1), axis_pos.y);
+ AddParam(param, h.param(2), axis_pos.z);
+ // The rotation quaternion
+ AddParam(param, h.param(3), 30 * PI / 180);
+ AddParam(param, h.param(4), axis_dir.x);
+ AddParam(param, h.param(5), axis_dir.y);
+ AddParam(param, h.param(6), axis_dir.z);
+
+ // Get some arbitrary point in the sketch, that will be used
+ // as a reference when defining end faces.
+ hEntity pt = { 0 };
- MakeLatheCircles(entity, param, he, axis_pos, axis_dir, ai);
- ai++;
+ int ai = 0, af = 2;
+ if (subtype == Subtype::TWO_SIDED)
+ {
+ ai = -1;
+ af = 1;
}
- break;
+ // Not using range-for here because we're changing the size of entity in the loop.
+ for(i = 0; i < entity->n; i++) {
+ Entity *e = &(entity->Get(i));
+ if(e->group != opA)
+ continue;
+
+ if(e->IsPoint()) pt = e->h;
+
+ e->CalculateNumerical(/*forExport=*/false);
+ hEntity he = e->h;
+ // one copy for each end of the revolved surface
+ CopyEntity(entity, e, ai, REMAP_LATHE_START, h.param(0),
+ h.param(1), h.param(2), h.param(3), h.param(4), h.param(5),
+ h.param(6), NO_PARAM, CopyAs::N_ROT_AA);
+
+ e = &(entity->Get(i)); // because we copied.
+ CopyEntity(entity, e, af, REMAP_LATHE_END, h.param(0),
+ h.param(1), h.param(2), h.param(3), h.param(4), h.param(5),
+ h.param(6), NO_PARAM, CopyAs::N_ROT_AA);
+
+ // Arcs are not generated for revolve groups, for now, because our current arc
+ // entity is not chiral, and dragging a revolve may break the arc by inverting it.
+ // MakeLatheCircles(entity, param, he, axis_pos, axis_dir);
+ MakeLatheSurfacesSelectable(entity, he, axis_dir);
+ }
+ MakeRevolveEndFaces(entity, pt, ai, af);
+ return;
}
- case TRANSLATE: {
+ case Type::HELIX: {
+ Vector axis_pos = SK.GetEntity(predef.origin)->PointGetNum();
+ Vector axis_dir = SK.GetEntity(predef.entityB)->VectorGetNum();
+
+ // The center of rotation
+ AddParam(param, h.param(0), axis_pos.x);
+ AddParam(param, h.param(1), axis_pos.y);
+ AddParam(param, h.param(2), axis_pos.z);
+ // The rotation quaternion
+ AddParam(param, h.param(3), 30 * PI / 180);
+ AddParam(param, h.param(4), axis_dir.x);
+ AddParam(param, h.param(5), axis_dir.y);
+ AddParam(param, h.param(6), axis_dir.z);
+ // distance to translate along the rotation axis
+ AddParam(param, h.param(7), 20);
+
+ // Get some arbitrary point in the sketch, that will be used
+ // as a reference when defining end faces.
+ hEntity pt = { 0 };
+
+ int ai = 0, af = 2; // initial and final number of transformations
+ if (subtype != Subtype::ONE_SIDED)
+ {
+ ai = -1;
+ af = 1;
+ }
+
+ // Not using range-for here because we're changing the size of entity in the loop.
+ for(i = 0; i < entity->n; i++) {
+ Entity *e = &(entity->Get(i));
+ if((e->group.v != opA.v) && !(e->h == predef.origin))
+ continue;
+
+ if(e->IsPoint()) pt = e->h;
+
+ e->CalculateNumerical(/*forExport=*/false);
+
+ // one copy for each end of the helix
+ CopyEntity(entity, e, ai, REMAP_LATHE_START, h.param(0),
+ h.param(1), h.param(2), h.param(3), h.param(4), h.param(5),
+ h.param(6), h.param(7), CopyAs::N_ROT_AXIS_TRANS);
+
+ e = &(entity->Get(i)); // because we copied.
+ CopyEntity(entity, e, af, REMAP_LATHE_END, h.param(0),
+ h.param(1), h.param(2), h.param(3), h.param(4), h.param(5),
+ h.param(6), h.param(7), CopyAs::N_ROT_AXIS_TRANS);
+
+ // For point entities on the axis, create a construction line
+ e = &(entity->Get(i));
+ if(e->IsPoint()) {
+ Vector check = e->PointGetNum().Minus(axis_pos).Cross(axis_dir);
+ if (check.Dot(check) < LENGTH_EPS) {
+ //! @todo isn't this the same as &(ent[i])?
+ Entity *ep = SK.GetEntity(e->h);
+ Entity en = {};
+ // A point gets extruded to form a line segment
+ en.point[0] = Remap(ep->h, REMAP_LATHE_START);
+ en.point[1] = Remap(ep->h, REMAP_LATHE_END);
+ en.group = h;
+ en.construction = ep->construction;
+ en.style = ep->style;
+ en.h = Remap(ep->h, REMAP_PT_TO_LINE);
+ en.type = Entity::Type::LINE_SEGMENT;
+ entity->Add(&en);
+ }
+ }
+ }
+ MakeRevolveEndFaces(entity, pt, ai, af);
+ return;
+ }
+
+ case Type::TRANSLATE: {
+ // inherit meshCombine from source group
+ Group *srcg = SK.GetGroup(opA);
+ meshCombine = srcg->meshCombine;
// The translation vector
AddParam(param, h.param(0), gp.x);
AddParam(param, h.param(1), gp.y);
AddParam(param, h.param(2), gp.z);
int n = (int)valA, a0 = 0;
- if(subtype == ONE_SIDED && skipFirst) {
+ if(subtype == Subtype::ONE_SIDED && skipFirst) {
a0++; n++;
}
for(a = a0; a < n; a++) {
+ // Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) {
- Entity *e = &(entity->elem[i]);
- if(e->group.v != opA.v) continue;
+ Entity *e = &(entity->Get(i));
+ if(e->group != opA) continue;
- e->CalculateNumerical(false);
+ e->CalculateNumerical(/*forExport=*/false);
CopyEntity(entity, e,
- a*2 - (subtype == ONE_SIDED ? 0 : (n-1)),
+ a*2 - (subtype == Subtype::ONE_SIDED ? 0 : (n-1)),
(a == (n - 1)) ? REMAP_LAST : a,
h.param(0), h.param(1), h.param(2),
- NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
- true, false);
+ NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
+ CopyAs::N_TRANS);
}
}
- break;
+ return;
}
- case ROTATE: {
+ case Type::ROTATE: {
+ // inherit meshCombine from source group
+ Group *srcg = SK.GetGroup(opA);
+ meshCombine = srcg->meshCombine;
// The center of rotation
AddParam(param, h.param(0), gc.x);
AddParam(param, h.param(1), gc.y);
AddParam(param, h.param(6), gn.z);
int n = (int)valA, a0 = 0;
- if(subtype == ONE_SIDED && skipFirst) {
+ if(subtype == Subtype::ONE_SIDED && skipFirst) {
a0++; n++;
}
for(a = a0; a < n; a++) {
+ // Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) {
- Entity *e = &(entity->elem[i]);
- if(e->group.v != opA.v) continue;
+ Entity *e = &(entity->Get(i));
+ if(e->group != opA) continue;
- e->CalculateNumerical(false);
+ e->CalculateNumerical(/*forExport=*/false);
CopyEntity(entity, e,
- a*2 - (subtype == ONE_SIDED ? 0 : (n-1)),
+ a*2 - (subtype == Subtype::ONE_SIDED ? 0 : (n-1)),
(a == (n - 1)) ? REMAP_LAST : a,
h.param(0), h.param(1), h.param(2),
- h.param(3), h.param(4), h.param(5), h.param(6),
- false, true);
+ h.param(3), h.param(4), h.param(5), h.param(6), NO_PARAM,
+ CopyAs::N_ROT_AA);
}
}
- break;
+ return;
}
- case LINKED:
+ case Type::LINKED:
// The translation vector
AddParam(param, h.param(0), gp.x);
AddParam(param, h.param(1), gp.y);
AddParam(param, h.param(5), 0);
AddParam(param, h.param(6), 0);
+ // Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < impEntity.n; i++) {
- Entity *ie = &(impEntity.elem[i]);
+ Entity *ie = &(impEntity[i]);
CopyEntity(entity, ie, 0, 0,
h.param(0), h.param(1), h.param(2),
- h.param(3), h.param(4), h.param(5), h.param(6),
- false, false);
+ h.param(3), h.param(4), h.param(5), h.param(6), NO_PARAM,
+ CopyAs::N_ROT_TRANS);
}
- break;
-
- default: oops();
+ return;
}
+ ssassert(false, "Unexpected group type");
}
bool Group::IsSolvedOkay() {
- return this->solved.how == System::SOLVED_OKAY ||
- (this->allowRedundant && this->solved.how == System::REDUNDANT_OKAY);
+ return this->solved.how == SolveResult::OKAY ||
+ (this->allowRedundant && this->solved.how == SolveResult::REDUNDANT_OKAY);
}
void Group::AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index) {
}
void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
- if(type == LINKED) {
+ if(type == Type::LINKED) {
// Normalize the quaternion
ExprQuaternion q = {
Expr::From(h.param(3)),
Expr::From(h.param(5)),
Expr::From(h.param(6)) };
AddEq(l, (q.Magnitude())->Minus(Expr::From(1)), 0);
- } else if(type == ROTATE) {
+ } else if(type == Type::ROTATE || type == Type::REVOLVE || type == Type::HELIX) {
// The axis and center of rotation are specified numerically
#define EC(x) (Expr::From(x))
#define EP(x) (Expr::From(h.param(x)))
AddEq(l, (EC(axis.z))->Minus(EP(6)), 5);
#undef EC
#undef EP
- } else if(type == EXTRUDE) {
- if(predef.entityB.v != Entity::FREE_IN_3D.v) {
+ } else if(type == Type::EXTRUDE) {
+ if(predef.entityB != Entity::FREE_IN_3D) {
// The extrusion path is locked along a line, normal to the
// specified workplane.
Entity *w = SK.GetEntity(predef.entityB);
AddEq(l, u.Dot(extruden), 0);
AddEq(l, v.Dot(extruden), 1);
}
- } else if(type == TRANSLATE) {
- if(predef.entityB.v != Entity::FREE_IN_3D.v) {
+ } else if(type == Type::TRANSLATE) {
+ if(predef.entityB != Entity::FREE_IN_3D) {
Entity *w = SK.GetEntity(predef.entityB);
ExprVector n = w->Normal()->NormalExprsN();
ExprVector trans;
}
hEntity Group::Remap(hEntity in, int copyNumber) {
- // A hash table is used to accelerate the search
- int hash = ((unsigned)(in.v*61 + copyNumber)) % REMAP_PRIME;
- int i = remapCache[hash];
- if(i >= 0 && i < remap.n) {
- EntityMap *em = &(remap.elem[i]);
- if(em->input.v == in.v && em->copyNumber == copyNumber) {
- return h.entity(em->h.v);
- }
- }
- // but if we don't find it in the hash table, then linear search
- for(i = 0; i < remap.n; i++) {
- EntityMap *em = &(remap.elem[i]);
- if(em->input.v == in.v && em->copyNumber == copyNumber) {
- // We already have a mapping for this entity.
- remapCache[hash] = i;
- return h.entity(em->h.v);
- }
+ auto it = remap.find({ in, copyNumber });
+ if(it == remap.end()) {
+ std::tie(it, std::ignore) =
+ remap.insert({ { in, copyNumber }, { (uint32_t)remap.size() + 1 } });
}
- // And if we still don't find it, then create a new entry.
- EntityMap em;
- em.input = in;
- em.copyNumber = copyNumber;
- remap.AddAndAssignId(&em);
- return h.entity(em.h.v);
+ return h.entity(it->second.v);
}
void Group::MakeExtrusionLines(IdList<Entity,hEntity> *el, hEntity in) {
en.construction = ep->construction;
en.style = ep->style;
en.h = Remap(ep->h, REMAP_PT_TO_LINE);
- en.type = Entity::LINE_SEGMENT;
+ en.type = Entity::Type::LINE_SEGMENT;
el->Add(&en);
- } else if(ep->type == Entity::LINE_SEGMENT) {
+ } else if(ep->type == Entity::Type::LINE_SEGMENT) {
// A line gets extruded to form a plane face; an endpoint of the
// original line is a point in the plane, and the line is in the plane.
Vector a = SK.GetEntity(ep->point[0])->PointGetNum();
en.construction = ep->construction;
en.style = ep->style;
en.h = Remap(ep->h, REMAP_LINE_TO_FACE);
- en.type = Entity::FACE_XPROD;
+ en.type = Entity::Type::FACE_XPROD;
el->Add(&en);
}
}
-void Group::MakeLatheCircles(IdList<Entity,hEntity> *el, IdList<Param,hParam> *param, hEntity in, Vector pt, Vector axis, int ai) {
+void Group::MakeLatheCircles(IdList<Entity,hEntity> *el, IdList<Param,hParam> *param, hEntity in, Vector pt, Vector axis) {
Entity *ep = SK.GetEntity(in);
Entity en = {};
if(ep->IsPoint()) {
// A point gets revolved to form an arc.
- en.point[0] = Remap(predef.origin, ai);
+ en.point[0] = Remap(ep->h, REMAP_LATHE_ARC_CENTER);
en.point[1] = Remap(ep->h, REMAP_LATHE_START);
- en.point[2] = Remap(ep->h, REMAP_LATHE_END);
+ en.point[2] = en.point[1]; //Remap(ep->h, REMAP_LATHE_END);
// Get arc center and point on arc.
Entity *pc = SK.GetEntity(en.point[0]);
en.construction = ep->construction;
en.style = ep->style;
en.h = Remap(ep->h, REMAP_PT_TO_ARC);
- en.type = Entity::ARC_OF_CIRCLE;
+ en.type = Entity::Type::ARC_OF_CIRCLE;
// Generate a normal.
Entity n = {};
n.h = Remap(ep->h, REMAP_PT_TO_NORMAL);
n.group = en.group;
n.style = en.style;
- n.type = Entity::NORMAL_N_COPY;
+ n.type = Entity::Type::NORMAL_N_COPY;
// Create basis for the normal.
Vector nu = pp->numPoint.Minus(pc->numPoint).WithMagnitude(1.0);
el->Add(&n);
en.normal = n.h;
el->Add(&en);
- } else if(ep->type == Entity::LINE_SEGMENT) {
+ }
+}
+
+void Group::MakeLatheSurfacesSelectable(IdList<Entity, hEntity> *el, hEntity in, Vector axis) {
+ Entity *ep = SK.GetEntity(in);
+
+ Entity en = {};
+ if(ep->type == Entity::Type::LINE_SEGMENT) {
// An axis-perpendicular line gets revolved to form a face.
Vector a = SK.GetEntity(ep->point[0])->PointGetNum();
Vector b = SK.GetEntity(ep->point[1])->PointGetNum();
en.construction = ep->construction;
en.style = ep->style;
en.h = Remap(ep->h, REMAP_LINE_TO_FACE);
- en.type = Entity::FACE_NORMAL_PT;
+ en.type = Entity::Type::FACE_NORMAL_PT;
en.point[0] = ep->point[0];
el->Add(&en);
}
}
}
+// For Revolve and Helix groups the end faces are remapped from an arbitrary
+// point on the sketch. We reference the transformed point but there is
+// no existing normal so we need to define the rotation and timesApplied.
+void Group::MakeRevolveEndFaces(IdList<Entity,hEntity> *el, hEntity pt, int ai, int af)
+{
+ if(pt.v == 0) return;
+ Group *src = SK.GetGroup(opA);
+ Vector n = src->polyLoops.normal;
+
+ // When there is no loop normal (e.g. if the loop is broken), use normal of workplane
+ // as fallback, to avoid breaking constraints depending on the faces.
+ if(n.Equals(Vector::From(0.0, 0.0, 0.0)) && src->type == Group::Type::DRAWING_WORKPLANE) {
+ n = SK.GetEntity(src->h.entity(0))->Normal()->NormalN();
+ }
+
+ Entity en = {};
+ en.type = Entity::Type::FACE_ROT_NORMAL_PT;
+ en.group = h;
+ // The center of rotation
+ en.param[0] = h.param(0);
+ en.param[1] = h.param(1);
+ en.param[2] = h.param(2);
+ // The rotation quaternion
+ en.param[3] = h.param(3);
+ en.param[4] = h.param(4);
+ en.param[5] = h.param(5);
+ en.param[6] = h.param(6);
+
+ en.numNormal = Quaternion::From(0, n.x, n.y, n.z);
+ en.point[0] = Remap(pt, REMAP_LATHE_START);
+ en.timesApplied = ai;
+ en.h = Remap(Entity::NO_ENTITY, REMAP_LATHE_START);
+ el->Add(&en);
+
+ en.point[0] = Remap(pt, REMAP_LATHE_END);
+ en.timesApplied = af;
+ en.h = Remap(Entity::NO_ENTITY, REMAP_LATHE_END);
+ el->Add(&en);
+}
+
void Group::MakeExtrusionTopBottomFaces(IdList<Entity,hEntity> *el, hEntity pt)
{
if(pt.v == 0) return;
Group *src = SK.GetGroup(opA);
Vector n = src->polyLoops.normal;
+ // When there is no loop normal (e.g. if the loop is broken), use normal of workplane
+ // as fallback, to avoid breaking constraints depending on the faces.
+ if(n.Equals(Vector::From(0.0, 0.0, 0.0)) && src->type == Group::Type::DRAWING_WORKPLANE) {
+ n = SK.GetEntity(src->h.entity(0))->Normal()->NormalN();
+ }
+
Entity en = {};
- en.type = Entity::FACE_NORMAL_PT;
+ en.type = Entity::Type::FACE_NORMAL_PT;
en.group = h;
en.numNormal = Quaternion::From(0, n.x, n.y, n.z);
void Group::CopyEntity(IdList<Entity,hEntity> *el,
Entity *ep, int timesApplied, int remap,
hParam dx, hParam dy, hParam dz,
- hParam qw, hParam qvx, hParam qvy, hParam qvz,
- bool asTrans, bool asAxisAngle)
+ hParam qw, hParam qvx, hParam qvy, hParam qvz, hParam dist,
+ CopyAs as)
{
Entity en = {};
en.type = ep->type;
en.style = ep->style;
en.str = ep->str;
en.font = ep->font;
+ en.file = ep->file;
switch(ep->type) {
- case Entity::WORKPLANE:
+ case Entity::Type::WORKPLANE:
// Don't copy these.
return;
- case Entity::POINT_N_COPY:
- case Entity::POINT_N_TRANS:
- case Entity::POINT_N_ROT_TRANS:
- case Entity::POINT_N_ROT_AA:
- case Entity::POINT_IN_3D:
- case Entity::POINT_IN_2D:
- if(asTrans) {
- en.type = Entity::POINT_N_TRANS;
+ case Entity::Type::POINT_N_COPY:
+ case Entity::Type::POINT_N_TRANS:
+ case Entity::Type::POINT_N_ROT_TRANS:
+ case Entity::Type::POINT_N_ROT_AA:
+ case Entity::Type::POINT_N_ROT_AXIS_TRANS:
+ case Entity::Type::POINT_IN_3D:
+ case Entity::Type::POINT_IN_2D:
+ if(as == CopyAs::N_TRANS) {
+ en.type = Entity::Type::POINT_N_TRANS;
en.param[0] = dx;
en.param[1] = dy;
en.param[2] = dz;
+ } else if(as == CopyAs::NUMERIC) {
+ en.type = Entity::Type::POINT_N_COPY;
} else {
- if(asAxisAngle) {
- en.type = Entity::POINT_N_ROT_AA;
+ if(as == CopyAs::N_ROT_AA) {
+ en.type = Entity::Type::POINT_N_ROT_AA;
+ } else if (as == CopyAs::N_ROT_AXIS_TRANS) {
+ en.type = Entity::Type::POINT_N_ROT_AXIS_TRANS;
} else {
- en.type = Entity::POINT_N_ROT_TRANS;
+ en.type = Entity::Type::POINT_N_ROT_TRANS;
}
en.param[0] = dx;
en.param[1] = dy;
en.param[4] = qvx;
en.param[5] = qvy;
en.param[6] = qvz;
+ if (as == CopyAs::N_ROT_AXIS_TRANS) {
+ en.param[7] = dist;
+ }
}
en.numPoint = (ep->actPoint).ScaledBy(scale);
break;
- case Entity::NORMAL_N_COPY:
- case Entity::NORMAL_N_ROT:
- case Entity::NORMAL_N_ROT_AA:
- case Entity::NORMAL_IN_3D:
- case Entity::NORMAL_IN_2D:
- if(asTrans) {
- en.type = Entity::NORMAL_N_COPY;
- } else {
- if(asAxisAngle) {
- en.type = Entity::NORMAL_N_ROT_AA;
+ case Entity::Type::NORMAL_N_COPY:
+ case Entity::Type::NORMAL_N_ROT:
+ case Entity::Type::NORMAL_N_ROT_AA:
+ case Entity::Type::NORMAL_IN_3D:
+ case Entity::Type::NORMAL_IN_2D:
+ if(as == CopyAs::N_TRANS || as == CopyAs::NUMERIC) {
+ en.type = Entity::Type::NORMAL_N_COPY;
+ } else { // N_ROT_AXIS_TRANS probably doesn't warrant a new entity Type
+ if(as == CopyAs::N_ROT_AA || as == CopyAs::N_ROT_AXIS_TRANS) {
+ en.type = Entity::Type::NORMAL_N_ROT_AA;
} else {
- en.type = Entity::NORMAL_N_ROT;
+ en.type = Entity::Type::NORMAL_N_ROT;
}
en.param[0] = qw;
en.param[1] = qvx;
en.point[0] = Remap(ep->point[0], remap);
break;
- case Entity::DISTANCE_N_COPY:
- case Entity::DISTANCE:
- en.type = Entity::DISTANCE_N_COPY;
+ case Entity::Type::DISTANCE_N_COPY:
+ case Entity::Type::DISTANCE:
+ en.type = Entity::Type::DISTANCE_N_COPY;
en.numDistance = ep->actDistance*fabs(scale);
break;
- case Entity::FACE_NORMAL_PT:
- case Entity::FACE_XPROD:
- case Entity::FACE_N_ROT_TRANS:
- case Entity::FACE_N_TRANS:
- case Entity::FACE_N_ROT_AA:
- if(asTrans) {
- en.type = Entity::FACE_N_TRANS;
+ case Entity::Type::FACE_NORMAL_PT:
+ case Entity::Type::FACE_XPROD:
+ case Entity::Type::FACE_N_ROT_TRANS:
+ case Entity::Type::FACE_N_TRANS:
+ case Entity::Type::FACE_N_ROT_AA:
+ case Entity::Type::FACE_ROT_NORMAL_PT:
+ case Entity::Type::FACE_N_ROT_AXIS_TRANS:
+ if(as == CopyAs::N_TRANS) {
+ en.type = Entity::Type::FACE_N_TRANS;
+ en.param[0] = dx;
+ en.param[1] = dy;
+ en.param[2] = dz;
+ } else if (as == CopyAs::NUMERIC) {
+ en.type = Entity::Type::FACE_NORMAL_PT;
+ } else if (as == CopyAs::N_ROT_AXIS_TRANS) {
+ en.type = Entity::Type::FACE_N_ROT_AXIS_TRANS;
en.param[0] = dx;
en.param[1] = dy;
en.param[2] = dz;
+ en.param[3] = qw;
+ en.param[4] = qvx;
+ en.param[5] = qvy;
+ en.param[6] = qvz;
+ en.param[7] = dist;
} else {
- if(asAxisAngle) {
- en.type = Entity::FACE_N_ROT_AA;
+ if(as == CopyAs::N_ROT_AA) {
+ en.type = Entity::Type::FACE_N_ROT_AA;
} else {
- en.type = Entity::FACE_N_ROT_TRANS;
+ en.type = Entity::Type::FACE_N_ROT_TRANS;
}
en.param[0] = dx;
en.param[1] = dy;
//-----------------------------------------------------------------------------
#include "solvespace.h"
-#define gs (SS.GW.gs)
-
void Group::AssembleLoops(bool *allClosed,
bool *allCoplanar,
bool *allNonZeroLen)
SBezierList sbl = {};
int i;
- for(i = 0; i < SK.entity.n; i++) {
- Entity *e = &(SK.entity.elem[i]);
- if(e->group.v != h.v) continue;
- if(e->construction) continue;
- if(e->forceHidden) continue;
-
- e->GenerateBezierCurves(&sbl);
+ for(auto &e : SK.entity) {
+ if(e.group != h)
+ continue;
+ if(e.construction)
+ continue;
+ if(e.forceHidden)
+ continue;
+
+ e.GenerateBezierCurves(&sbl);
}
SBezier *sb;
sbl.Clear();
}
-void Group::GenerateLoops(void) {
+void Group::GenerateLoops() {
polyLoops.Clear();
bezierLoops.Clear();
bezierOpens.Clear();
- if(type == DRAWING_3D || type == DRAWING_WORKPLANE ||
- type == ROTATE || type == TRANSLATE || type == LINKED)
+ if(type == Type::DRAWING_3D || type == Type::DRAWING_WORKPLANE ||
+ type == Type::ROTATE || type == Type::TRANSLATE || type == Type::LINKED)
{
bool allClosed = false, allCoplanar = false, allNonZeroLen = false;
AssembleLoops(&allClosed, &allCoplanar, &allNonZeroLen);
if(!allNonZeroLen) {
- polyError.how = POLY_ZERO_LEN_EDGE;
+ polyError.how = PolyError::ZERO_LEN_EDGE;
} else if(!allCoplanar) {
- polyError.how = POLY_NOT_COPLANAR;
+ polyError.how = PolyError::NOT_COPLANAR;
} else if(!allClosed) {
- polyError.how = POLY_NOT_CLOSED;
+ polyError.how = PolyError::NOT_CLOSED;
} else {
- polyError.how = POLY_GOOD;
+ polyError.how = PolyError::GOOD;
// The self-intersecting check is kind of slow, so don't run it
// unless requested.
if(SS.checkClosedContour) {
if(polyLoops.SelfIntersecting(&(polyError.errorPointAt))) {
- polyError.how = POLY_SELF_INTERSECTING;
+ polyError.how = PolyError::SELF_INTERSECTING;
}
}
}
SSurface *ss;
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)){
hEntity face = { ss->face };
- if(face.v == Entity::NO_ENTITY.v) continue;
+ if(face == Entity::NO_ENTITY) continue;
face = g->Remap(face, remap);
ss->face = face.v;
STriangle *tr;
for(tr = l.First(); tr; tr = l.NextAfter(tr)) {
hEntity face = { tr->meta.face };
- if(face.v == Entity::NO_ENTITY.v) continue;
+ if(face == Entity::NO_ENTITY) continue;
face = g->Remap(face, remap);
tr->meta.face = face.v;
}
template<class T>
-void Group::GenerateForStepAndRepeat(T *steps, T *outs) {
- T workA, workB;
- workA = {};
- workB = {};
- T *soFar = &workA, *scratch = &workB;
+void Group::GenerateForStepAndRepeat(T *steps, T *outs, Group::CombineAs forWhat) {
int n = (int)valA, a0 = 0;
- if(subtype == ONE_SIDED && skipFirst) {
+ if(subtype == Subtype::ONE_SIDED && skipFirst) {
a0++; n++;
}
+
int a;
+ // create all the transformed copies
+ std::vector <T> transd(n);
+ std::vector <T> workA(n);
+ workA[0] = {};
+ // first generate a shell/mesh with each transformed copy
+#pragma omp parallel for
for(a = a0; a < n; a++) {
- int ap = a*2 - (subtype == ONE_SIDED ? 0 : (n-1));
- int remap = (a == (n - 1)) ? REMAP_LAST : a;
+ transd[a] = {};
+ workA[a] = {};
+ int ap = a*2 - (subtype == Subtype::ONE_SIDED ? 0 : (n-1));
- T transd = {};
- if(type == TRANSLATE) {
+ if(type == Type::TRANSLATE) {
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
trans = trans.ScaledBy(ap);
- transd.MakeFromTransformationOf(steps,
+ transd[a].MakeFromTransformationOf(steps,
trans, Quaternion::IDENTITY, 1.0);
} else {
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
Vector axis = Vector::From(h.param(4), h.param(5), h.param(6));
Quaternion q = Quaternion::From(c, s*axis.x, s*axis.y, s*axis.z);
// Rotation is centered at t; so A(x - t) + t = Ax + (t - At)
- transd.MakeFromTransformationOf(steps,
+ transd[a].MakeFromTransformationOf(steps,
trans.Minus(q.Rotate(trans)), q, 1.0);
}
-
+ }
+ for(a = a0; a < n; a++) {
// We need to rewrite any plane face entities to the transformed ones.
- transd.RemapFaces(this, remap);
+ int remap = (a == (n - 1)) ? REMAP_LAST : a;
+ transd[a].RemapFaces(this, remap);
+ }
- // And tack this transformed copy on to the return.
- if(soFar->IsEmpty()) {
- scratch->MakeFromCopyOf(&transd);
- } else {
- scratch->MakeFromUnionOf(soFar, &transd);
+ std::vector<T> *soFar = &transd;
+ std::vector<T> *scratch = &workA;
+ // do the boolean operations on pairs of equal size
+ while(n > 1) {
+ for(a = 0; a < n; a+=2) {
+ scratch->at(a/2).Clear();
+ // combine a pair of shells
+ if((a==0) && (a0==1)) { // if the first was skipped just copy the 2nd
+ scratch->at(a/2).MakeFromCopyOf(&(soFar->at(a+1)));
+ (soFar->at(a+1)).Clear();
+ a0 = 0;
+ } else if (a == n-1) { // for an odd number just copy the last one
+ scratch->at(a/2).MakeFromCopyOf(&(soFar->at(a)));
+ (soFar->at(a)).Clear();
+ } else if(forWhat == CombineAs::ASSEMBLE) {
+ scratch->at(a/2).MakeFromAssemblyOf(&(soFar->at(a)), &(soFar->at(a+1)));
+ (soFar->at(a)).Clear();
+ (soFar->at(a+1)).Clear();
+ } else {
+ scratch->at(a/2).MakeFromUnionOf(&(soFar->at(a)), &(soFar->at(a+1)));
+ (soFar->at(a)).Clear();
+ (soFar->at(a+1)).Clear();
+ }
}
-
swap(scratch, soFar);
- scratch->Clear();
- transd.Clear();
+ n = (n+1)/2;
}
-
outs->Clear();
- *outs = *soFar;
+ *outs = soFar->at(0);
}
template<class T>
-void Group::GenerateForBoolean(T *prevs, T *thiss, T *outs, int how) {
+void Group::GenerateForBoolean(T *prevs, T *thiss, T *outs, Group::CombineAs how) {
// If this group contributes no new mesh, then our running mesh is the
// same as last time, no combining required. Likewise if we have a mesh
// but it's suppressed.
// So our group's shell appears in thisShell. Combine this with the
// previous group's shell, using the requested operation.
- if(how == COMBINE_AS_UNION) {
- outs->MakeFromUnionOf(prevs, thiss);
- } else if(how == COMBINE_AS_DIFFERENCE) {
- outs->MakeFromDifferenceOf(prevs, thiss);
- } else {
- outs->MakeFromAssemblyOf(prevs, thiss);
+ switch(how) {
+ case CombineAs::UNION:
+ outs->MakeFromUnionOf(prevs, thiss);
+ break;
+
+ case CombineAs::DIFFERENCE:
+ outs->MakeFromDifferenceOf(prevs, thiss);
+ break;
+
+ case CombineAs::INTERSECTION:
+ outs->MakeFromIntersectionOf(prevs, thiss);
+ break;
+
+ case CombineAs::ASSEMBLE:
+ outs->MakeFromAssemblyOf(prevs, thiss);
+ break;
}
}
-void Group::GenerateShellAndMesh(void) {
+void Group::GenerateShellAndMesh() {
bool prevBooleanFailed = booleanFailed;
booleanFailed = false;
// Don't attempt a lathe or extrusion unless the source section is good:
// planar and not self-intersecting.
bool haveSrc = true;
- if(type == EXTRUDE || type == LATHE) {
+ if(type == Type::EXTRUDE || type == Type::LATHE || type == Type::REVOLVE) {
Group *src = SK.GetGroup(opA);
- if(src->polyError.how != POLY_GOOD) {
+ if(src->polyError.how != PolyError::GOOD) {
haveSrc = false;
}
}
- if(type == TRANSLATE || type == ROTATE) {
- // A step and repeat gets merged against the group's prevous group,
+ if(type == Type::TRANSLATE || type == Type::ROTATE) {
+ // A step and repeat gets merged against the group's previous group,
// not our own previous group.
srcg = SK.GetGroup(opA);
if(!srcg->suppress) {
- GenerateForStepAndRepeat<SShell>(&(srcg->thisShell), &thisShell);
- GenerateForStepAndRepeat<SMesh> (&(srcg->thisMesh), &thisMesh);
+ if(!IsForcedToMesh()) {
+ GenerateForStepAndRepeat<SShell>(&(srcg->thisShell), &thisShell, srcg->meshCombine);
+ } else {
+ SMesh prevm = {};
+ prevm.MakeFromCopyOf(&srcg->thisMesh);
+ srcg->thisShell.TriangulateInto(&prevm);
+ GenerateForStepAndRepeat<SMesh> (&prevm, &thisMesh, srcg->meshCombine);
+ }
}
- } else if(type == EXTRUDE && haveSrc) {
+ } else if(type == Type::EXTRUDE && haveSrc) {
Group *src = SK.GetGroup(opA);
Vector translate = Vector::From(h.param(0), h.param(1), h.param(2));
Vector tbot, ttop;
- if(subtype == ONE_SIDED) {
+ if(subtype == Subtype::ONE_SIDED) {
tbot = Vector::From(0, 0, 0); ttop = translate.ScaledBy(2);
} else {
tbot = translate.ScaledBy(-1); ttop = translate.ScaledBy(1);
// that face, so that the user can select them with the mouse.
Vector onOrig = sbls->point;
int i;
+ // Not using range-for here because we're starting at a different place and using
+ // indices for meaning.
for(i = is; i < thisShell.surface.n; i++) {
- SSurface *ss = &(thisShell.surface.elem[i]);
+ SSurface *ss = &(thisShell.surface[i]);
hEntity face = Entity::NO_ENTITY;
Vector p = ss->PointAt(0, 0),
Entity *e;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
- if(e->group.v != opA.v) continue;
- if(e->type != Entity::LINE_SEGMENT) continue;
+ if(e->group != opA) continue;
+ if(e->type != Entity::Type::LINE_SEGMENT) continue;
Vector a = SK.GetEntity(e->point[0])->PointGetNum(),
b = SK.GetEntity(e->point[1])->PointGetNum();
}
}
}
- } else if(type == LATHE && haveSrc) {
+ } else if(type == Type::LATHE && haveSrc) {
Group *src = SK.GetGroup(opA);
Vector pt = SK.GetEntity(predef.origin)->PointGetNum(),
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
thisShell.MakeFromRevolutionOf(sbls, pt, axis, color, this);
}
- } else if(type == LINKED) {
+ } else if(type == Type::REVOLVE && haveSrc) {
+ Group *src = SK.GetGroup(opA);
+ double anglef = SK.GetParam(h.param(3))->val * 4; // why the 4 is needed?
+ double dists = 0, distf = 0;
+ double angles = 0.0;
+ if(subtype != Subtype::ONE_SIDED) {
+ anglef *= 0.5;
+ angles = -anglef;
+ }
+ Vector pt = SK.GetEntity(predef.origin)->PointGetNum(),
+ axis = SK.GetEntity(predef.entityB)->VectorGetNum();
+ axis = axis.WithMagnitude(1);
+
+ SBezierLoopSetSet *sblss = &(src->bezierLoops);
+ SBezierLoopSet *sbls;
+ for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
+ if(fabs(anglef - angles) < 2 * PI) {
+ thisShell.MakeFromHelicalRevolutionOf(sbls, pt, axis, color, this,
+ angles, anglef, dists, distf);
+ } else {
+ thisShell.MakeFromRevolutionOf(sbls, pt, axis, color, this);
+ }
+ }
+ } else if(type == Type::HELIX && haveSrc) {
+ Group *src = SK.GetGroup(opA);
+ double anglef = SK.GetParam(h.param(3))->val * 4; // why the 4 is needed?
+ double dists = 0, distf = 0;
+ double angles = 0.0;
+ distf = SK.GetParam(h.param(7))->val * 2; // dist is applied twice
+ if(subtype != Subtype::ONE_SIDED) {
+ anglef *= 0.5;
+ angles = -anglef;
+ distf *= 0.5;
+ dists = -distf;
+ }
+ Vector pt = SK.GetEntity(predef.origin)->PointGetNum(),
+ axis = SK.GetEntity(predef.entityB)->VectorGetNum();
+ axis = axis.WithMagnitude(1);
+
+ SBezierLoopSetSet *sblss = &(src->bezierLoops);
+ SBezierLoopSet *sbls;
+ for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
+ thisShell.MakeFromHelicalRevolutionOf(sbls, pt, axis, color, this,
+ angles, anglef, dists, distf);
+ }
+ } else if(type == Type::LINKED) {
// The imported shell or mesh are copied over, with the appropriate
// transformation applied. We also must remap the face entities.
Vector offset = {
thisShell.RemapFaces(this, 0);
}
- if(srcg->meshCombine != COMBINE_AS_ASSEMBLE) {
+ if(srcg->meshCombine != CombineAs::ASSEMBLE) {
thisShell.MergeCoincidentSurfaces();
}
Group *prevg = srcg->RunningMeshGroup();
- if(prevg->runningMesh.IsEmpty() && thisMesh.IsEmpty() && !forceToMesh) {
+ if(!IsForcedToMesh()) {
SShell *prevs = &(prevg->runningShell);
GenerateForBoolean<SShell>(prevs, &thisShell, &runningShell,
srcg->meshCombine);
- if(srcg->meshCombine != COMBINE_AS_ASSEMBLE) {
+ if(srcg->meshCombine != CombineAs::ASSEMBLE) {
runningShell.MergeCoincidentSurfaces();
}
SMesh outm = {};
GenerateForBoolean<SMesh>(&prevm, &thism, &outm, srcg->meshCombine);
- // And make sure that the output mesh is vertex-to-vertex.
- SKdNode *root = SKdNode::From(&outm);
- root->SnapToMesh(&outm);
- root->MakeMeshInto(&runningMesh);
+ // Remove degenerate triangles; if we don't, they'll get split in SnapToMesh
+ // in every generated group, resulting in polynomial increase in triangle count,
+ // and corresponding slowdown.
+ outm.RemoveDegenerateTriangles();
+
+ if(srcg->meshCombine != CombineAs::ASSEMBLE) {
+ // And make sure that the output mesh is vertex-to-vertex.
+ SKdNode *root = SKdNode::From(&outm);
+ root->SnapToMesh(&outm);
+ root->MakeMeshInto(&runningMesh);
+ } else {
+ runningMesh.MakeFromCopyOf(&outm);
+ }
outm.Clear();
thism.Clear();
displayDirty = true;
}
-void Group::GenerateDisplayItems(void) {
+void Group::GenerateDisplayItems() {
// This is potentially slow (since we've got to triangulate a shell, or
// to find the emphasized edges for a mesh), so we will run it only
// if its inputs have changed.
displayMesh.Clear();
displayMesh.MakeFromCopyOf(&(pg->displayMesh));
- displayEdges.Clear();
displayOutlines.Clear();
- if(SS.GW.showEdges) {
- SEdge *se;
- SEdgeList *src = &(pg->displayEdges);
- for(se = src->l.First(); se; se = src->l.NextAfter(se)) {
- displayEdges.l.Add(se);
- }
+ if(SS.GW.showEdges || SS.GW.showOutlines) {
displayOutlines.MakeFromCopyOf(&pg->displayOutlines);
}
} else {
displayMesh.AddTriangle(&trn);
}
- displayEdges.Clear();
displayOutlines.Clear();
- if(SS.GW.showEdges) {
- if(runningMesh.l.n > 0) {
+ if(SS.GW.showEdges || SS.GW.showOutlines) {
+ SOutlineList rawOutlines = {};
+ if(!runningMesh.l.IsEmpty()) {
// Triangle mesh only; no shell or emphasized edges.
- runningMesh.MakeCertainEdgesAndOutlinesInto(
- &displayEdges, &displayOutlines, SKdNode::EMPHASIZED_EDGES);
+ runningMesh.MakeOutlinesInto(&rawOutlines, EdgeKind::EMPHASIZED);
} else {
- displayMesh.MakeCertainEdgesAndOutlinesInto(
- &displayEdges, &displayOutlines, SKdNode::SHARP_EDGES);
+ displayMesh.MakeOutlinesInto(&rawOutlines, EdgeKind::SHARP);
}
+
+ PolylineBuilder builder;
+ builder.MakeFromOutlines(rawOutlines);
+ builder.GenerateOutlines(&displayOutlines);
+ rawOutlines.Clear();
}
}
+ // If we render this mesh, we need to know whether it's transparent,
+ // and we'll want all transparent triangles last, to make the depth test
+ // work correctly.
+ displayMesh.PrecomputeTransparency();
+
+ // Recalculate mass center if needed
+ if(SS.centerOfMass.draw && SS.centerOfMass.dirty && h == SS.GW.activeGroup) {
+ SS.UpdateCenterOfMass();
+ }
displayDirty = false;
}
}
-Group *Group::PreviousGroup(void) {
- int i;
- for(i = 0; i < SK.groupOrder.n; i++) {
- Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
- if(g->h.v == h.v) break;
+Group *Group::PreviousGroup() const {
+ Group *prev = nullptr;
+ for(auto const &gh : SK.groupOrder) {
+ Group *g = SK.GetGroup(gh);
+ if(g->h == h) {
+ return prev;
+ }
+ prev = g;
}
- if(i == 0 || i >= SK.groupOrder.n) return NULL;
- return SK.GetGroup(SK.groupOrder.elem[i - 1]);
+ return nullptr;
}
-Group *Group::RunningMeshGroup(void) {
- if(type == TRANSLATE || type == ROTATE) {
+Group *Group::RunningMeshGroup() const {
+ if(type == Type::TRANSLATE || type == Type::ROTATE) {
return SK.GetGroup(opA)->RunningMeshGroup();
} else {
return PreviousGroup();
bool Group::IsMeshGroup() {
switch(type) {
- case Group::EXTRUDE:
- case Group::LATHE:
- case Group::ROTATE:
- case Group::TRANSLATE:
+ case Group::Type::EXTRUDE:
+ case Group::Type::LATHE:
+ case Group::Type::REVOLVE:
+ case Group::Type::HELIX:
+ case Group::Type::ROTATE:
+ case Group::Type::TRANSLATE:
return true;
+
+ default:
+ return false;
}
- return false;
}
-void Group::DrawDisplayItems(int t) {
- RgbaColor specColor;
- bool useSpecColor;
- if(t == DRAWING_3D || t == DRAWING_WORKPLANE) {
- // force the color to something dim
- specColor = Style::Color(Style::DIM_SOLID);
- useSpecColor = true;
- } else {
- useSpecColor = false; // use the model color
- }
- // The back faces are drawn in red; should never seem them, since we
- // draw closed shells, so that's a debugging aid.
- GLfloat mpb[] = { 1.0f, 0.1f, 0.1f, 1.0f };
- glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, mpb);
-
- // When we fill the mesh, we need to know which triangles are selected
- // or hovered, in order to draw them differently.
- uint32_t mh = 0, ms1 = 0, ms2 = 0;
- hEntity he = SS.GW.hover.entity;
- if(he.v != 0 && SK.GetEntity(he)->IsFace()) {
- mh = he.v;
- }
- SS.GW.GroupSelection();
- if(gs.faces > 0) ms1 = gs.face[0].v;
- if(gs.faces > 1) ms2 = gs.face[1].v;
-
- if(SS.GW.showShaded || SS.GW.showHdnLines) {
- if(SS.drawBackFaces && !displayMesh.isTransparent) {
- // For debugging, draw the backs of the triangles in red, so that we
- // notice when a shell is open
- glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
- } else {
- glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
- }
+void Group::DrawMesh(DrawMeshAs how, Canvas *canvas) {
+ if(!(SS.GW.showShaded ||
+ SS.GW.drawOccludedAs != GraphicsWindow::DrawOccludedAs::VISIBLE)) return;
+
+ switch(how) {
+ case DrawMeshAs::DEFAULT: {
+ // Force the shade color to something dim to not distract from
+ // the sketch.
+ Canvas::Fill fillFront = {};
+ if(!SS.GW.showShaded) {
+ fillFront.layer = Canvas::Layer::DEPTH_ONLY;
+ }
+ if(type == Type::DRAWING_3D || type == Type::DRAWING_WORKPLANE) {
+ fillFront.color = Style::Color(Style::DIM_SOLID);
+ }
+ Canvas::hFill hcfFront = canvas->GetFill(fillFront);
+
+ // The back faces are drawn in red; should never seem them, since we
+ // draw closed shells, so that's a debugging aid.
+ Canvas::hFill hcfBack = {};
+ if(SS.drawBackFaces && !displayMesh.isTransparent) {
+ Canvas::Fill fillBack = {};
+ fillBack.layer = fillFront.layer;
+ fillBack.color = RgbaColor::FromFloat(1.0f, 0.1f, 0.1f);
+ hcfBack = canvas->GetFill(fillBack);
+ } else {
+ hcfBack = hcfFront;
+ }
- // Draw the shaded solid into the depth buffer for hidden line removal,
- // and if we're actually going to display it, to the color buffer too.
- glEnable(GL_LIGHTING);
- if(!SS.GW.showShaded) glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
- ssglFillMesh(useSpecColor, specColor, &displayMesh, mh, ms1, ms2);
- if(!SS.GW.showShaded) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- glDisable(GL_LIGHTING);
- }
+ // Draw the shaded solid into the depth buffer for hidden line removal,
+ // and if we're actually going to display it, to the color buffer too.
+ canvas->DrawMesh(displayMesh, hcfFront, hcfBack);
+
+ // Draw mesh edges, for debugging.
+ if(SS.GW.showMesh) {
+ Canvas::Stroke strokeTriangle = {};
+ strokeTriangle.zIndex = 1;
+ strokeTriangle.color = RgbaColor::FromFloat(0.0f, 1.0f, 0.0f);
+ strokeTriangle.width = 1;
+ strokeTriangle.unit = Canvas::Unit::PX;
+ Canvas::hStroke hcsTriangle = canvas->GetStroke(strokeTriangle);
+ SEdgeList edges = {};
+ for(const STriangle &t : displayMesh.l) {
+ edges.AddEdge(t.a, t.b);
+ edges.AddEdge(t.b, t.c);
+ edges.AddEdge(t.c, t.a);
+ }
+ canvas->DrawEdges(edges, hcsTriangle);
+ edges.Clear();
+ }
+ break;
+ }
- if(SS.GW.showEdges) {
- Vector projDir = SS.GW.projRight.Cross(SS.GW.projUp);
-
- glDepthMask(GL_FALSE);
- if(SS.GW.showHdnLines) {
- ssglDepthRangeOffset(0);
- glDepthFunc(GL_GREATER);
- ssglDrawEdges(&displayEdges, false, { Style::HIDDEN_EDGE });
- ssglDrawOutlines(&displayOutlines, projDir, { Style::HIDDEN_EDGE });
- glDepthFunc(GL_LEQUAL);
+ case DrawMeshAs::HOVERED: {
+ Canvas::Fill fill = {};
+ fill.color = Style::Color(Style::HOVERED);
+ fill.pattern = Canvas::FillPattern::CHECKERED_A;
+ fill.zIndex = 2;
+ Canvas::hFill hcf = canvas->GetFill(fill);
+
+ std::vector<uint32_t> faces;
+ hEntity he = SS.GW.hover.entity;
+ if(he.v != 0 && SK.GetEntity(he)->IsFace()) {
+ faces.push_back(he.v);
+ }
+ canvas->DrawFaces(displayMesh, faces, hcf);
+ break;
}
- ssglDepthRangeOffset(2);
- ssglDrawEdges(&displayEdges, false, { Style::SOLID_EDGE });
- if(SS.GW.showOutlines) {
- ssglDrawOutlines(&displayOutlines, projDir, { Style::OUTLINE });
- } else {
- ssglDrawOutlines(&displayOutlines, projDir, { Style::SOLID_EDGE });
+
+ case DrawMeshAs::SELECTED: {
+ Canvas::Fill fill = {};
+ fill.color = Style::Color(Style::SELECTED);
+ fill.pattern = Canvas::FillPattern::CHECKERED_B;
+ fill.zIndex = 1;
+ Canvas::hFill hcf = canvas->GetFill(fill);
+
+ std::vector<uint32_t> faces;
+ SS.GW.GroupSelection();
+ auto const &gs = SS.GW.gs;
+ if(gs.faces > 0) faces.push_back(gs.face[0].v);
+ if(gs.faces > 1) faces.push_back(gs.face[1].v);
+ canvas->DrawFaces(displayMesh, faces, hcf);
+ break;
}
- glDepthMask(GL_TRUE);
}
-
- if(SS.GW.showMesh) ssglDebugMesh(&displayMesh);
}
-void Group::Draw(void) {
+void Group::Draw(Canvas *canvas) {
// Everything here gets drawn whether or not the group is hidden; we
// can control this stuff independently, with show/hide solids, edges,
// mesh, etc.
GenerateDisplayItems();
- DrawDisplayItems(type);
+ DrawMesh(DrawMeshAs::DEFAULT, canvas);
+
+ if(SS.GW.showEdges) {
+ Canvas::Stroke strokeEdge = Style::Stroke(Style::SOLID_EDGE);
+ strokeEdge.zIndex = 1;
+ Canvas::hStroke hcsEdge = canvas->GetStroke(strokeEdge);
+
+ canvas->DrawOutlines(displayOutlines, hcsEdge,
+ SS.GW.showOutlines
+ ? Canvas::DrawOutlinesAs::EMPHASIZED_WITHOUT_CONTOUR
+ : Canvas::DrawOutlinesAs::EMPHASIZED_AND_CONTOUR);
+
+ if(SS.GW.drawOccludedAs != GraphicsWindow::DrawOccludedAs::INVISIBLE) {
+ Canvas::Stroke strokeHidden = Style::Stroke(Style::HIDDEN_EDGE);
+ if(SS.GW.drawOccludedAs == GraphicsWindow::DrawOccludedAs::VISIBLE) {
+ strokeHidden.stipplePattern = StipplePattern::CONTINUOUS;
+ }
+ strokeHidden.layer = Canvas::Layer::OCCLUDED;
+ Canvas::hStroke hcsHidden = canvas->GetStroke(strokeHidden);
+
+ canvas->DrawOutlines(displayOutlines, hcsHidden,
+ Canvas::DrawOutlinesAs::EMPHASIZED_AND_CONTOUR);
+ }
+ }
+
+ if(SS.GW.showOutlines) {
+ Canvas::Stroke strokeOutline = Style::Stroke(Style::OUTLINE);
+ strokeOutline.zIndex = 1;
+ Canvas::hStroke hcsOutline = canvas->GetStroke(strokeOutline);
+
+ canvas->DrawOutlines(displayOutlines, hcsOutline,
+ Canvas::DrawOutlinesAs::CONTOUR_ONLY);
+ }
+}
+
+void Group::DrawPolyError(Canvas *canvas) {
+ const Camera &camera = canvas->GetCamera();
+
+ Canvas::Stroke strokeUnclosed = Style::Stroke(Style::DRAW_ERROR);
+ strokeUnclosed.color = strokeUnclosed.color.WithAlpha(50);
+ Canvas::hStroke hcsUnclosed = canvas->GetStroke(strokeUnclosed);
- if(!SS.checkClosedContour) return;
+ Canvas::Stroke strokeError = Style::Stroke(Style::DRAW_ERROR);
+ strokeError.layer = Canvas::Layer::FRONT;
+ strokeError.width = 1.0f;
+ Canvas::hStroke hcsError = canvas->GetStroke(strokeError);
+
+ double textHeight = Style::DefaultTextHeight() / camera.scale;
// And finally show the polygons too, and any errors if it's not possible
// to assemble the lines into closed polygons.
- if(polyError.how == POLY_NOT_CLOSED) {
+ if(polyError.how == PolyError::NOT_CLOSED) {
// Report this error only in sketch-in-workplane groups; otherwise
// it's just a nuisance.
- if(type == DRAWING_WORKPLANE) {
- glDisable(GL_DEPTH_TEST);
- ssglColorRGBa(Style::Color(Style::DRAW_ERROR), 0.2);
- ssglLineWidth (Style::Width(Style::DRAW_ERROR));
- glBegin(GL_LINES);
- ssglVertex3v(polyError.notClosedAt.a);
- ssglVertex3v(polyError.notClosedAt.b);
- glEnd();
- ssglColorRGB(Style::Color(Style::DRAW_ERROR));
- ssglWriteText("not closed contour, or not all same style!",
- Style::DefaultTextHeight(),
- polyError.notClosedAt.b, SS.GW.projRight, SS.GW.projUp,
- NULL, NULL);
- glEnable(GL_DEPTH_TEST);
+ if(type == Type::DRAWING_WORKPLANE) {
+ canvas->DrawVectorText(_("not closed contour, or not all same style!"),
+ textHeight,
+ polyError.notClosedAt.b, camera.projRight, camera.projUp,
+ hcsError);
+ canvas->DrawLine(polyError.notClosedAt.a, polyError.notClosedAt.b, hcsUnclosed);
}
- } else if(polyError.how == POLY_NOT_COPLANAR ||
- polyError.how == POLY_SELF_INTERSECTING ||
- polyError.how == POLY_ZERO_LEN_EDGE)
- {
+ } else if(polyError.how == PolyError::NOT_COPLANAR ||
+ polyError.how == PolyError::SELF_INTERSECTING ||
+ polyError.how == PolyError::ZERO_LEN_EDGE) {
// These errors occur at points, not lines
- if(type == DRAWING_WORKPLANE) {
- glDisable(GL_DEPTH_TEST);
- ssglColorRGB(Style::Color(Style::DRAW_ERROR));
+ if(type == Type::DRAWING_WORKPLANE) {
const char *msg;
- if(polyError.how == POLY_NOT_COPLANAR) {
- msg = "points not all coplanar!";
- } else if(polyError.how == POLY_SELF_INTERSECTING) {
- msg = "contour is self-intersecting!";
+ if(polyError.how == PolyError::NOT_COPLANAR) {
+ msg = _("points not all coplanar!");
+ } else if(polyError.how == PolyError::SELF_INTERSECTING) {
+ msg = _("contour is self-intersecting!");
} else {
- msg = "zero-length edge!";
+ msg = _("zero-length edge!");
}
- ssglWriteText(msg, Style::DefaultTextHeight(),
- polyError.errorPointAt, SS.GW.projRight, SS.GW.projUp,
- NULL, NULL);
- glEnable(GL_DEPTH_TEST);
+ canvas->DrawVectorText(msg, textHeight,
+ polyError.errorPointAt, camera.projRight, camera.projUp,
+ hcsError);
}
} else {
// The contours will get filled in DrawFilledPaths.
}
}
-void Group::FillLoopSetAsPolygon(SBezierLoopSet *sbls) {
- SPolygon sp = {};
- sbls->MakePwlInto(&sp);
- ssglDepthRangeOffset(1);
- ssglFillPolygon(&sp);
- ssglDepthRangeOffset(0);
- sp.Clear();
-}
+void Group::DrawFilledPaths(Canvas *canvas) {
+ for(const SBezierLoopSet &sbls : bezierLoops.l) {
+ if(sbls.l.IsEmpty() || sbls.l[0].l.IsEmpty())
+ continue;
-void Group::DrawFilledPaths(void) {
- SBezierLoopSet *sbls;
- SBezierLoopSetSet *sblss = &bezierLoops;
- for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
- if(sbls->l.n == 0 || sbls->l.elem[0].l.n == 0) continue;
// In an assembled loop, all the styles should be the same; so doesn't
// matter which one we grab.
- SBezier *sb = &(sbls->l.elem[0].l.elem[0]);
- hStyle hs = { (uint32_t)sb->auxA };
- Style *s = Style::Get(hs);
+ const SBezier *sb = &(sbls.l[0].l[0]);
+ Style *s = Style::Get({ (uint32_t)sb->auxA });
+
+ Canvas::Fill fill = {};
+ fill.zIndex = 1;
if(s->filled) {
// This is a filled loop, where the user specified a fill color.
- ssglColorRGBa(s->fillColor, 1);
- FillLoopSetAsPolygon(sbls);
- } else {
- if(h.v == SS.GW.activeGroup.v && SS.checkClosedContour &&
- polyError.how == POLY_GOOD)
- {
- // If this is the active group, and we are supposed to check
- // for closed contours, and we do indeed have a closed and
- // non-intersecting contour, then fill it dimly.
- ssglColorRGBa(Style::Color(Style::CONTOUR_FILL), 0.5);
- ssglDepthRangeOffset(1);
- FillLoopSetAsPolygon(sbls);
- ssglDepthRangeOffset(0);
- }
- }
+ fill.color = s->fillColor;
+ } else if(h == SS.GW.activeGroup && SS.checkClosedContour &&
+ polyError.how == PolyError::GOOD) {
+ // If this is the active group, and we are supposed to check
+ // for closed contours, and we do indeed have a closed and
+ // non-intersecting contour, then fill it dimly.
+ fill.color = Style::Color(Style::CONTOUR_FILL).WithAlpha(127);
+ } else continue;
+ Canvas::hFill hcf = canvas->GetFill(fill);
+
+ SPolygon sp = {};
+ sbls.MakePwlInto(&sp);
+ canvas->DrawPolygon(sp, hcf);
+ sp.Clear();
+ }
+}
+
+void Group::DrawContourAreaLabels(Canvas *canvas) {
+ const Camera &camera = canvas->GetCamera();
+ Vector gr = camera.projRight.ScaledBy(1 / camera.scale);
+ Vector gu = camera.projUp.ScaledBy(1 / camera.scale);
+
+ for(SBezierLoopSet &sbls : bezierLoops.l) {
+ if(sbls.l.IsEmpty() || sbls.l[0].l.IsEmpty())
+ continue;
+
+ Vector min = sbls.l[0].l[0].ctrl[0];
+ Vector max = min;
+ Vector zero = Vector::From(0.0, 0.0, 0.0);
+ sbls.GetBoundingProjd(Vector::From(1.0, 0.0, 0.0), zero, &min.x, &max.x);
+ sbls.GetBoundingProjd(Vector::From(0.0, 1.0, 0.0), zero, &min.y, &max.y);
+ sbls.GetBoundingProjd(Vector::From(0.0, 0.0, 1.0), zero, &min.z, &max.z);
+
+ Vector mid = min.Plus(max).ScaledBy(0.5);
+
+ hStyle hs = { Style::CONSTRAINT };
+ Canvas::Stroke stroke = Style::Stroke(hs);
+ stroke.layer = Canvas::Layer::FRONT;
+
+ std::string label = SS.MmToStringSI(fabs(sbls.SignedArea()), /*dim=*/2);
+ double fontHeight = Style::TextHeight(hs);
+ double textWidth = VectorFont::Builtin()->GetWidth(fontHeight, label),
+ textHeight = VectorFont::Builtin()->GetCapHeight(fontHeight);
+ Vector pos = mid.Minus(gr.ScaledBy(textWidth / 2.0))
+ .Minus(gu.ScaledBy(textHeight / 2.0));
+ canvas->DrawVectorText(label, fontHeight, pos, gr, gu, canvas->GetStroke(stroke));
}
}
+++ /dev/null
-//-----------------------------------------------------------------------------
-// Our main() function, and GTK3-specific stuff to set up our windows and
-// otherwise handle our interface to the operating system. Everything
-// outside gtk/... should be standard C++ and OpenGL.
-//
-// Copyright 2015 <whitequark@whitequark.org>
-//-----------------------------------------------------------------------------
-#include <errno.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <time.h>
-
-#include <iostream>
-
-#include <json-c/json_object.h>
-#include <json-c/json_util.h>
-
-#include <glibmm/main.h>
-#include <glibmm/convert.h>
-#include <giomm/file.h>
-#include <gdkmm/cursor.h>
-#include <gtkmm/drawingarea.h>
-#include <gtkmm/scrollbar.h>
-#include <gtkmm/entry.h>
-#include <gtkmm/eventbox.h>
-#include <gtkmm/fixed.h>
-#include <gtkmm/adjustment.h>
-#include <gtkmm/separatormenuitem.h>
-#include <gtkmm/menuitem.h>
-#include <gtkmm/checkmenuitem.h>
-#include <gtkmm/radiomenuitem.h>
-#include <gtkmm/radiobuttongroup.h>
-#include <gtkmm/menu.h>
-#include <gtkmm/menubar.h>
-#include <gtkmm/scrolledwindow.h>
-#include <gtkmm/filechooserdialog.h>
-#include <gtkmm/messagedialog.h>
-#include <gtkmm/main.h>
-
-#if HAVE_GTK3
-#include <gtkmm/hvbox.h>
-#else
-#include <gtkmm/box.h>
-#endif
-
-#include <cairomm/xlib_surface.h>
-#include <pangomm/fontdescription.h>
-#include <gdk/gdkx.h>
-#include <fontconfig/fontconfig.h>
-
-#include <GL/glx.h>
-
-#include "solvespace.h"
-#include "config.h"
-#include "../unix/gloffscreen.h"
-
-#ifdef HAVE_SPACEWARE
-#include <spnav.h>
-#endif
-
-namespace SolveSpace {
-/* Settings */
-
-/* Why not just use GSettings? Two reasons. It doesn't allow to easily see
- whether the setting had the default value, and it requires to install
- a schema globally. */
-static json_object *settings = NULL;
-
-static std::string CnfPrepare() {
- // Refer to http://standards.freedesktop.org/basedir-spec/latest/
-
- std::string dir;
- if(getenv("XDG_CONFIG_HOME")) {
- dir = std::string(getenv("XDG_CONFIG_HOME")) + "/solvespace";
- } else if(getenv("HOME")) {
- dir = std::string(getenv("HOME")) + "/.config/solvespace";
- } else {
- dbp("neither XDG_CONFIG_HOME nor HOME are set");
- return "";
- }
-
- struct stat st;
- if(stat(dir.c_str(), &st)) {
- if(errno == ENOENT) {
- if(mkdir(dir.c_str(), 0777)) {
- dbp("cannot mkdir %s: %s", dir.c_str(), strerror(errno));
- return "";
- }
- } else {
- dbp("cannot stat %s: %s", dir.c_str(), strerror(errno));
- return "";
- }
- } else if(!S_ISDIR(st.st_mode)) {
- dbp("%s is not a directory", dir.c_str());
- return "";
- }
-
- return dir + "/settings.json";
-}
-
-static void CnfLoad() {
- std::string path = CnfPrepare();
- if(path.empty())
- return;
-
- if(settings)
- json_object_put(settings); // deallocate
-
- settings = json_object_from_file(path.c_str());
- if(!settings) {
- if(errno != ENOENT)
- dbp("cannot load settings: %s", strerror(errno));
-
- settings = json_object_new_object();
- }
-}
-
-static void CnfSave() {
- std::string path = CnfPrepare();
- if(path.empty())
- return;
-
- /* json-c <0.12 has the first argument non-const here */
- if(json_object_to_file_ext((char*) path.c_str(), settings, JSON_C_TO_STRING_PRETTY))
- dbp("cannot save settings: %s", strerror(errno));
-}
-
-void CnfFreezeInt(uint32_t val, const std::string &key) {
- struct json_object *jval = json_object_new_int(val);
- json_object_object_add(settings, key.c_str(), jval);
- CnfSave();
-}
-
-uint32_t CnfThawInt(uint32_t val, const std::string &key) {
- struct json_object *jval;
- if(json_object_object_get_ex(settings, key.c_str(), &jval))
- return json_object_get_int(jval);
- else return val;
-}
-
-void CnfFreezeFloat(float val, const std::string &key) {
- struct json_object *jval = json_object_new_double(val);
- json_object_object_add(settings, key.c_str(), jval);
- CnfSave();
-}
-
-float CnfThawFloat(float val, const std::string &key) {
- struct json_object *jval;
- if(json_object_object_get_ex(settings, key.c_str(), &jval))
- return json_object_get_double(jval);
- else return val;
-}
-
-void CnfFreezeString(const std::string &val, const std::string &key) {
- struct json_object *jval = json_object_new_string(val.c_str());
- json_object_object_add(settings, key.c_str(), jval);
- CnfSave();
-}
-
-std::string CnfThawString(const std::string &val, const std::string &key) {
- struct json_object *jval;
- if(json_object_object_get_ex(settings, key.c_str(), &jval))
- return json_object_get_string(jval);
- return val;
-}
-
-static void CnfFreezeWindowPos(Gtk::Window *win, const std::string &key) {
- int x, y, w, h;
- win->get_position(x, y);
- win->get_size(w, h);
-
- CnfFreezeInt(x, key + "_left");
- CnfFreezeInt(y, key + "_top");
- CnfFreezeInt(w, key + "_width");
- CnfFreezeInt(h, key + "_height");
-}
-
-static void CnfThawWindowPos(Gtk::Window *win, const std::string &key) {
- int x, y, w, h;
- win->get_position(x, y);
- win->get_size(w, h);
-
- x = CnfThawInt(x, key + "_left");
- y = CnfThawInt(y, key + "_top");
- w = CnfThawInt(w, key + "_width");
- h = CnfThawInt(h, key + "_height");
-
- win->move(x, y);
- win->resize(w, h);
-}
-
-/* Timers */
-
-int64_t GetMilliseconds(void) {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return 1000 * (uint64_t) ts.tv_sec + ts.tv_nsec / 1000000;
-}
-
-static bool TimerCallback() {
- SS.GW.TimerCallback();
- SS.TW.TimerCallback();
- return false;
-}
-
-void SetTimerFor(int milliseconds) {
- Glib::signal_timeout().connect(&TimerCallback, milliseconds);
-}
-
-static bool AutosaveTimerCallback() {
- SS.Autosave();
- return false;
-}
-
-void SetAutosaveTimerFor(int minutes) {
- Glib::signal_timeout().connect(&AutosaveTimerCallback, minutes * 60 * 1000);
-}
-
-static bool LaterCallback() {
- SS.DoLater();
- return false;
-}
-
-void ScheduleLater() {
- Glib::signal_idle().connect(&LaterCallback);
-}
-
-/* GL wrapper */
-
-#define GL_CHECK() \
- do { \
- int err = (int)glGetError(); \
- if(err) dbp("%s:%d: glGetError() == 0x%X %s", \
- __FILE__, __LINE__, err, gluErrorString(err)); \
- } while (0)
-
-class GlWidget : public Gtk::DrawingArea {
-public:
- GlWidget() : _offscreen(NULL) {
- _xdisplay = gdk_x11_get_default_xdisplay();
-
- int glxmajor, glxminor;
- if(!glXQueryVersion(_xdisplay, &glxmajor, &glxminor)) {
- dbp("OpenGL is not supported");
- oops();
- }
-
- if(glxmajor < 1 || (glxmajor == 1 && glxminor < 3)) {
- dbp("GLX version %d.%d is too old; 1.3 required", glxmajor, glxminor);
- oops();
- }
-
- static int fbconfig_attrs[] = {
- GLX_RENDER_TYPE, GLX_RGBA_BIT,
- GLX_RED_SIZE, 8,
- GLX_GREEN_SIZE, 8,
- GLX_BLUE_SIZE, 8,
- GLX_DEPTH_SIZE, 24,
- None
- };
- int fbconfig_num = 0;
- GLXFBConfig *fbconfigs = glXChooseFBConfig(_xdisplay, DefaultScreen(_xdisplay),
- fbconfig_attrs, &fbconfig_num);
- if(!fbconfigs || fbconfig_num == 0)
- oops();
-
- /* prefer FBConfigs with depth of 32;
- * Mesa software rasterizer explodes with a BadMatch without this;
- * without this, Intel on Mesa flickers horribly for some reason.
- this does not seem to affect other rasterizers (ie NVidia).
-
- see this Mesa bug:
- http://lists.freedesktop.org/archives/mesa-dev/2015-January/074693.html */
- GLXFBConfig fbconfig = fbconfigs[0];
- for(int i = 0; i < fbconfig_num; i++) {
- XVisualInfo *visual_info = glXGetVisualFromFBConfig(_xdisplay, fbconfigs[i]);
- /* some GL visuals, notably on Chromium GL, do not have an associated
- X visual; this is not an obstacle as we always render offscreen. */
- if(!visual_info) continue;
- int depth = visual_info->depth;
- XFree(visual_info);
-
- if(depth == 32) {
- fbconfig = fbconfigs[i];
- break;
- }
- }
-
- _glcontext = glXCreateNewContext(_xdisplay,
- fbconfig, GLX_RGBA_TYPE, 0, True);
- if(!_glcontext) {
- dbp("cannot create OpenGL context");
- oops();
- }
-
- XFree(fbconfigs);
-
- /* create a dummy X window to create a rendering context against.
- we could use a Pbuffer, but some implementations (Chromium GL)
- don't support these. we could use an existing window, but
- some implementations (Chromium GL... do you see a pattern?)
- do really strange things, i.e. draw a black rectangle on
- the very front of the desktop if you do this. */
- _xwindow = XCreateSimpleWindow(_xdisplay,
- XRootWindow(_xdisplay, gdk_x11_get_default_screen()),
- /*x*/ 0, /*y*/ 0, /*width*/ 1, /*height*/ 1,
- /*border_width*/ 0, /*border*/ 0, /*background*/ 0);
- }
-
- ~GlWidget() {
- glXMakeCurrent(_xdisplay, None, NULL);
-
- XDestroyWindow(_xdisplay, _xwindow);
-
- delete _offscreen;
-
- glXDestroyContext(_xdisplay, _glcontext);
- }
-
-protected:
- /* Draw on a GLX framebuffer object, then read pixels out and draw them on
- the Cairo context. Slower, but you get to overlay nice widgets. */
- virtual bool on_draw(const Cairo::RefPtr<Cairo::Context> &cr) {
- if(!glXMakeCurrent(_xdisplay, _xwindow, _glcontext))
- oops();
-
- if(!_offscreen)
- _offscreen = new GLOffscreen;
-
- Gdk::Rectangle allocation = get_allocation();
- if(!_offscreen->begin(allocation.get_width(), allocation.get_height()))
- oops();
-
- on_gl_draw();
- glFlush();
- GL_CHECK();
-
- Cairo::RefPtr<Cairo::ImageSurface> surface = Cairo::ImageSurface::create(
- _offscreen->end(), Cairo::FORMAT_RGB24,
- allocation.get_width(), allocation.get_height(), allocation.get_width() * 4);
- cr->set_source(surface, 0, 0);
- cr->paint();
- surface->finish();
-
- return true;
- }
-
-#ifdef HAVE_GTK2
- virtual bool on_expose_event(GdkEventExpose *) {
- return on_draw(get_window()->create_cairo_context());
- }
-#endif
-
- virtual void on_gl_draw() = 0;
-
-private:
- Display *_xdisplay;
- GLXContext _glcontext;
- GLOffscreen *_offscreen;
- ::Window _xwindow;
-};
-
-/* Editor overlay */
-
-class EditorOverlay : public Gtk::Fixed {
-public:
- EditorOverlay(Gtk::Widget &underlay) : _underlay(underlay) {
- set_size_request(0, 0);
-
- add(_underlay);
-
- _entry.set_no_show_all(true);
- _entry.set_has_frame(false);
- add(_entry);
-
- _entry.signal_activate().
- connect(sigc::mem_fun(this, &EditorOverlay::on_activate));
- }
-
- void start_editing(int x, int y, int font_height, bool is_monospace, int minWidthChars,
- const std::string &val) {
- Pango::FontDescription font_desc;
- font_desc.set_family(is_monospace ? "monospace" : "normal");
- font_desc.set_absolute_size(font_height * Pango::SCALE);
-
-#ifdef HAVE_GTK3
- /* For some reason override_font doesn't take screen DPI into
- account on GTK3 when working with font descriptors specified
- in absolute sizes; modify_font does on GTK2. */
- Pango::FontDescription override_font_desc(font_desc);
- double dpi = get_screen()->get_resolution();
- override_font_desc.set_size(font_height * 72.0 / dpi * Pango::SCALE);
- _entry.override_font(override_font_desc);
-#else
- _entry.modify_font(font_desc);
-#endif
-
- /* y coordinate denotes baseline */
- Pango::FontMetrics font_metrics = get_pango_context()->get_metrics(font_desc);
- y -= font_metrics.get_ascent() / Pango::SCALE;
-
- Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(get_pango_context());
- layout->set_font_description(font_desc);
- layout->set_text(val + " "); /* avoid scrolling */
- int width = layout->get_logical_extents().get_width();
-
-#ifdef HAVE_GTK3
- Gtk::Border border = _entry.get_style_context()->get_padding();
- move(_entry, x - border.get_left(), y - border.get_top());
- _entry.set_width_chars(minWidthChars);
- _entry.set_size_request(width / Pango::SCALE, -1);
-#else
- /* We need _gtk_entry_effective_inner_border, but it's not
- in the public API, so emulate its logic. */
- Gtk::Border border = { 2, 2, 2, 2 }, *style_border;
- gtk_widget_style_get(GTK_WIDGET(_entry.gobj()), "inner-border",
- &style_border, NULL);
- if(style_border) border = *style_border;
- move(_entry, x - border.left, y - border.top);
- /* This is what set_width_chars does. */
- int minWidth = minWidthChars * std::max(font_metrics.get_approximate_digit_width(),
- font_metrics.get_approximate_char_width());
- _entry.set_size_request(std::max(width, minWidth) / Pango::SCALE, -1);
-#endif
-
- _entry.set_text(val);
- if(!_entry.is_visible()) {
- _entry.show();
- _entry.grab_focus();
- _entry.add_modal_grab();
- }
- }
-
- void stop_editing() {
- if(_entry.is_visible())
- _entry.remove_modal_grab();
- _entry.hide();
- }
-
- bool is_editing() const {
- return _entry.is_visible();
- }
-
- sigc::signal<void, Glib::ustring> signal_editing_done() {
- return _signal_editing_done;
- }
-
- Gtk::Entry &get_entry() {
- return _entry;
- }
-
-protected:
- virtual bool on_key_press_event(GdkEventKey *event) {
- if(event->keyval == GDK_KEY_Escape) {
- stop_editing();
- return true;
- }
-
- return false;
- }
-
- virtual void on_size_allocate(Gtk::Allocation& allocation) {
- Gtk::Fixed::on_size_allocate(allocation);
-
- _underlay.size_allocate(allocation);
- }
-
- virtual void on_activate() {
- _signal_editing_done(_entry.get_text());
- }
-
-private:
- Gtk::Widget &_underlay;
- Gtk::Entry _entry;
- sigc::signal<void, Glib::ustring> _signal_editing_done;
-};
-
-/* Graphics window */
-
-int DeltaYOfScrollEvent(GdkEventScroll *event) {
-#ifdef HAVE_GTK3
- int delta_y = event->delta_y;
-#else
- int delta_y = 0;
-#endif
- if(delta_y == 0) {
- switch(event->direction) {
- case GDK_SCROLL_UP:
- delta_y = -1;
- break;
-
- case GDK_SCROLL_DOWN:
- delta_y = 1;
- break;
-
- default:
- /* do nothing */
- return false;
- }
- }
-
- return delta_y;
-}
-
-class GraphicsWidget : public GlWidget {
-public:
- GraphicsWidget() {
- set_events(Gdk::POINTER_MOTION_MASK |
- Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::BUTTON_MOTION_MASK |
- Gdk::SCROLL_MASK |
- Gdk::LEAVE_NOTIFY_MASK);
- set_double_buffered(true);
- }
-
-protected:
- virtual bool on_configure_event(GdkEventConfigure *event) {
- _w = event->width;
- _h = event->height;
-
- return GlWidget::on_configure_event(event);;
- }
-
- virtual void on_gl_draw() {
- SS.GW.Paint();
- }
-
- virtual bool on_motion_notify_event(GdkEventMotion *event) {
- int x, y;
- ij_to_xy(event->x, event->y, x, y);
-
- SS.GW.MouseMoved(x, y,
- event->state & GDK_BUTTON1_MASK,
- event->state & GDK_BUTTON2_MASK,
- event->state & GDK_BUTTON3_MASK,
- event->state & GDK_SHIFT_MASK,
- event->state & GDK_CONTROL_MASK);
-
- return true;
- }
-
- virtual bool on_button_press_event(GdkEventButton *event) {
- int x, y;
- ij_to_xy(event->x, event->y, x, y);
-
- switch(event->button) {
- case 1:
- if(event->type == GDK_BUTTON_PRESS)
- SS.GW.MouseLeftDown(x, y);
- else if(event->type == GDK_2BUTTON_PRESS)
- SS.GW.MouseLeftDoubleClick(x, y);
- break;
-
- case 2:
- case 3:
- SS.GW.MouseMiddleOrRightDown(x, y);
- break;
- }
-
- return true;
- }
-
- virtual bool on_button_release_event(GdkEventButton *event) {
- int x, y;
- ij_to_xy(event->x, event->y, x, y);
-
- switch(event->button) {
- case 1:
- SS.GW.MouseLeftUp(x, y);
- break;
-
- case 3:
- SS.GW.MouseRightUp(x, y);
- break;
- }
-
- return true;
- }
-
- virtual bool on_scroll_event(GdkEventScroll *event) {
- int x, y;
- ij_to_xy(event->x, event->y, x, y);
-
- SS.GW.MouseScroll(x, y, -DeltaYOfScrollEvent(event));
-
- return true;
- }
-
- virtual bool on_leave_notify_event (GdkEventCrossing *) {
- SS.GW.MouseLeave();
-
- return true;
- }
-
-private:
- int _w, _h;
- void ij_to_xy(int i, int j, int &x, int &y) {
- // Convert to xy (vs. ij) style coordinates,
- // with (0, 0) at center
- x = i - _w / 2;
- y = _h / 2 - j;
- }
-};
-
-class GraphicsWindowGtk : public Gtk::Window {
-public:
- GraphicsWindowGtk() : _overlay(_widget), _is_fullscreen(false) {
- set_default_size(900, 600);
-
- _box.pack_start(_menubar, false, true);
- _box.pack_start(_overlay, true, true);
-
- add(_box);
-
- _overlay.signal_editing_done().
- connect(sigc::mem_fun(this, &GraphicsWindowGtk::on_editing_done));
- }
-
- GraphicsWidget &get_widget() {
- return _widget;
- }
-
- EditorOverlay &get_overlay() {
- return _overlay;
- }
-
- Gtk::MenuBar &get_menubar() {
- return _menubar;
- }
-
- bool is_fullscreen() const {
- return _is_fullscreen;
- }
-
- bool emulate_key_press(GdkEventKey *event) {
- return on_key_press_event(event);
- }
-
-protected:
- virtual void on_show() {
- Gtk::Window::on_show();
-
- CnfThawWindowPos(this, "GraphicsWindow");
- }
-
- virtual void on_hide() {
- CnfFreezeWindowPos(this, "GraphicsWindow");
-
- Gtk::Window::on_hide();
- }
-
- virtual bool on_delete_event(GdkEventAny *) {
- if(!SS.OkayToStartNewFile()) return true;
- SS.Exit();
-
- return true;
- }
-
- virtual bool on_window_state_event(GdkEventWindowState *event) {
- _is_fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
-
- /* The event arrives too late for the caller of ToggleFullScreen
- to notice state change; and it's possible that the WM will
- refuse our request, so we can't just toggle the saved state */
- SS.GW.EnsureValidActives();
-
- return Gtk::Window::on_window_state_event(event);
- }
-
- virtual bool on_key_press_event(GdkEventKey *event) {
- int chr;
-
- switch(event->keyval) {
- case GDK_KEY_Escape:
- chr = GraphicsWindow::ESCAPE_KEY;
- break;
-
- case GDK_KEY_Delete:
- chr = GraphicsWindow::DELETE_KEY;
- break;
-
- case GDK_KEY_Tab:
- chr = '\t';
- break;
-
- case GDK_KEY_BackSpace:
- case GDK_KEY_Back:
- chr = '\b';
- break;
-
- default:
- if(event->keyval >= GDK_KEY_F1 && event->keyval <= GDK_KEY_F12) {
- chr = GraphicsWindow::FUNCTION_KEY_BASE + (event->keyval - GDK_KEY_F1);
- } else {
- chr = gdk_keyval_to_unicode(event->keyval);
- }
- }
-
- if(event->state & GDK_SHIFT_MASK){
- chr |= GraphicsWindow::SHIFT_MASK;
- }
- if(event->state & GDK_CONTROL_MASK) {
- chr |= GraphicsWindow::CTRL_MASK;
- }
-
- if(chr && SS.GW.KeyDown(chr)) {
- return true;
- }
-
- if(chr == '\t') {
- // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=123994.
- GraphicsWindow::MenuView(GraphicsWindow::MNU_SHOW_TEXT_WND);
- return true;
- }
-
- return Gtk::Window::on_key_press_event(event);
- }
-
- virtual void on_editing_done(Glib::ustring value) {
- SS.GW.EditControlDone(value.c_str());
- }
-
-private:
- GraphicsWidget _widget;
- EditorOverlay _overlay;
- Gtk::MenuBar _menubar;
- Gtk::VBox _box;
-
- bool _is_fullscreen;
-};
-
-std::unique_ptr<GraphicsWindowGtk> GW;
-
-void GetGraphicsWindowSize(int *w, int *h) {
- Gdk::Rectangle allocation = GW->get_widget().get_allocation();
- *w = allocation.get_width();
- *h = allocation.get_height();
-}
-
-void InvalidateGraphics(void) {
- GW->get_widget().queue_draw();
-}
-
-void PaintGraphics(void) {
- GW->get_widget().queue_draw();
- /* Process animation */
- Glib::MainContext::get_default()->iteration(false);
-}
-
-void SetCurrentFilename(const std::string &filename) {
- if(!filename.empty()) {
- GW->set_title("SolveSpace - " + filename);
- } else {
- GW->set_title("SolveSpace - (not yet saved)");
- }
-}
-
-void ToggleFullScreen(void) {
- if(GW->is_fullscreen())
- GW->unfullscreen();
- else
- GW->fullscreen();
-}
-
-bool FullScreenIsActive(void) {
- return GW->is_fullscreen();
-}
-
-void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars,
- const std::string &val) {
- Gdk::Rectangle rect = GW->get_widget().get_allocation();
-
- // Convert to ij (vs. xy) style coordinates,
- // and compensate for the input widget height due to inverse coord
- int i, j;
- i = x + rect.get_width() / 2;
- j = -y + rect.get_height() / 2;
-
- GW->get_overlay().start_editing(i, j, fontHeight, /*is_monospace=*/false, minWidthChars, val);
-}
-
-void HideGraphicsEditControl(void) {
- GW->get_overlay().stop_editing();
-}
-
-bool GraphicsEditControlIsVisible(void) {
- return GW->get_overlay().is_editing();
-}
-
-/* TODO: removing menubar breaks accelerators. */
-void ToggleMenuBar(void) {
- GW->get_menubar().set_visible(!GW->get_menubar().is_visible());
-}
-
-bool MenuBarIsVisible(void) {
- return GW->get_menubar().is_visible();
-}
-
-/* Context menus */
-
-class ContextMenuItem : public Gtk::MenuItem {
-public:
- static int choice;
-
- ContextMenuItem(const Glib::ustring &label, int id, bool mnemonic=false) :
- Gtk::MenuItem(label, mnemonic), _id(id) {
- }
-
-protected:
- virtual void on_activate() {
- Gtk::MenuItem::on_activate();
-
- if(has_submenu())
- return;
-
- choice = _id;
- }
-
- /* Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=695488.
- This is used in addition to on_activate() to catch mouse events.
- Without on_activate(), it would be impossible to select a menu item
- via keyboard.
- This selects the item twice in some cases, but we are idempotent.
- */
- virtual bool on_button_press_event(GdkEventButton *event) {
- if(event->button == 1 && event->type == GDK_BUTTON_PRESS) {
- on_activate();
- return true;
- }
-
- return Gtk::MenuItem::on_button_press_event(event);
- }
-
-private:
- int _id;
-};
-
-int ContextMenuItem::choice = 0;
-
-static Gtk::Menu *context_menu = NULL, *context_submenu = NULL;
-
-void AddContextMenuItem(const char *label, int id) {
- Gtk::MenuItem *menu_item;
- if(label)
- menu_item = new ContextMenuItem(label, id);
- else
- menu_item = new Gtk::SeparatorMenuItem();
-
- if(id == CONTEXT_SUBMENU) {
- menu_item->set_submenu(*context_submenu);
- context_submenu = NULL;
- }
-
- if(context_submenu) {
- context_submenu->append(*menu_item);
- } else {
- if(!context_menu)
- context_menu = new Gtk::Menu;
-
- context_menu->append(*menu_item);
- }
-}
-
-void CreateContextSubmenu(void) {
- if(context_submenu) oops();
-
- context_submenu = new Gtk::Menu;
-}
-
-int ShowContextMenu(void) {
- if(!context_menu)
- return -1;
-
- Glib::RefPtr<Glib::MainLoop> loop = Glib::MainLoop::create();
- context_menu->signal_deactivate().
- connect(sigc::mem_fun(loop.operator->(), &Glib::MainLoop::quit));
-
- ContextMenuItem::choice = -1;
-
- context_menu->show_all();
- context_menu->popup(3, GDK_CURRENT_TIME);
-
- loop->run();
-
- delete context_menu;
- context_menu = NULL;
-
- return ContextMenuItem::choice;
-}
-
-/* Main menu */
-
-template<class MenuItem> class MainMenuItem : public MenuItem {
-public:
- MainMenuItem(const GraphicsWindow::MenuEntry &entry) :
- MenuItem(), _entry(entry), _synthetic(false) {
- Glib::ustring label(_entry.label);
- for(size_t i = 0; i < label.length(); i++) {
- if(label[i] == '&')
- label.replace(i, 1, "_");
- }
-
- guint accel_key = 0;
- Gdk::ModifierType accel_mods = Gdk::ModifierType();
- switch(_entry.accel) {
- case GraphicsWindow::DELETE_KEY:
- accel_key = GDK_KEY_Delete;
- break;
-
- case GraphicsWindow::ESCAPE_KEY:
- accel_key = GDK_KEY_Escape;
- break;
-
- case '\t':
- accel_key = GDK_KEY_Tab;
- break;
-
- default:
- accel_key = _entry.accel & ~(GraphicsWindow::SHIFT_MASK | GraphicsWindow::CTRL_MASK);
- if(accel_key > GraphicsWindow::FUNCTION_KEY_BASE &&
- accel_key <= GraphicsWindow::FUNCTION_KEY_BASE + 12)
- accel_key = GDK_KEY_F1 + (accel_key - GraphicsWindow::FUNCTION_KEY_BASE - 1);
- else
- accel_key = gdk_unicode_to_keyval(accel_key);
-
- if(_entry.accel & GraphicsWindow::SHIFT_MASK)
- accel_mods |= Gdk::SHIFT_MASK;
- if(_entry.accel & GraphicsWindow::CTRL_MASK)
- accel_mods |= Gdk::CONTROL_MASK;
- }
-
- MenuItem::set_label(label);
- MenuItem::set_use_underline(true);
- if(!(accel_key & 0x01000000))
- MenuItem::set_accel_key(Gtk::AccelKey(accel_key, accel_mods));
- }
-
- void set_active(bool checked) {
- if(MenuItem::get_active() == checked)
- return;
-
- _synthetic = true;
- MenuItem::set_active(checked);
- }
-
-protected:
- virtual void on_activate() {
- MenuItem::on_activate();
-
- if(_synthetic)
- _synthetic = false;
- else if(!MenuItem::has_submenu() && _entry.fn)
- _entry.fn(_entry.id);
- }
-
-private:
- const GraphicsWindow::MenuEntry &_entry;
- bool _synthetic;
-};
-
-static std::map<int, Gtk::MenuItem *> main_menu_items;
-
-static void InitMainMenu(Gtk::MenuShell *menu_shell) {
- Gtk::MenuItem *menu_item = NULL;
- Gtk::MenuShell *levels[5] = {menu_shell, 0};
-
- const GraphicsWindow::MenuEntry *entry = &GraphicsWindow::menu[0];
- int current_level = 0;
- while(entry->level >= 0) {
- if(entry->level > current_level) {
- Gtk::Menu *menu = new Gtk::Menu;
- menu_item->set_submenu(*menu);
-
- if((unsigned)entry->level >= sizeof(levels) / sizeof(levels[0]))
- oops();
-
- levels[entry->level] = menu;
- }
-
- current_level = entry->level;
-
- if(entry->label) {
- switch(entry->kind) {
- case GraphicsWindow::MENU_ITEM_NORMAL:
- menu_item = new MainMenuItem<Gtk::MenuItem>(*entry);
- break;
-
- case GraphicsWindow::MENU_ITEM_CHECK:
- menu_item = new MainMenuItem<Gtk::CheckMenuItem>(*entry);
- break;
-
- case GraphicsWindow::MENU_ITEM_RADIO:
- MainMenuItem<Gtk::CheckMenuItem> *radio_item =
- new MainMenuItem<Gtk::CheckMenuItem>(*entry);
- radio_item->set_draw_as_radio(true);
- menu_item = radio_item;
- break;
- }
- } else {
- menu_item = new Gtk::SeparatorMenuItem();
- }
-
- levels[entry->level]->append(*menu_item);
-
- main_menu_items[entry->id] = menu_item;
-
- ++entry;
- }
-}
-
-void EnableMenuById(int id, bool enabled) {
- main_menu_items[id]->set_sensitive(enabled);
-}
-
-void CheckMenuById(int id, bool checked) {
- ((MainMenuItem<Gtk::CheckMenuItem>*)main_menu_items[id])->set_active(checked);
-}
-
-void RadioMenuById(int id, bool selected) {
- SolveSpace::CheckMenuById(id, selected);
-}
-
-class RecentMenuItem : public Gtk::MenuItem {
-public:
- RecentMenuItem(const Glib::ustring& label, int id) :
- MenuItem(label), _id(id) {
- }
-
-protected:
- virtual void on_activate() {
- if(_id >= RECENT_OPEN && _id < (RECENT_OPEN + MAX_RECENT))
- SolveSpaceUI::MenuFile(_id);
- else if(_id >= RECENT_LINK && _id < (RECENT_LINK + MAX_RECENT))
- Group::MenuGroup(_id);
- }
-
-private:
- int _id;
-};
-
-static void RefreshRecentMenu(int id, int base) {
- Gtk::MenuItem *recent = static_cast<Gtk::MenuItem*>(main_menu_items[id]);
- recent->unset_submenu();
-
- Gtk::Menu *menu = new Gtk::Menu;
- recent->set_submenu(*menu);
-
- if(std::string(RecentFile[0]).empty()) {
- Gtk::MenuItem *placeholder = new Gtk::MenuItem("(no recent files)");
- placeholder->set_sensitive(false);
- menu->append(*placeholder);
- } else {
- for(int i = 0; i < MAX_RECENT; i++) {
- if(std::string(RecentFile[i]).empty())
- break;
-
- RecentMenuItem *item = new RecentMenuItem(RecentFile[i], base + i);
- menu->append(*item);
- }
- }
-
- menu->show_all();
-}
-
-void RefreshRecentMenus(void) {
- RefreshRecentMenu(GraphicsWindow::MNU_OPEN_RECENT, RECENT_OPEN);
- RefreshRecentMenu(GraphicsWindow::MNU_GROUP_RECENT, RECENT_LINK);
-}
-
-/* Save/load */
-
-static std::string ConvertFilters(std::string active, const FileFilter ssFilters[],
- Gtk::FileChooser *chooser) {
- for(const FileFilter *ssFilter = ssFilters; ssFilter->name; ssFilter++) {
-#ifdef HAVE_GTK3
- Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
-#else
- Gtk::FileFilter *filter = new Gtk::FileFilter;
-#endif
- filter->set_name(ssFilter->name);
-
- bool is_active = false;
- std::string desc = "";
- for(const char *const *ssPattern = ssFilter->patterns; *ssPattern; ssPattern++) {
- std::string pattern = "*." + std::string(*ssPattern);
- filter->add_pattern(pattern);
- filter->add_pattern(Glib::ustring(pattern).uppercase());
- if(active == "")
- active = pattern.substr(2);
- if("*." + active == pattern)
- is_active = true;
- if(desc == "")
- desc = pattern;
- else
- desc += ", " + pattern;
- }
- filter->set_name(filter->get_name() + " (" + desc + ")");
-
-#ifdef HAVE_GTK3
- chooser->add_filter(filter);
- if(is_active)
- chooser->set_filter(filter);
-#else
- chooser->add_filter(*filter);
- if(is_active)
- chooser->set_filter(*filter);
-#endif
- }
-
- return active;
-}
-
-bool GetOpenFile(std::string *filename, const std::string &activeOrEmpty,
- const FileFilter filters[]) {
- Gtk::FileChooserDialog chooser(*GW, "SolveSpace - Open File");
- chooser.set_filename(*filename);
- chooser.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
- chooser.add_button("_Open", Gtk::RESPONSE_OK);
- chooser.set_current_folder(CnfThawString("", "FileChooserPath"));
-
- ConvertFilters(activeOrEmpty, filters, &chooser);
-
- if(chooser.run() == Gtk::RESPONSE_OK) {
- CnfFreezeString(chooser.get_current_folder(), "FileChooserPath");
- *filename = chooser.get_filename();
- return true;
- } else {
- return false;
- }
-}
-
-/* Glib::path_get_basename got /removed/ in 3.0?! Come on */
-static std::string Basename(std::string filename) {
- int slash = filename.rfind('/');
- if(slash >= 0)
- return filename.substr(slash + 1, filename.length());
- return "";
-}
-
-static void ChooserFilterChanged(Gtk::FileChooserDialog *chooser)
-{
- /* Extract the pattern from the filter. GtkFileFilter doesn't provide
- any way to list the patterns, so we extract it from the filter name.
- Gross. */
- std::string filter_name = chooser->get_filter()->get_name();
- int lparen = filter_name.rfind('(') + 1;
- int rdelim = filter_name.find(',', lparen);
- if(rdelim < 0)
- rdelim = filter_name.find(')', lparen);
- if(lparen < 0 || rdelim < 0)
- oops();
-
- std::string extension = filter_name.substr(lparen, rdelim - lparen);
- if(extension == "*")
- return;
-
- if(extension.length() > 2 && extension.substr(0, 2) == "*.")
- extension = extension.substr(2, extension.length() - 2);
-
- std::string basename = Basename(chooser->get_filename());
- int dot = basename.rfind('.');
- if(dot >= 0) {
- basename.replace(dot + 1, basename.length() - dot - 1, extension);
- chooser->set_current_name(basename);
- } else {
- chooser->set_current_name(basename + "." + extension);
- }
-}
-
-bool GetSaveFile(std::string *filename, const std::string &activeOrEmpty,
- const FileFilter filters[]) {
- Gtk::FileChooserDialog chooser(*GW, "SolveSpace - Save File",
- Gtk::FILE_CHOOSER_ACTION_SAVE);
- chooser.set_do_overwrite_confirmation(true);
- chooser.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
- chooser.add_button("_Save", Gtk::RESPONSE_OK);
-
- std::string active = ConvertFilters(activeOrEmpty, filters, &chooser);
-
- chooser.set_current_folder(CnfThawString("", "FileChooserPath"));
- chooser.set_current_name(std::string("untitled.") + active);
-
- /* Gtk's dialog doesn't change the extension when you change the filter,
- and makes it extremely hard to do so. Gtk is garbage. */
- chooser.property_filter().signal_changed().
- connect(sigc::bind(sigc::ptr_fun(&ChooserFilterChanged), &chooser));
-
- if(chooser.run() == Gtk::RESPONSE_OK) {
- CnfFreezeString(chooser.get_current_folder(), "FileChooserPath");
- *filename = chooser.get_filename();
- return true;
- } else {
- return false;
- }
-}
-
-DialogChoice SaveFileYesNoCancel(void) {
- Glib::ustring message =
- "The file has changed since it was last saved.\n"
- "Do you want to save the changes?";
- Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, Gtk::MESSAGE_QUESTION,
- Gtk::BUTTONS_NONE, /*is_modal*/ true);
- dialog.set_title("SolveSpace - Modified File");
- dialog.add_button("_Save", Gtk::RESPONSE_YES);
- dialog.add_button("Do_n't Save", Gtk::RESPONSE_NO);
- dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
-
- switch(dialog.run()) {
- case Gtk::RESPONSE_YES:
- return DIALOG_YES;
-
- case Gtk::RESPONSE_NO:
- return DIALOG_NO;
-
- case Gtk::RESPONSE_CANCEL:
- default:
- return DIALOG_CANCEL;
- }
-}
-
-DialogChoice LoadAutosaveYesNo(void) {
- Glib::ustring message =
- "An autosave file is availible for this project.\n"
- "Do you want to load the autosave file instead?";
- Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, Gtk::MESSAGE_QUESTION,
- Gtk::BUTTONS_NONE, /*is_modal*/ true);
- dialog.set_title("SolveSpace - Autosave Available");
- dialog.add_button("_Load autosave", Gtk::RESPONSE_YES);
- dialog.add_button("Do_n't Load", Gtk::RESPONSE_NO);
-
- switch(dialog.run()) {
- case Gtk::RESPONSE_YES:
- return DIALOG_YES;
-
- case Gtk::RESPONSE_NO:
- default:
- return DIALOG_NO;
- }
-}
-
-DialogChoice LocateImportedFileYesNoCancel(const std::string &filename,
- bool canCancel) {
- Glib::ustring message =
- "The linked file " + filename + " is not present.\n"
- "Do you want to locate it manually?\n"
- "If you select \"No\", any geometry that depends on "
- "the missing file will be removed.";
- Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, Gtk::MESSAGE_QUESTION,
- Gtk::BUTTONS_NONE, /*is_modal*/ true);
- dialog.set_title("SolveSpace - Missing File");
- dialog.add_button("_Yes", Gtk::RESPONSE_YES);
- dialog.add_button("_No", Gtk::RESPONSE_NO);
- if(canCancel)
- dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
-
- switch(dialog.run()) {
- case Gtk::RESPONSE_YES:
- return DIALOG_YES;
-
- case Gtk::RESPONSE_NO:
- return DIALOG_NO;
-
- case Gtk::RESPONSE_CANCEL:
- default:
- return DIALOG_CANCEL;
- }
-}
-
-/* Text window */
-
-class TextWidget : public GlWidget {
-public:
-#ifdef HAVE_GTK3
- TextWidget(Glib::RefPtr<Gtk::Adjustment> adjustment) : _adjustment(adjustment) {
-#else
- TextWidget(Gtk::Adjustment* adjustment) : _adjustment(adjustment) {
-#endif
- set_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK |
- Gdk::LEAVE_NOTIFY_MASK);
- }
-
- void set_cursor_hand(bool is_hand) {
- Glib::RefPtr<Gdk::Window> gdkwin = get_window();
- if(gdkwin) { // returns NULL if not realized
- Gdk::CursorType type = is_hand ? Gdk::HAND1 : Gdk::ARROW;
-#ifdef HAVE_GTK3
- gdkwin->set_cursor(Gdk::Cursor::create(type));
-#else
- gdkwin->set_cursor(Gdk::Cursor(type));
-#endif
- }
- }
-
-protected:
- virtual void on_gl_draw() {
- SS.TW.Paint();
- }
-
- virtual bool on_motion_notify_event(GdkEventMotion *event) {
- SS.TW.MouseEvent(/*leftClick*/ false,
- /*leftDown*/ event->state & GDK_BUTTON1_MASK,
- event->x, event->y);
-
- return true;
- }
-
- virtual bool on_button_press_event(GdkEventButton *event) {
- SS.TW.MouseEvent(/*leftClick*/ event->type == GDK_BUTTON_PRESS,
- /*leftDown*/ event->state & GDK_BUTTON1_MASK,
- event->x, event->y);
-
- return true;
- }
-
- virtual bool on_scroll_event(GdkEventScroll *event) {
- _adjustment->set_value(_adjustment->get_value() +
- DeltaYOfScrollEvent(event) * _adjustment->get_page_increment());
-
- return true;
- }
-
- virtual bool on_leave_notify_event (GdkEventCrossing *) {
- SS.TW.MouseLeave();
-
- return true;
- }
-
-private:
-#ifdef HAVE_GTK3
- Glib::RefPtr<Gtk::Adjustment> _adjustment;
-#else
- Gtk::Adjustment *_adjustment;
-#endif
-};
-
-class TextWindowGtk : public Gtk::Window {
-public:
- TextWindowGtk() : _scrollbar(), _widget(_scrollbar.get_adjustment()),
- _overlay(_widget), _box() {
- set_type_hint(Gdk::WINDOW_TYPE_HINT_UTILITY);
- set_skip_taskbar_hint(true);
- set_skip_pager_hint(true);
- set_title("SolveSpace - Property Browser");
- set_default_size(420, 300);
-
- _box.pack_start(_overlay, true, true);
- _box.pack_start(_scrollbar, false, true);
- add(_box);
-
- _scrollbar.get_adjustment()->signal_value_changed().
- connect(sigc::mem_fun(this, &TextWindowGtk::on_scrollbar_value_changed));
-
- _overlay.signal_editing_done().
- connect(sigc::mem_fun(this, &TextWindowGtk::on_editing_done));
-
- _overlay.get_entry().signal_motion_notify_event().
- connect(sigc::mem_fun(this, &TextWindowGtk::on_editor_motion_notify_event));
- _overlay.get_entry().signal_button_press_event().
- connect(sigc::mem_fun(this, &TextWindowGtk::on_editor_button_press_event));
- }
-
- Gtk::VScrollbar &get_scrollbar() {
- return _scrollbar;
- }
-
- TextWidget &get_widget() {
- return _widget;
- }
-
- EditorOverlay &get_overlay() {
- return _overlay;
- }
-
-protected:
- virtual void on_show() {
- Gtk::Window::on_show();
-
- CnfThawWindowPos(this, "TextWindow");
- }
-
- virtual void on_hide() {
- CnfFreezeWindowPos(this, "TextWindow");
-
- Gtk::Window::on_hide();
- }
-
- virtual bool on_delete_event(GdkEventAny *) {
- /* trigger the action and ignore the request */
- GraphicsWindow::MenuView(GraphicsWindow::MNU_SHOW_TEXT_WND);
-
- return false;
- }
-
- virtual void on_scrollbar_value_changed() {
- SS.TW.ScrollbarEvent(_scrollbar.get_adjustment()->get_value());
- }
-
- virtual void on_editing_done(Glib::ustring value) {
- SS.TW.EditControlDone(value.c_str());
- }
-
- virtual bool on_editor_motion_notify_event(GdkEventMotion *event) {
- return _widget.event((GdkEvent*) event);
- }
-
- virtual bool on_editor_button_press_event(GdkEventButton *event) {
- return _widget.event((GdkEvent*) event);
- }
-
- virtual bool on_key_press_event(GdkEventKey *event) {
- if(GW->emulate_key_press(event)) {
- return true;
- }
-
- return Gtk::Window::on_key_press_event(event);
- }
-
-private:
- Gtk::VScrollbar _scrollbar;
- TextWidget _widget;
- EditorOverlay _overlay;
- Gtk::HBox _box;
-};
-
-std::unique_ptr<TextWindowGtk> TW;
-
-void ShowTextWindow(bool visible) {
- if(visible)
- TW->show();
- else
- TW->hide();
-}
-
-void GetTextWindowSize(int *w, int *h) {
- Gdk::Rectangle allocation = TW->get_widget().get_allocation();
- *w = allocation.get_width();
- *h = allocation.get_height();
-}
-
-void InvalidateText(void) {
- TW->get_widget().queue_draw();
-}
-
-void MoveTextScrollbarTo(int pos, int maxPos, int page) {
- TW->get_scrollbar().get_adjustment()->configure(pos, 0, maxPos, 1, 10, page);
-}
-
-void SetMousePointerToHand(bool is_hand) {
- TW->get_widget().set_cursor_hand(is_hand);
-}
-
-void ShowTextEditControl(int x, int y, const std::string &val) {
- TW->get_overlay().start_editing(x, y, TextWindow::CHAR_HEIGHT, /*is_monospace=*/true, 30, val);
-}
-
-void HideTextEditControl(void) {
- TW->get_overlay().stop_editing();
-}
-
-bool TextEditControlIsVisible(void) {
- return TW->get_overlay().is_editing();
-}
-
-/* Miscellanea */
-
-
-void DoMessageBox(const char *message, int rows, int cols, bool error) {
- Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true,
- error ? Gtk::MESSAGE_ERROR : Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK,
- /*is_modal*/ true);
- dialog.set_title(error ? "SolveSpace - Error" : "SolveSpace - Message");
- dialog.run();
-}
-
-void OpenWebsite(const char *url) {
- gtk_show_uri(Gdk::Screen::get_default()->gobj(), url, GDK_CURRENT_TIME, NULL);
-}
-
-/* fontconfig is already initialized by GTK */
-std::vector<std::string> GetFontFiles() {
- std::vector<std::string> fonts;
-
- FcPattern *pat = FcPatternCreate();
- FcObjectSet *os = FcObjectSetBuild(FC_FILE, (char *)0);
- FcFontSet *fs = FcFontList(0, pat, os);
-
- for(int i = 0; i < fs->nfont; i++) {
- FcChar8 *filenameFC = FcPatternFormat(fs->fonts[i], (const FcChar8*) "%{file}");
- std::string filename = (char*) filenameFC;
- fonts.push_back(filename);
- FcStrFree(filenameFC);
- }
-
- FcFontSetDestroy(fs);
- FcObjectSetDestroy(os);
- FcPatternDestroy(pat);
-
- return fonts;
-}
-
-/* Space Navigator support */
-
-#ifdef HAVE_SPACEWARE
-static GdkFilterReturn GdkSpnavFilter(GdkXEvent *gxevent, GdkEvent *, gpointer) {
- XEvent *xevent = (XEvent*) gxevent;
-
- spnav_event sev;
- if(!spnav_x11_event(xevent, &sev))
- return GDK_FILTER_CONTINUE;
-
- switch(sev.type) {
- case SPNAV_EVENT_MOTION:
- SS.GW.SpaceNavigatorMoved(
- (double)sev.motion.x,
- (double)sev.motion.y,
- (double)sev.motion.z * -1.0,
- (double)sev.motion.rx * 0.001,
- (double)sev.motion.ry * 0.001,
- (double)sev.motion.rz * -0.001,
- xevent->xmotion.state & ShiftMask);
- break;
-
- case SPNAV_EVENT_BUTTON:
- if(!sev.button.press && sev.button.bnum == 0) {
- SS.GW.SpaceNavigatorButtonUp();
- }
- break;
- }
-
- return GDK_FILTER_REMOVE;
-}
-#endif
-
-/* Application lifecycle */
-
-void ExitNow(void) {
- GW->hide();
- TW->hide();
-}
-};
-
-int main(int argc, char** argv) {
- /* It would in principle be possible to judiciously use
- Glib::filename_{from,to}_utf8, but it's not really worth
- the effort.
- The setlocale() call is necessary for Glib::get_charset()
- to detect the system character set; otherwise it thinks
- it is always ANSI_X3.4-1968.
- We set it back to C after all. */
- setlocale(LC_ALL, "");
- if(!Glib::get_charset()) {
- std::cerr << "Sorry, only UTF-8 locales are supported." << std::endl;
- return 1;
- }
- setlocale(LC_ALL, "C");
-
- /* If we don't do this, gtk_init will set the C standard library
- locale, and printf will format floats using ",". We will then
- fail to parse these. Also, many text window lines will become
- ambiguous. */
- gtk_disable_setlocale();
-
- Gtk::Main main(argc, argv);
-
-#ifdef HAVE_SPACEWARE
- gdk_window_add_filter(NULL, GdkSpnavFilter, NULL);
-#endif
-
- CnfLoad();
-
- TW.reset(new TextWindowGtk);
- GW.reset(new GraphicsWindowGtk);
- InitMainMenu(&GW->get_menubar());
- GW->get_menubar().accelerate(*TW);
- TW->set_transient_for(*GW);
-
- TW->show_all();
- GW->show_all();
-
-#ifdef HAVE_SPACEWARE
-#ifdef HAVE_GTK3
- // We don't care if it can't be opened; just continue without.
- spnav_x11_open(gdk_x11_get_default_xdisplay(),
- gdk_x11_window_get_xid(GW->get_window()->gobj()));
-#else
- spnav_x11_open(gdk_x11_get_default_xdisplay(),
- GDK_WINDOW_XWINDOW(GW->get_window()->gobj()));
-#endif
-#endif
-
- SS.Init();
-
- if(argc >= 2) {
- if(argc > 2) {
- std::cerr << "Only the first file passed on command line will be opened."
- << std::endl;
- }
-
- /* Make sure the argument is valid UTF-8. */
- SS.OpenFile(Glib::ustring(argv[1]));
- }
-
- main.run(*GW);
-
- TW.reset();
- GW.reset();
-
- SK.Clear();
- SS.Clear();
-
- return 0;
-}
#include "libdxfrw.h"
#include "libdwgr.h"
-#ifdef WIN32
-// Conflicts with DRW::TEXT.
-# undef TEXT
-#endif
-
namespace SolveSpace {
static std::string ToUpper(std::string str) {
return str;
}
-class DxfReadInterface : public DRW_Interface {
+static Quaternion NormalFromExtPoint(Vector extPoint) {
+ // DXF arbitrary axis algorithm for transforming a Z-vector into a rotated
+ // coordinate system
+ Vector ax, ay;
+ Vector az = extPoint.WithMagnitude(1.0);
+
+ if ((fabs(az.x) < 1/64.) && (fabs(az.y) < 1/64.)) {
+ ax = Vector::From(0, 1, 0).Cross(az).WithMagnitude(1.0);
+ } else {
+ ax = Vector::From(0, 0, 1).Cross(az).WithMagnitude(1.0);
+ }
+ ay = az.Cross(ax).WithMagnitude(1.0);
+ return Quaternion::From(ax, ay);
+}
+
+class DxfImport : public DRW_Interface {
public:
Vector blockX;
Vector blockY;
+ Vector blockZ;
Vector blockT;
void invertXTransform() {
void clearBlockTransform() {
blockX = Vector::From(1.0, 0.0, 0.0);
blockY = Vector::From(0.0, 1.0, 0.0);
+ blockZ = Vector::From(0.0, 0.0, 1.0);
blockT = Vector::From(0.0, 0.0, 0.0);
}
Vector r = blockT;
r = r.Plus(blockX.ScaledBy(v.x));
r = r.Plus(blockY.ScaledBy(v.y));
+ r = r.Plus(blockZ.ScaledBy(v.z));
return r;
}
}
Vector toVector(const DRW_Coord &c, bool transform = true) {
- Vector result = Vector::From(c.x, c.y, 0.0);
+ Vector result = Vector::From(c.x, c.y, c.z);
if(transform) return blockTransform(result);
return result;
}
}
Vector toVector(const DRW_Vertex &c) {
- Vector result = Vector::From(c.basePoint.x, c.basePoint.y, 0.0);
+ Vector result = Vector::From(c.basePoint.x, c.basePoint.y, c.basePoint.z);
return blockTransform(result);
}
if(reversed) std::swap(p0, p1);
blockTransformArc(¢er, &p0, &p1);
- hRequest hr = SS.GW.AddRequest(Request::ARC_OF_CIRCLE, false);
+ hRequest hr = SS.GW.AddRequest(Request::Type::ARC_OF_CIRCLE, /*rememberForUndo=*/false);
SK.GetEntity(hr.entity(1))->PointForceTo(center);
SK.GetEntity(hr.entity(2))->PointForceTo(p0);
SK.GetEntity(hr.entity(3))->PointForceTo(p1);
DRW_Block data;
};
+ bool asConstruction = false;
unsigned unknownEntities = 0;
std::map<std::string, hStyle> styles;
std::map<std::string, Block> blocks;
}
}
- int dxfAlignToOrigin(DRW_Text::HAlign alignH, DRW_Text::VAlign alignV) {
- int origin = 0;
+ Style::TextOrigin dxfAlignToOrigin(DRW_Text::HAlign alignH, DRW_Text::VAlign alignV) {
+ uint32_t origin = 0;
switch(alignH) {
case DRW_Text::HLeft:
- origin |= Style::ORIGIN_LEFT;
+ origin |= (uint32_t)Style::TextOrigin::LEFT;
break;
case DRW_Text::HMiddle:
break;
case DRW_Text::HRight:
- origin |= Style::ORIGIN_RIGHT;
+ origin |= (uint32_t)Style::TextOrigin::RIGHT;
break;
case DRW_Text::HAligned:
case DRW_Text::HFit:
default:
- origin |= Style::ORIGIN_LEFT;
+ origin |= (uint32_t)Style::TextOrigin::LEFT;
break;
}
switch(alignV) {
case DRW_Text::VBaseLine:
case DRW_Text::VBottom:
- origin |= Style::ORIGIN_BOT;
+ origin |= (uint32_t)Style::TextOrigin::BOT;
break;
case DRW_Text::VMiddle:
break;
case DRW_Text::VTop:
- origin |= Style::ORIGIN_TOP;
+ origin |= (uint32_t)Style::TextOrigin::TOP;
break;
default:
- origin |= Style::ORIGIN_BOT;
+ origin |= (uint32_t)Style::TextOrigin::BOT;
break;
}
- return origin;
+ return (Style::TextOrigin)origin;
}
DRW_Layer *getSourceLayer(const DRW_Entity *e) {
hStyle styleFor(const DRW_Entity *e) {
// Color.
- // TODO: which color to choose: index or RGB one?
+ //! @todo which color to choose: index or RGB one?
int col = getColor(e);
RgbaColor c = RgbaColor::From(DRW::dxfColors[col][0],
DRW::dxfColors[col][1],
if(width < 0.0) width = 1.0;
// Line stipple.
- // TODO: Probably, we can load default autocad patterns and match it with ours.
+ //! @todo Probably, we can load default autocad patterns and match it with ours.
std::string lineType = getLineType(e);
- int stipple = Style::STIPPLE_CONTINUOUS;
- for(int i = 0; i <= Style::LAST_STIPPLE; i++) {
- if(lineType == DxfFileWriter::lineTypeName(i)) {
- stipple = i;
+ StipplePattern stipple = StipplePattern::CONTINUOUS;
+ for(uint32_t i = 0; i <= (uint32_t)StipplePattern::LAST; i++) {
+ StipplePattern st = (StipplePattern)i;
+ if(lineType == DxfFileWriter::lineTypeName(st)) {
+ stipple = st;
break;
}
}
hStyle hs = { Style::CreateCustomStyle(/*rememberForUndo=*/false) };
Style *s = Style::Get(hs);
if(lw != DRW_LW_Conv::widthDefault) {
- s->widthAs = Style::UNITS_AS_MM;
+ s->widthAs = Style::UnitsAs::MM;
s->width = width;
s->stippleScale = 1.0 + width * 2.0;
}
s->name = id;
s->stippleType = stipple;
if(c.red != 0 || c.green != 0 || c.blue != 0) s->color = c;
- s->textHeightAs = Style::UNITS_AS_MM;
+ s->textHeightAs = Style::UnitsAs::MM;
s->textHeight = textHeight;
s->textAngle = textAngle;
s->textOrigin = dxfAlignToOrigin(alignH, alignV);
return hs;
}
- void setStyle(hRequest hr, hStyle hs) {
+ void configureRequest(hRequest hr, hStyle hs) {
Request *r = SK.GetRequest(hr);
+ r->construction = asConstruction;
r->style = hs;
}
Entity *e = SK.GetEntity(he);
Vector pos = e->PointGetNum();
hEntity p = findPoint(pos);
- if(p.v == he.v) return;
- if(p.v != Entity::NO_ENTITY.v) {
+ if(p == he) return;
+ if(p != Entity::NO_ENTITY) {
if(constrain) {
Constraint::ConstrainCoincident(he, p);
}
hEntity createOrGetPoint(const Vector &p) {
hEntity he = findPoint(p);
- if(he.v != Entity::NO_ENTITY.v) return he;
+ if(he != Entity::NO_ENTITY) return he;
- hRequest hr = SS.GW.AddRequest(Request::DATUM_POINT, false);
+ hRequest hr = SS.GW.AddRequest(Request::Type::DATUM_POINT, /*rememberForUndo=*/false);
he = hr.entity(0);
SK.GetEntity(he)->PointForceTo(p);
points.emplace(p, he);
return he;
}
- hEntity createLine(Vector p0, Vector p1, uint32_t style, bool constrainHV = false) {
+ hEntity createLine(Vector p0, Vector p1, hStyle style, bool constrainHV = false) {
if(p0.Equals(p1)) return Entity::NO_ENTITY;
- hRequest hr = SS.GW.AddRequest(Request::LINE_SEGMENT, false);
+ hRequest hr = SS.GW.AddRequest(Request::Type::LINE_SEGMENT, /*rememberForUndo=*/false);
SK.GetEntity(hr.entity(1))->PointForceTo(p0);
SK.GetEntity(hr.entity(2))->PointForceTo(p1);
processPoint(hr.entity(1));
processPoint(hr.entity(2));
- if(constrainHV) {
- int cType = -1;
+ if(constrainHV && SS.GW.LockedInWorkplane()) {
+ bool hasConstraint = false;
+ Constraint::Type cType;
if(fabs(p0.x - p1.x) < LENGTH_EPS) {
- cType = Constraint::VERTICAL;
- }
- else if(fabs(p0.y - p1.y) < LENGTH_EPS) {
- cType = Constraint::HORIZONTAL;
+ hasConstraint = true;
+ cType = Constraint::Type::VERTICAL;
+ } else if(fabs(p0.y - p1.y) < LENGTH_EPS) {
+ hasConstraint = true;
+ cType = Constraint::Type::HORIZONTAL;
}
- if(cType != -1) {
+ if(hasConstraint) {
Constraint::Constrain(
cType,
Entity::NO_ENTITY,
}
}
- if(style != 0) {
- Request *r = SK.GetRequest(hr);
- r->style = hStyle{ style };
- }
+ configureRequest(hr, style);
+ return hr.entity(0);
+ }
+
+ hEntity createWorkplane(const Vector &p, const Quaternion &q) {
+ hRequest hr = SS.GW.AddRequest(Request::Type::WORKPLANE, /*rememberForUndo=*/false);
+ SK.GetEntity(hr.entity(1))->PointForceTo(p);
+ processPoint(hr.entity(1));
+ SK.GetEntity(hr.entity(32))->NormalForceTo(q);
return hr.entity(0);
}
- hEntity createCircle(const Vector &c, double r, uint32_t style) {
- hRequest hr = SS.GW.AddRequest(Request::CIRCLE, false);
+ hEntity findOrCreateWorkplane(const Vector &p, const Quaternion &q) {
+ Vector z = q.RotationN();
+ for(auto &r : SK.request) {
+ if((r.type == Request::Type::WORKPLANE) && (r.group == SS.GW.activeGroup)) {
+ Vector wp = SK.GetEntity(r.h.entity(1))->PointGetNum();
+ Vector wz = SK.GetEntity(r.h.entity(32))->NormalN();
+
+ if ((p.DistanceToPlane(wz, wp) < LENGTH_EPS) && z.Equals(wz)) {
+ return r.h.entity(0);
+ }
+ }
+ }
+
+ return createWorkplane(p, q);
+ }
+
+ static void activateWorkplane(hEntity he) {
+ Group *g = SK.GetGroup(SS.GW.activeGroup);
+ g->activeWorkplane = he;
+ }
+
+ hEntity createCircle(const Vector &c, const Quaternion &q, double r, hStyle style) {
+ hRequest hr = SS.GW.AddRequest(Request::Type::CIRCLE, /*rememberForUndo=*/false);
SK.GetEntity(hr.entity(1))->PointForceTo(c);
processPoint(hr.entity(1));
+ SK.GetEntity(hr.entity(32))->NormalForceTo(q);
SK.GetEntity(hr.entity(64))->DistanceForceTo(r);
- if(style != 0) {
- Request *r = SK.GetRequest(hr);
- r->style = hStyle{ style };
- }
+ configureRequest(hr, style);
return hr.entity(0);
}
- virtual void addLayer(const DRW_Layer &data) {
+ void addLayer(const DRW_Layer &data) override {
layers.emplace(data.name, data);
}
- virtual void addBlock(const DRW_Block &data) {
+ void addBlock(const DRW_Block &data) override {
readBlock = &blocks[data.name];
readBlock->data = data;
}
- virtual void endBlock() {
+ void endBlock() override {
readBlock = NULL;
}
- virtual void addPoint(const DRW_Point &data) {
+ void addPoint(const DRW_Point &data) override {
if(data.space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_Point>(data)) return;
- hRequest hr = SS.GW.AddRequest(Request::DATUM_POINT, false);
+ hRequest hr = SS.GW.AddRequest(Request::Type::DATUM_POINT, /*rememberForUndo=*/false);
SK.GetEntity(hr.entity(0))->PointForceTo(toVector(data.basePoint));
processPoint(hr.entity(0));
}
- virtual void addLine(const DRW_Line &data) {
+ void addLine(const DRW_Line &data) override {
if(data.space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_Line>(data)) return;
- createLine(toVector(data.basePoint), toVector(data.secPoint), styleFor(&data).v, true);
+ createLine(toVector(data.basePoint), toVector(data.secPoint), styleFor(&data),
+ /*constrainHV=*/true);
}
- virtual void addArc(const DRW_Arc &data) {
+ void addArc(const DRW_Arc &data) override {
if(data.space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_Arc>(data)) return;
- hRequest hr = SS.GW.AddRequest(Request::ARC_OF_CIRCLE, false);
double r = data.radious;
double sa = data.staangle;
double ea = data.endangle;
- Vector c = Vector::From(data.basePoint.x, data.basePoint.y, 0.0);
- Vector rvs = Vector::From(r * cos(sa), r * sin(sa), data.basePoint.z).Plus(c);
- Vector rve = Vector::From(r * cos(ea), r * sin(ea), data.basePoint.z).Plus(c);
+ Vector c = toVector(data.basePoint);
+ Vector nz = toVector(data.extPoint);
+ Quaternion q = NormalFromExtPoint(nz);
+
+ bool planar = q.RotationN().Equals(Vector::From(0, 0, 1));
+ bool onPlane = c.z < LENGTH_EPS;
+
+ hEntity oldWorkplane = SS.GW.ActiveWorkplane();
+ if (!planar || !onPlane) {
+ activateWorkplane(findOrCreateWorkplane(c, q));
+ }
+
+ hRequest hr = SS.GW.AddRequest(Request::Type::ARC_OF_CIRCLE, /*rememberForUndo=*/false);
+ Vector u = q.RotationU(), v = q.RotationV();
+ Vector rvs = c.Plus(u.ScaledBy(r * cos(sa))).Plus(v.ScaledBy(r * sin(sa)));
+ Vector rve = c.Plus(u.ScaledBy(r * cos(ea))).Plus(v.ScaledBy(r * sin(ea)));
if(data.extPoint.z == -1.0) {
c.x = -c.x;
processPoint(hr.entity(1));
processPoint(hr.entity(2));
processPoint(hr.entity(3));
- setStyle(hr, styleFor(&data));
+ configureRequest(hr, styleFor(&data));
+ activateWorkplane(oldWorkplane);
}
- virtual void addCircle(const DRW_Circle &data) {
+ void addCircle(const DRW_Circle &data) override {
if(data.space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_Circle>(data)) return;
- createCircle(toVector(data.basePoint), data.radious, styleFor(&data).v);
+ Vector nz = toVector(data.extPoint);
+ Quaternion normal = NormalFromExtPoint(nz);
+ createCircle(toVector(data.basePoint), normal, data.radious, styleFor(&data));
}
- virtual void addLWPolyline(const DRW_LWPolyline &data) {
+ void addLWPolyline(const DRW_LWPolyline &data) override {
if(data.space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_LWPolyline>(data)) return;
hStyle hs = styleFor(&data);
if(EXACT(data.vertlist[i]->bulge == 0.0)) {
- createLine(blockTransform(p0), blockTransform(p1), hs.v, true);
+ createLine(blockTransform(p0), blockTransform(p1), hs, /*constrainHV=*/true);
} else {
hRequest hr = createBulge(p0, p1, c0.bulge);
- setStyle(hr, hs);
+ configureRequest(hr, hs);
}
}
}
- virtual void addPolyline(const DRW_Polyline &data) {
+ void addPolyline(const DRW_Polyline &data) override {
if(data.space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_Polyline>(data)) return;
- int vNum = data.vertlist.size();
+ size_t vNum = data.vertlist.size();
// Check for closed polyline.
if((data.flags & 1) != 1) vNum--;
// Correct coordinate system for the case where z=-1, as described in
// http://paulbourke.net/dataformats/dxf/dxf10.html.
- bool needSwapX = data.extPoint.z == -1.0;
+ bool needSwapX = (data.extPoint.z == -1.0);
- for(int i = 0; i < vNum; i++) {
+ for(size_t i = 0; i < vNum; i++) {
DRW_Coord c0 = data.vertlist[i]->basePoint;
DRW_Coord c1 = data.vertlist[(i + 1) % data.vertlist.size()]->basePoint;
bulge = -bulge;
}
- Vector p0 = Vector::From(c0.x, c0.y, 0.0);
- Vector p1 = Vector::From(c1.x, c1.y, 0.0);
+ Vector p0 = Vector::From(c0.x, c0.y, c0.z);
+ Vector p1 = Vector::From(c1.x, c1.y, c1.z);
hStyle hs = styleFor(&data);
if(EXACT(bulge == 0.0)) {
- createLine(blockTransform(p0), blockTransform(p1), hs.v, true);
+ createLine(blockTransform(p0), blockTransform(p1), hs, /*constrainHV=*/true);
} else {
hRequest hr = createBulge(p0, p1, bulge);
- setStyle(hr, hs);
+ configureRequest(hr, hs);
}
}
}
- virtual void addSpline(const DRW_Spline *data) {
+ void addSpline(const DRW_Spline *data) override {
if(data->space != DRW::ModelSpace) return;
if(data->degree != 3) return;
if(addPendingBlockEntity<DRW_Spline>(*data)) return;
- hRequest hr = SS.GW.AddRequest(Request::CUBIC, false);
+ hRequest hr = SS.GW.AddRequest(Request::Type::CUBIC, /*rememberForUndo=*/false);
for(int i = 0; i < 4; i++) {
SK.GetEntity(hr.entity(i + 1))->PointForceTo(toVector(*data->controllist[i]));
processPoint(hr.entity(i + 1));
}
- setStyle(hr, styleFor(data));
+ configureRequest(hr, styleFor(data));
}
- virtual void addInsert(const DRW_Insert &data) {
+ void addInsert(const DRW_Insert &data) override {
if(data.space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_Insert>(data)) return;
auto bi = blocks.find(data.name);
- if(bi == blocks.end()) oops();
+ ssassert(bi != blocks.end(), "Inserted block does not exist");
Block *block = &bi->second;
// Push transform.
insertInsert = &data;
if(data.extPoint.z == -1.0) invertXTransform();
- multBlockTransform(data.basePoint.x, data.basePoint.y, data.xscale, data.yscale, data.angle);
+ multBlockTransform(data.basePoint.x, data.basePoint.y, data.xscale, data.yscale,
+ data.angle);
for(auto &e : block->entities) {
addEntity(&*e);
}
blockT = t;
}
- virtual void addMText(const DRW_MText &data) {
+ void addMText(const DRW_MText &data) override {
if(data.space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_MText>(data)) return;
addText(text);
}
- virtual void addText(const DRW_Text &data) {
+ void addText(const DRW_Text &data) override {
if(data.space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_Text>(data)) return;
Constraint c = {};
c.group = SS.GW.activeGroup;
c.workplane = SS.GW.ActiveWorkplane();
- c.type = Constraint::COMMENT;
+ c.type = Constraint::Type::COMMENT;
if(data.alignH == DRW_Text::HLeft && data.alignV == DRW_Text::VBaseLine) {
c.disp.offset = toVector(data.basePoint);
} else {
}
c.comment = data.text;
c.disp.style = styleFor(&data);
- Constraint::AddConstraint(&c, false);
+ Constraint::AddConstraint(&c, /*rememberForUndo=*/false);
}
- virtual void addDimAlign(const DRW_DimAligned *data) {
+ void addDimAlign(const DRW_DimAligned *data) override {
if(data->space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_DimAligned>(*data)) return;
Vector p1 = toVector(data->getDef2Point());
Vector p2 = toVector(data->getTextPoint());
hConstraint hc = Constraint::Constrain(
- Constraint::PT_PT_DISTANCE,
+ Constraint::Type::PT_PT_DISTANCE,
createOrGetPoint(p0),
createOrGetPoint(p1),
Entity::NO_ENTITY
c->disp.offset = p2.Minus(p0.Plus(p1).ScaledBy(0.5));
}
- virtual void addDimLinear(const DRW_DimLinear *data) {
+ void addDimLinear(const DRW_DimLinear *data) override {
if(data->space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_DimLinear>(*data)) return;
- Vector p0 = toVector(data->getDef1Point(), false);
- Vector p1 = toVector(data->getDef2Point(), false);
- Vector p2 = toVector(data->getTextPoint(), false);
+ Vector p0 = toVector(data->getDef1Point(), /*transform=*/false);
+ Vector p1 = toVector(data->getDef2Point(), /*transform=*/false);
+ Vector p2 = toVector(data->getTextPoint(), /*transform=*/false);
double angle = data->getAngle() * PI / 180.0;
Vector dir = Vector::From(cos(angle), sin(angle), 0.0);
p4 = blockTransform(p4);
hConstraint hc = Constraint::Constrain(
- Constraint::PT_LINE_DISTANCE,
+ Constraint::Type::PT_LINE_DISTANCE,
createOrGetPoint(p0),
Entity::NO_ENTITY,
- createLine(p1, p3, invisibleStyle().v)
+ createLine(p1, p3, invisibleStyle())
);
Constraint *c = SK.GetConstraint(hc);
c->disp.offset = p2.Minus(p4);
}
- virtual void addDimAngular(const DRW_DimAngular *data) {
+ void addDimAngular(const DRW_DimAngular *data) override {
if(data->space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_DimAngular>(*data)) return;
Vector l1p1 = toVector(data->getSecondLine2());
hConstraint hc = Constraint::Constrain(
- Constraint::ANGLE,
+ Constraint::Type::ANGLE,
Entity::NO_ENTITY,
Entity::NO_ENTITY,
- createLine(l0p0, l0p1, invisibleStyle().v),
- createLine(l1p1, l1p0, invisibleStyle().v),
+ createLine(l0p0, l0p1, invisibleStyle()),
+ createLine(l1p1, l1p0, invisibleStyle()),
/*other=*/false,
/*other2=*/false
);
}
}
- hConstraint createDiametric(Vector cp, double r, Vector tp, double actual, bool asRadius = false) {
- hEntity he = createCircle(cp, r, invisibleStyle().v);
+ hConstraint createDiametric(Vector cp, Quaternion q, double r, Vector tp,
+ double actual, bool asRadius = false) {
+ hEntity he = createCircle(cp, q, r, invisibleStyle());
hConstraint hc = Constraint::Constrain(
- Constraint::DIAMETER,
+ Constraint::Type::DIAMETER,
Entity::NO_ENTITY,
Entity::NO_ENTITY,
he
return hc;
}
- virtual void addDimRadial(const DRW_DimRadial *data) {
+ void addDimRadial(const DRW_DimRadial *data) override {
if(data->space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_DimRadial>(*data)) return;
actual = data->getActualMeasurement();
}
- createDiametric(cp, cp.Minus(dp).Magnitude(), tp, actual, /*asRadius=*/true);
+ Vector nz = toVector(data->getExtrusion());
+ Quaternion q = NormalFromExtPoint(nz);
+ createDiametric(cp, q, cp.Minus(dp).Magnitude(), tp, actual, /*asRadius=*/true);
}
- virtual void addDimDiametric(const DRW_DimDiametric *data) {
+ void addDimDiametric(const DRW_DimDiametric *data) override {
if(data->space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_DimRadial>(*data)) return;
actual = data->getActualMeasurement();
}
- createDiametric(cp, cp.Minus(dp1).Magnitude(), tp, actual, /*asRadius=*/false);
+ Vector nz = toVector(data->getExtrusion());
+ Quaternion q = NormalFromExtPoint(nz);
+ createDiametric(cp, q, cp.Minus(dp1).Magnitude(), tp, actual, /*asRadius=*/false);
}
- virtual void addDimAngular3P(const DRW_DimAngular3p *data) {
+ void addDimAngular3P(const DRW_DimAngular3p *data) override {
if(data->space != DRW::ModelSpace) return;
if(addPendingBlockEntity<DRW_DimAngular3p>(*data)) return;
}
};
-void ImportDxf(const std::string &filename) {
- DxfReadInterface interface;
- interface.clearBlockTransform();
+class DxfCheck3D : public DRW_Interface {
+public:
+ bool is3d;
- std::string data;
- if(!ReadFile(filename, &data)) {
- Error("Couldn't read from '%s'", filename.c_str());
- return;
+ void addEntity(DRW_Entity *e) {
+ switch(e->eType) {
+ case DRW::POINT:
+ addPoint(*static_cast<DRW_Point *>(e));
+ break;
+ case DRW::LINE:
+ addLine(*static_cast<DRW_Line *>(e));
+ break;
+ case DRW::ARC:
+ addArc(*static_cast<DRW_Arc *>(e));
+ break;
+ case DRW::CIRCLE:
+ addCircle(*static_cast<DRW_Circle *>(e));
+ break;
+ case DRW::POLYLINE:
+ addPolyline(*static_cast<DRW_Polyline *>(e));
+ break;
+ case DRW::LWPOLYLINE:
+ addLWPolyline(*static_cast<DRW_LWPolyline *>(e));
+ break;
+ case DRW::SPLINE:
+ addSpline(static_cast<DRW_Spline *>(e));
+ break;
+ case DRW::INSERT:
+ addInsert(*static_cast<DRW_Insert *>(e));
+ break;
+ case DRW::TEXT:
+ addText(*static_cast<DRW_Text *>(e));
+ break;
+ case DRW::MTEXT:
+ addMText(*static_cast<DRW_MText *>(e));
+ break;
+ case DRW::DIMALIGNED:
+ addDimAlign(static_cast<DRW_DimAligned *>(e));
+ break;
+ case DRW::DIMLINEAR:
+ addDimLinear(static_cast<DRW_DimLinear *>(e));
+ break;
+ case DRW::DIMRADIAL:
+ addDimRadial(static_cast<DRW_DimRadial *>(e));
+ break;
+ case DRW::DIMDIAMETRIC:
+ addDimDiametric(static_cast<DRW_DimDiametric *>(e));
+ break;
+ case DRW::DIMANGULAR:
+ addDimAngular(static_cast<DRW_DimAngular *>(e));
+ break;
+ default:
+ break;
+ }
}
- SS.UndoRemember();
- std::stringstream stream(data);
- if(!dxfRW().read(stream, &interface, /*ext=*/false)) {
- Error("Corrupted DXF file.");
+ void addPoint(const DRW_Point &data) override {
+ if(data.space != DRW::ModelSpace) return;
+ checkCoord(data.basePoint);
}
- if(interface.unknownEntities > 0) {
- Message(ssprintf("%u DXF entities of unknown type were ignored.",
- interface.unknownEntities).c_str());
+ void addLine(const DRW_Line &data) override {
+ if(data.space != DRW::ModelSpace) return;
+ checkCoord(data.basePoint);
+ checkCoord(data.secPoint);
+ }
+
+ void addArc(const DRW_Arc &data) override {
+ if(data.space != DRW::ModelSpace) return;
+ checkCoord(data.basePoint);
+ checkExt(data.extPoint);
+ }
+
+ void addCircle(const DRW_Circle &data) override {
+ if(data.space != DRW::ModelSpace) return;
+ checkCoord(data.basePoint);
+ checkExt(data.extPoint);
+ }
+
+ void addPolyline(const DRW_Polyline &data) override {
+ if(data.space != DRW::ModelSpace) return;
+ for(size_t i = 0; i < data.vertlist.size(); i++) {
+ checkCoord(data.vertlist[i]->basePoint);
+ }
+ }
+
+ void addSpline(const DRW_Spline *data) override {
+ if(data->space != DRW::ModelSpace) return;
+ if(data->degree != 3) return;
+ for(int i = 0; i < 4; i++) {
+ checkCoord(*data->controllist[i]);
+ }
+ }
+
+ void addInsert(const DRW_Insert &data) override {
+ if(data.space != DRW::ModelSpace) return;
+ checkCoord(data.basePoint);
+ }
+
+ void addMText(const DRW_MText &data) override {
+ if(data.space != DRW::ModelSpace) return;
+
+ DRW_MText text = data;
+ text.secPoint = text.basePoint;
+ addText(text);
+ }
+
+ void addText(const DRW_Text &data) override {
+ if(data.space != DRW::ModelSpace) return;
+ checkCoord(data.basePoint);
+ checkCoord(data.secPoint);
+ }
+
+ void addDimAlign(const DRW_DimAligned *data) override {
+ if(data->space != DRW::ModelSpace) return;
+ checkCoord(data->getDef1Point());
+ checkCoord(data->getDef2Point());
+ checkCoord(data->getTextPoint());
}
-}
-void ImportDwg(const std::string &filename) {
- DxfReadInterface interface;
- interface.clearBlockTransform();
+ void addDimLinear(const DRW_DimLinear *data) override {
+ if(data->space != DRW::ModelSpace) return;
+ checkCoord(data->getDef1Point());
+ checkCoord(data->getDef2Point());
+ checkCoord(data->getTextPoint());
+ }
+
+ void addDimAngular(const DRW_DimAngular *data) override {
+ if(data->space != DRW::ModelSpace) return;
+ checkCoord(data->getFirstLine1());
+ checkCoord(data->getFirstLine2());
+ checkCoord(data->getSecondLine1());
+ checkCoord(data->getSecondLine2());
+ checkCoord(data->getTextPoint());
+ }
+
+ void addDimRadial(const DRW_DimRadial *data) override {
+ if(data->space != DRW::ModelSpace) return;
+ checkCoord(data->getCenterPoint());
+ checkCoord(data->getDiameterPoint());
+ checkCoord(data->getTextPoint());
+ checkExt(data->getExtrusion());
+ }
+
+ void addDimDiametric(const DRW_DimDiametric *data) override {
+ if(data->space != DRW::ModelSpace) return;
+ checkCoord(data->getDiameter1Point());
+ checkCoord(data->getDiameter2Point());
+ checkCoord(data->getTextPoint());
+ checkExt(data->getExtrusion());
+ }
+
+ void addDimAngular3P(const DRW_DimAngular3p *data) override {
+ if(data->space != DRW::ModelSpace) return;
+ DRW_DimAngular dim = *static_cast<const DRW_Dimension *>(data);
+
+ dim.setFirstLine1(data->getVertexPoint());
+ dim.setFirstLine2(data->getFirstLine());
+ dim.setSecondLine1(data->getVertexPoint());
+ dim.setSecondLine2(data->getSecondLine());
+ addDimAngular(&dim);
+ }
+
+ void checkCoord(const DRW_Coord &coord) {
+ if(fabs(coord.z) > LENGTH_EPS) {
+ is3d = true;
+ }
+ }
+
+ void checkExt(const DRW_Coord &coord) {
+ if ((fabs(coord.x) > 1/64.) || (fabs(coord.y) > 1/64.)) {
+ is3d = true;
+ }
+ }
+};
+
+static void
+ImportDwgDxf(const Platform::Path &filename,
+ const std::function<bool(const std::string &data, DRW_Interface *intf)> &read) {
+ std::string fileType = ToUpper(filename.Extension());
std::string data;
if(!ReadFile(filename, &data)) {
- Error("Couldn't read from '%s'", filename.c_str());
+ Error("Couldn't read from '%s'", filename.raw.c_str());
return;
}
- SS.UndoRemember();
- std::stringstream stream(data);
- if(!dwgR().read(stream, &interface, /*ext=*/false)) {
- Error("Corrupted DWG file.");
+ bool asConstruction = true;
+ if(SS.GW.LockedInWorkplane()) {
+ DxfCheck3D checker = {};
+ read(data, &checker);
+ if(checker.is3d) {
+ Message("This %s file contains entities with non-zero Z coordinate; "
+ "the entire file will be imported as construction entities in 3d.",
+ fileType.c_str());
+ SS.GW.SetWorkplaneFreeIn3d();
+ SS.GW.EnsureValidActives();
+ } else {
+ asConstruction = false;
+ }
}
- if(interface.unknownEntities > 0) {
- Message(ssprintf("%u DWG entities of unknown type were ignored.",
- interface.unknownEntities).c_str());
+ SS.UndoRemember();
+
+ DxfImport importer = {};
+ importer.asConstruction = asConstruction;
+ importer.clearBlockTransform();
+ if(!read(data, &importer)) {
+ Error("Corrupted %s file.", fileType.c_str());
+ return;
}
+ if(importer.unknownEntities > 0) {
+ Message("%u %s entities of unknown type were ignored.",
+ importer.unknownEntities, fileType.c_str());
+ }
+}
+
+void ImportDxf(const Platform::Path &filename) {
+ ImportDwgDxf(filename, [](const std::string &data, DRW_Interface *intf) {
+ std::stringstream stream(data);
+ return dxfRW().read(stream, intf, /*ext=*/true);
+ });
+}
+
+void ImportDwg(const Platform::Path &filename) {
+ ImportDwgDxf(filename, [](const std::string &data, DRW_Interface *intf) {
+ std::stringstream stream(data);
+ return dwgR().read(stream, intf, /*ext=*/true);
+ });
}
}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Intermediate Data Format (IDF) file reader. Reads an IDF file for PCB outlines and creates
+// an equivalent SovleSpace sketch/extrusion. Supports only Linking, not import.
+// Part placement is not currently supported.
+//
+// Copyright 2020 Paul Kahler.
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+#include "sketch.h"
+
+// Split a string into substrings separated by spaces.
+// Allow quotes to enclose spaces within a string
+static std::vector <std::string> splitString(const std::string line) {
+ std::vector <std::string> v = {};
+
+ if(line.length() == 0) return v;
+
+ std::string s = "";
+ bool inString = false;
+ bool inQuotes = false;
+
+ for (size_t i=0; i<line.length(); i++) {
+ char c = line.at(i);
+ if (inQuotes) {
+ if (c != '"') {
+ s.push_back(c);
+ } else {
+ v.push_back(s);
+ inQuotes = false;
+ inString = false;
+ s = "";
+ }
+ } else if (inString) {
+ if (c != ' ') {
+ s.push_back(c);
+ } else {
+ v.push_back(s);
+ inString = false;
+ s = "";
+ }
+ } else if(c == '"') {
+ inString = true;
+ inQuotes = true;
+ } else if(c != ' ') {
+ s = "";
+ s.push_back(c);
+ inString = true;
+ }
+ }
+ if(s.length() > 0)
+ v.push_back(s);
+
+ return v;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Functions for linking an IDF file - we need to create entites that
+// get remapped into a linked group similar to linking .slvs files
+//////////////////////////////////////////////////////////////////////////////
+
+// Make a new point - type doesn't matter since we will make a copy later
+static hEntity newPoint(EntityList *el, int *id, Vector p, bool visible = true) {
+ Entity en = {};
+ en.type = Entity::Type::POINT_N_COPY;
+ en.extraPoints = 0;
+ en.timesApplied = 0;
+ en.group.v = 462;
+ en.actPoint = p;
+ en.construction = false;
+ en.style.v = Style::DATUM;
+ en.actVisible = visible;
+ en.forceHidden = false;
+
+ *id = *id+1;
+ en.h.v = *id + en.group.v*65536;
+ el->Add(&en);
+ return en.h;
+}
+
+static hEntity newLine(EntityList *el, int *id, hEntity p0, hEntity p1) {
+ Entity en = {};
+ en.type = Entity::Type::LINE_SEGMENT;
+ en.point[0] = p0;
+ en.point[1] = p1;
+ en.extraPoints = 0;
+ en.timesApplied = 0;
+ en.group.v = 493;
+ en.construction = false;
+ en.style.v = Style::ACTIVE_GRP;
+ en.actVisible = true;
+ en.forceHidden = false;
+
+ *id = *id+1;
+ en.h.v = *id + en.group.v*65536;
+ el->Add(&en);
+ return en.h;
+}
+
+static hEntity newNormal(EntityList *el, int *id, Quaternion normal) {
+ // normals have parameters, but we don't need them to make a NORMAL_N_COPY from this
+ Entity en = {};
+ en.type = Entity::Type::NORMAL_N_COPY;
+ en.extraPoints = 0;
+ en.timesApplied = 0;
+ en.group.v = 472;
+ en.actNormal = normal;
+ en.construction = false;
+ en.style.v = Style::ACTIVE_GRP;
+ // to be visible we need to add a point.
+ en.point[0] = newPoint(el, id, Vector::From(0,0,3), /*visible=*/ true);
+ en.actVisible = true;
+ en.forceHidden = false;
+
+ *id = *id+1;
+ en.h.v = *id + en.group.v*65536;
+ el->Add(&en);
+ return en.h;
+}
+
+static hEntity newArc(EntityList *el, int *id, hEntity p0, hEntity p1, hEntity pc, hEntity hnorm) {
+ Entity en = {};
+ en.type = Entity::Type::ARC_OF_CIRCLE;
+ en.point[0] = pc;
+ en.point[1] = p0;
+ en.point[2] = p1;
+ en.normal = hnorm;
+ en.extraPoints = 0;
+ en.timesApplied = 0;
+ en.group.v = 403;
+ en.construction = false;
+ en.style.v = Style::ACTIVE_GRP;
+ en.actVisible = true;
+ en.forceHidden = false; *id = *id+1;
+
+ *id = *id + 1;
+ en.h.v = *id + en.group.v*65536;
+ el->Add(&en);
+ return en.h;
+}
+
+static hEntity newDistance(EntityList *el, int *id, double distance) {
+ // normals have parameters, but we don't need them to make a NORMAL_N_COPY from this
+ Entity en = {};
+ en.type = Entity::Type::DISTANCE;
+ en.extraPoints = 0;
+ en.timesApplied = 0;
+ en.group.v = 472;
+ en.actDistance = distance;
+ en.construction = false;
+ en.style.v = Style::ACTIVE_GRP;
+ // to be visible we'll need to add a point?
+ en.actVisible = false;
+ en.forceHidden = false;
+
+ *id = *id+1;
+ en.h.v = *id + en.group.v*65536;
+ el->Add(&en);
+ return en.h;
+}
+
+static hEntity newCircle(EntityList *el, int *id, hEntity p0, hEntity hdist, hEntity hnorm) {
+ Entity en = {};
+ en.type = Entity::Type::CIRCLE;
+ en.point[0] = p0;
+ en.normal = hnorm;
+ en.distance = hdist;
+ en.extraPoints = 0;
+ en.timesApplied = 0;
+ en.group.v = 399;
+ en.construction = false;
+ en.style.v = Style::ACTIVE_GRP;
+ en.actVisible = true;
+ en.forceHidden = false;
+
+ *id = *id+1;
+ en.h.v = *id + en.group.v*65536;
+ el->Add(&en);
+ return en.h;
+}
+
+static Vector ArcCenter(Vector p0, Vector p1, double angle) {
+ // locate the center of an arc
+ Vector m = p0.Plus(p1).ScaledBy(0.5);
+ Vector perp = Vector::From(p1.y-p0.y, p0.x-p1.x, 0.0).WithMagnitude(1.0);
+ double dist = 0;
+ if (angle != 180) {
+ dist = (p1.Minus(m).Magnitude())/tan(0.5*angle*3.141592653589793/180.0);
+ } else {
+ dist = 0.0;
+ }
+ Vector c = m.Minus(perp.ScaledBy(dist));
+ return c;
+}
+
+// Add an IDF line or arc to the entity list. According to spec, zero angle indicates a line.
+// Positive angles are counter clockwise, negative are clockwise. An angle of 360
+// indicates a circle centered at x1,y1 passing through x2,y2 and is a complete loop.
+static void CreateEntity(EntityList *el, int *id, hEntity h0, hEntity h1, hEntity hnorm,
+ Vector p0, Vector p1, double angle) {
+ if (angle == 0.0) {
+ //line
+ if(p0.Equals(p1)) return;
+
+ newLine(el, id, h0, h1);
+
+ } else if(angle == 360.0) {
+ // circle
+ double d = p1.Minus(p0).Magnitude();
+ hEntity hd = newDistance(el, id, d);
+ newCircle(el, id, h1, hd, hnorm);
+
+ } else {
+ // arc
+ if(angle < 0.0) {
+ swap(p0,p1);
+ swap(h0,h1);
+ }
+ // locate the center of the arc
+ Vector m = p0.Plus(p1).ScaledBy(0.5);
+ Vector perp = Vector::From(p1.y-p0.y, p0.x-p1.x, 0.0).WithMagnitude(1.0);
+ double dist = 0;
+ if (angle != 180) {
+ dist = (p1.Minus(m).Magnitude())/tan(0.5*angle*3.141592653589793/180.0);
+ } else {
+ dist = 0.0;
+ }
+ Vector c = m.Minus(perp.ScaledBy(dist));
+ hEntity hc = newPoint(el, id, c, /*visible=*/false);
+ newArc(el, id, h0, h1, hc, hnorm);
+ }
+}
+
+// borrowed from Entity::GenerateBezierCurves because we don't have parameters.
+static void MakeBeziersForArcs(SBezierList *sbl, Vector center, Vector pa, Vector pb,
+ Quaternion q, double angle) {
+
+ Vector u = q.RotationU(), v = q.RotationV();
+ double r = pa.Minus(center).Magnitude();
+ double theta, dtheta;
+
+ if(angle == 360.0) {
+ theta = 0;
+ } else {
+ Point2d c2 = center.Project2d(u, v);
+ Point2d pa2 = (pa.Project2d(u, v)).Minus(c2);
+
+ theta = atan2(pa2.y, pa2.x);
+ }
+ dtheta = angle * PI/180;
+
+ int i, n;
+ if(dtheta > (3*PI/2 + 0.01)) {
+ n = 4;
+ } else if(dtheta > (PI + 0.01)) {
+ n = 3;
+ } else if(dtheta > (PI/2 + 0.01)) {
+ n = 2;
+ } else {
+ n = 1;
+ }
+ dtheta /= n;
+
+ for(i = 0; i < n; i++) {
+ double s, c;
+
+ c = cos(theta);
+ s = sin(theta);
+ // The start point of the curve, and the tangent vector at
+ // that start point.
+ Vector p0 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
+ t0 = u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
+
+ theta += dtheta;
+
+ c = cos(theta);
+ s = sin(theta);
+ Vector p2 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
+ t2 = u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
+
+ // The control point must lie on both tangents.
+ Vector p1 = Vector::AtIntersectionOfLines(p0, p0.Plus(t0),
+ p2, p2.Plus(t2),
+ NULL);
+
+ SBezier sb = SBezier::From(p0, p1, p2);
+ sb.weight[1] = cos(dtheta/2);
+ sbl->l.Add(&sb);
+ }
+}
+
+namespace SolveSpace {
+
+// Here we read the important section of an IDF file. SolveSpace Entities are directly created by
+// the funcions above, which is only OK because of the way linking works. For example points do
+// not have handles for solver parameters (coordinates), they only have their actPoint values
+// set (or actNormal or actDistance). These are incompete entites and would be a problem if
+// they were part of the sketch, but they are not. After making a list of them here, a new group
+// gets created from copies of these. Those copies are complete and part of the sketch group.
+bool LinkIDF(const Platform::Path &filename, EntityList *el, SMesh *m, SShell *sh) {
+ dbp("\nLink IDF board outline.");
+ el->Clear();
+ std::string data;
+ if(!ReadFile(filename, &data)) {
+ Error("Couldn't read from '%s'", filename.raw.c_str());
+ return false;
+ }
+
+ enum IDF_SECTION {
+ none,
+ header,
+ board_outline,
+ other_outline,
+ routing_outline,
+ placement_outline,
+ routing_keepout,
+ via_keepout,
+ placement_group,
+ drilled_holes,
+ notes,
+ component_placement
+ } section;
+
+ section = IDF_SECTION::none;
+ int record_number = 0;
+ int curve = -1;
+ int entityCount = 0;
+
+ hEntity hprev;
+ hEntity hprevTop;
+ Vector pprev = Vector::From(0,0,0);
+ Vector pprevTop = Vector::From(0,0,0);
+
+ double board_thickness = 10.0;
+ double scale = 1.0; //mm
+ bool topEntities, bottomEntities;
+
+ Quaternion normal = Quaternion::From(Vector::From(1,0,0), Vector::From(0,1,0));
+ hEntity hnorm = newNormal(el, &entityCount, normal);
+
+ // to create the extursion we will need to collect a set of bezier curves defined
+ // by the perimeter, cutouts, and holes.
+ SBezierList sbl = {};
+
+ std::stringstream stream(data);
+ for(std::string line; getline( stream, line ); ) {
+ if (line.find(".END_") == 0) {
+ section = none;
+ curve = -1;
+ }
+ switch (section) {
+ case none:
+ if(line.find(".HEADER") == 0) {
+ section = header;
+ record_number = 1;
+ } else if (line.find(".BOARD_OUTLINE") == 0) {
+ section = board_outline;
+ record_number = 1;
+// no keepouts for now - they should also be shown as construction?
+// } else if (line.find(".ROUTE_KEEPOUT") == 0) {
+// section = routing_keepout;
+// record_number = 1;
+ } else if(line.find(".DRILLED_HOLES") == 0) {
+ section = drilled_holes;
+ record_number = 1;
+ }
+ break;
+
+ case header:
+ if(record_number == 3) {
+ if(line.find("MM") != std::string::npos) {
+ dbp("IDF units are MM");
+ scale = 1.0;
+ } else if(line.find("THOU") != std::string::npos) {
+ dbp("IDF units are thousandths of an inch");
+ scale = 0.0254;
+ } else {
+ dbp("IDF import, no units found in file.");
+ }
+ }
+ break;
+
+ case routing_keepout:
+ case board_outline:
+ if (record_number == 2) {
+ if(section == board_outline) {
+ topEntities = true;
+ bottomEntities = true;
+ board_thickness = std::stod(line) * scale;
+ dbp("IDF board thickness: %lf", board_thickness);
+ } else if (section == routing_keepout) {
+ topEntities = false;
+ bottomEntities = false;
+ if(line.find("TOP") == 0 || line.find("BOTH") == 0)
+ topEntities = true;
+ if(line.find("BOTTOM") == 0 || line.find("BOTH") == 0)
+ bottomEntities = true;
+ }
+ } else { // records 3+ are lines, arcs, and circles
+ std::vector <std::string> values = splitString(line);
+ if(values.size() != 4) continue;
+ int c = stoi(values[0]);
+ double x = stof(values[1]);
+ double y = stof(values[2]);
+ double ang = stof(values[3]);
+ Vector point = Vector::From(x,y,0.0);
+ Vector pTop = Vector::From(x,y,board_thickness);
+ if(c != curve) { // start a new curve
+ curve = c;
+ if (bottomEntities)
+ hprev = newPoint(el, &entityCount, point, /*visible=*/false);
+ if (topEntities)
+ hprevTop = newPoint(el, &entityCount, pTop, /*visible=*/false);
+ pprev = point;
+ pprevTop = pTop;
+ } else {
+ if(section == board_outline) {
+ // create a bezier for the extrusion
+ if (ang == 0) {
+ // straight lines
+ SBezier sb = SBezier::From(pprev, point);
+ sbl.l.Add(&sb);
+ } else if (ang != 360.0) {
+ // Arcs
+ Vector c = ArcCenter(pprev, point, ang);
+ MakeBeziersForArcs(&sbl, c, pprev, point, normal, ang);
+ } else {
+ // circles
+ MakeBeziersForArcs(&sbl, point, pprev, pprev, normal, ang);
+ }
+ }
+ // next create the entities
+ // only curves and points at circle centers will be visible
+ bool vis = (ang == 360.0);
+ if (bottomEntities) {
+ hEntity hp = newPoint(el, &entityCount, point, /*visible=*/vis);
+ CreateEntity(el, &entityCount, hprev, hp, hnorm, pprev, point, ang);
+ pprev = point;
+ hprev = hp;
+ }
+ if (topEntities) {
+ hEntity hp = newPoint(el, &entityCount, pTop, /*visible=*/vis);
+ CreateEntity(el, &entityCount, hprevTop, hp, hnorm, pprevTop, pTop, ang);
+ pprevTop = pTop;
+ hprevTop = hp;
+ }
+ }
+ }
+ break;
+
+ case other_outline:
+ case routing_outline:
+ case placement_outline:
+ case via_keepout:
+ case placement_group:
+ break;
+
+ case drilled_holes: {
+ std::vector <std::string> values = splitString(line);
+ if(values.size() < 6) continue;
+ double d = stof(values[0]);
+ double x = stof(values[1]);
+ double y = stof(values[2]);
+ // Only show holes likely to be useful in MCAD to reduce complexity.
+ if((d > 1.7) || (values[5].compare(0,3,"PIN") == 0)
+ || (values[5].compare(0,3,"MTG") == 0)) {
+ // create the entity
+ Vector cent = Vector::From(x,y,0.0);
+ hEntity hcent = newPoint(el, &entityCount, cent);
+ hEntity hdist = newDistance(el, &entityCount, d/2);
+ newCircle(el, &entityCount, hcent, hdist, hnorm);
+ // and again for the top
+ Vector cTop = Vector::From(x,y,board_thickness);
+ hcent = newPoint(el, &entityCount, cTop);
+ hdist = newDistance(el, &entityCount, d/2);
+ newCircle(el, &entityCount, hcent, hdist, hnorm);
+ // create the curves for the extrusion
+ Vector pt = Vector::From(x+d/2, y, 0.0);
+ MakeBeziersForArcs(&sbl, cent, pt, pt, normal, 360.0);
+ }
+
+ break;
+ }
+ case notes:
+ case component_placement:
+ break;
+
+ default:
+ section = none;
+ break;
+ }
+ record_number++;
+ }
+ // now we can create an extrusion from all the Bezier curves. We can skip things
+ // like checking for a coplanar sketch because everything is at z=0.
+ SPolygon polyLoops = {};
+ bool allClosed;
+ bool allCoplanar;
+ Vector errorPointAt = Vector::From(0,0,0);
+ SEdge errorAt = {};
+
+ SBezierLoopSetSet sblss = {};
+ sblss.FindOuterFacesFrom(&sbl, &polyLoops, NULL,
+ 100.0, &allClosed, &errorAt,
+ &allCoplanar, &errorPointAt, NULL);
+
+ //hack for when there is no sketch yet and the first group is a linked IDF
+ double ctc = SS.chordTolCalculated;
+ if(ctc == 0.0) SS.chordTolCalculated = 0.1; //mm
+ // there should only by one sbls in the sblss unless a board has disjointed parts...
+ sh->MakeFromExtrusionOf(sblss.l.First(), Vector::From(0.0, 0.0, 0.0),
+ Vector::From(0.0, 0.0, board_thickness),
+ RgbaColor::From(0, 180, 0) );
+ SS.chordTolCalculated = ctc;
+ sblss.Clear();
+ sbl.Clear();
+ sh->booleanFailed = false;
+
+ return true;
+}
+
+}
Sketch SolveSpace::SK = {};
static System SYS;
-static int IsInit = 0;
-
-void Group::GenerateEquations(IdList<Equation,hEquation> *) {
- // Nothing to do for now.
-}
-
-void SolveSpace::CnfFreezeInt(uint32_t, const std::string &)
-{
- abort();
-}
-
-uint32_t SolveSpace::CnfThawInt(uint32_t, const std::string &)
-{
+void SolveSpace::Platform::FatalError(const std::string &message) {
+ fprintf(stderr, "%s", message.c_str());
abort();
- return 0;
}
-void SolveSpace::DoMessageBox(const char *, int, int, bool)
-{
- abort();
+void Group::GenerateEquations(IdList<Equation,hEquation> *) {
+ // Nothing to do for now.
}
extern "C" {
void Slvs_Solve(Slvs_System *ssys, Slvs_hGroup shg)
{
- if(!IsInit) {
- InitHeaps();
- IsInit = 1;
- }
-
int i;
for(i = 0; i < ssys->params; i++) {
Slvs_Param *sp = &(ssys->param[i]);
EntityBase e = {};
switch(se->type) {
-case SLVS_E_POINT_IN_3D: e.type = Entity::POINT_IN_3D; break;
-case SLVS_E_POINT_IN_2D: e.type = Entity::POINT_IN_2D; break;
-case SLVS_E_NORMAL_IN_3D: e.type = Entity::NORMAL_IN_3D; break;
-case SLVS_E_NORMAL_IN_2D: e.type = Entity::NORMAL_IN_2D; break;
-case SLVS_E_DISTANCE: e.type = Entity::DISTANCE; break;
-case SLVS_E_WORKPLANE: e.type = Entity::WORKPLANE; break;
-case SLVS_E_LINE_SEGMENT: e.type = Entity::LINE_SEGMENT; break;
-case SLVS_E_CUBIC: e.type = Entity::CUBIC; break;
-case SLVS_E_CIRCLE: e.type = Entity::CIRCLE; break;
-case SLVS_E_ARC_OF_CIRCLE: e.type = Entity::ARC_OF_CIRCLE; break;
+case SLVS_E_POINT_IN_3D: e.type = Entity::Type::POINT_IN_3D; break;
+case SLVS_E_POINT_IN_2D: e.type = Entity::Type::POINT_IN_2D; break;
+case SLVS_E_NORMAL_IN_3D: e.type = Entity::Type::NORMAL_IN_3D; break;
+case SLVS_E_NORMAL_IN_2D: e.type = Entity::Type::NORMAL_IN_2D; break;
+case SLVS_E_DISTANCE: e.type = Entity::Type::DISTANCE; break;
+case SLVS_E_WORKPLANE: e.type = Entity::Type::WORKPLANE; break;
+case SLVS_E_LINE_SEGMENT: e.type = Entity::Type::LINE_SEGMENT; break;
+case SLVS_E_CUBIC: e.type = Entity::Type::CUBIC; break;
+case SLVS_E_CIRCLE: e.type = Entity::Type::CIRCLE; break;
+case SLVS_E_ARC_OF_CIRCLE: e.type = Entity::Type::ARC_OF_CIRCLE; break;
default: dbp("bad entity type %d", se->type); return;
}
SK.entity.Add(&e);
}
-
+ IdList<Param, hParam> params = {};
for(i = 0; i < ssys->constraints; i++) {
Slvs_Constraint *sc = &(ssys->constraint[i]);
ConstraintBase c = {};
- int t;
+ Constraint::Type t;
switch(sc->type) {
-case SLVS_C_POINTS_COINCIDENT: t = Constraint::POINTS_COINCIDENT; break;
-case SLVS_C_PT_PT_DISTANCE: t = Constraint::PT_PT_DISTANCE; break;
-case SLVS_C_PT_PLANE_DISTANCE: t = Constraint::PT_PLANE_DISTANCE; break;
-case SLVS_C_PT_LINE_DISTANCE: t = Constraint::PT_LINE_DISTANCE; break;
-case SLVS_C_PT_FACE_DISTANCE: t = Constraint::PT_FACE_DISTANCE; break;
-case SLVS_C_PT_IN_PLANE: t = Constraint::PT_IN_PLANE; break;
-case SLVS_C_PT_ON_LINE: t = Constraint::PT_ON_LINE; break;
-case SLVS_C_PT_ON_FACE: t = Constraint::PT_ON_FACE; break;
-case SLVS_C_EQUAL_LENGTH_LINES: t = Constraint::EQUAL_LENGTH_LINES; break;
-case SLVS_C_LENGTH_RATIO: t = Constraint::LENGTH_RATIO; break;
-case SLVS_C_EQ_LEN_PT_LINE_D: t = Constraint::EQ_LEN_PT_LINE_D; break;
-case SLVS_C_EQ_PT_LN_DISTANCES: t = Constraint::EQ_PT_LN_DISTANCES; break;
-case SLVS_C_EQUAL_ANGLE: t = Constraint::EQUAL_ANGLE; break;
-case SLVS_C_EQUAL_LINE_ARC_LEN: t = Constraint::EQUAL_LINE_ARC_LEN; break;
-case SLVS_C_LENGTH_DIFFERENCE: t = Constraint::LENGTH_DIFFERENCE; break;
-case SLVS_C_SYMMETRIC: t = Constraint::SYMMETRIC; break;
-case SLVS_C_SYMMETRIC_HORIZ: t = Constraint::SYMMETRIC_HORIZ; break;
-case SLVS_C_SYMMETRIC_VERT: t = Constraint::SYMMETRIC_VERT; break;
-case SLVS_C_SYMMETRIC_LINE: t = Constraint::SYMMETRIC_LINE; break;
-case SLVS_C_AT_MIDPOINT: t = Constraint::AT_MIDPOINT; break;
-case SLVS_C_HORIZONTAL: t = Constraint::HORIZONTAL; break;
-case SLVS_C_VERTICAL: t = Constraint::VERTICAL; break;
-case SLVS_C_DIAMETER: t = Constraint::DIAMETER; break;
-case SLVS_C_PT_ON_CIRCLE: t = Constraint::PT_ON_CIRCLE; break;
-case SLVS_C_SAME_ORIENTATION: t = Constraint::SAME_ORIENTATION; break;
-case SLVS_C_ANGLE: t = Constraint::ANGLE; break;
-case SLVS_C_PARALLEL: t = Constraint::PARALLEL; break;
-case SLVS_C_PERPENDICULAR: t = Constraint::PERPENDICULAR; break;
-case SLVS_C_ARC_LINE_TANGENT: t = Constraint::ARC_LINE_TANGENT; break;
-case SLVS_C_CUBIC_LINE_TANGENT: t = Constraint::CUBIC_LINE_TANGENT; break;
-case SLVS_C_EQUAL_RADIUS: t = Constraint::EQUAL_RADIUS; break;
-case SLVS_C_PROJ_PT_DISTANCE: t = Constraint::PROJ_PT_DISTANCE; break;
-case SLVS_C_WHERE_DRAGGED: t = Constraint::WHERE_DRAGGED; break;
-case SLVS_C_CURVE_CURVE_TANGENT:t = Constraint::CURVE_CURVE_TANGENT; break;
+case SLVS_C_POINTS_COINCIDENT: t = Constraint::Type::POINTS_COINCIDENT; break;
+case SLVS_C_PT_PT_DISTANCE: t = Constraint::Type::PT_PT_DISTANCE; break;
+case SLVS_C_PT_PLANE_DISTANCE: t = Constraint::Type::PT_PLANE_DISTANCE; break;
+case SLVS_C_PT_LINE_DISTANCE: t = Constraint::Type::PT_LINE_DISTANCE; break;
+case SLVS_C_PT_FACE_DISTANCE: t = Constraint::Type::PT_FACE_DISTANCE; break;
+case SLVS_C_PT_IN_PLANE: t = Constraint::Type::PT_IN_PLANE; break;
+case SLVS_C_PT_ON_LINE: t = Constraint::Type::PT_ON_LINE; break;
+case SLVS_C_PT_ON_FACE: t = Constraint::Type::PT_ON_FACE; break;
+case SLVS_C_EQUAL_LENGTH_LINES: t = Constraint::Type::EQUAL_LENGTH_LINES; break;
+case SLVS_C_LENGTH_RATIO: t = Constraint::Type::LENGTH_RATIO; break;
+case SLVS_C_EQ_LEN_PT_LINE_D: t = Constraint::Type::EQ_LEN_PT_LINE_D; break;
+case SLVS_C_EQ_PT_LN_DISTANCES: t = Constraint::Type::EQ_PT_LN_DISTANCES; break;
+case SLVS_C_EQUAL_ANGLE: t = Constraint::Type::EQUAL_ANGLE; break;
+case SLVS_C_EQUAL_LINE_ARC_LEN: t = Constraint::Type::EQUAL_LINE_ARC_LEN; break;
+case SLVS_C_LENGTH_DIFFERENCE: t = Constraint::Type::LENGTH_DIFFERENCE; break;
+case SLVS_C_SYMMETRIC: t = Constraint::Type::SYMMETRIC; break;
+case SLVS_C_SYMMETRIC_HORIZ: t = Constraint::Type::SYMMETRIC_HORIZ; break;
+case SLVS_C_SYMMETRIC_VERT: t = Constraint::Type::SYMMETRIC_VERT; break;
+case SLVS_C_SYMMETRIC_LINE: t = Constraint::Type::SYMMETRIC_LINE; break;
+case SLVS_C_AT_MIDPOINT: t = Constraint::Type::AT_MIDPOINT; break;
+case SLVS_C_HORIZONTAL: t = Constraint::Type::HORIZONTAL; break;
+case SLVS_C_VERTICAL: t = Constraint::Type::VERTICAL; break;
+case SLVS_C_DIAMETER: t = Constraint::Type::DIAMETER; break;
+case SLVS_C_PT_ON_CIRCLE: t = Constraint::Type::PT_ON_CIRCLE; break;
+case SLVS_C_SAME_ORIENTATION: t = Constraint::Type::SAME_ORIENTATION; break;
+case SLVS_C_ANGLE: t = Constraint::Type::ANGLE; break;
+case SLVS_C_PARALLEL: t = Constraint::Type::PARALLEL; break;
+case SLVS_C_PERPENDICULAR: t = Constraint::Type::PERPENDICULAR; break;
+case SLVS_C_ARC_LINE_TANGENT: t = Constraint::Type::ARC_LINE_TANGENT; break;
+case SLVS_C_CUBIC_LINE_TANGENT: t = Constraint::Type::CUBIC_LINE_TANGENT; break;
+case SLVS_C_EQUAL_RADIUS: t = Constraint::Type::EQUAL_RADIUS; break;
+case SLVS_C_PROJ_PT_DISTANCE: t = Constraint::Type::PROJ_PT_DISTANCE; break;
+case SLVS_C_WHERE_DRAGGED: t = Constraint::Type::WHERE_DRAGGED; break;
+case SLVS_C_CURVE_CURVE_TANGENT:t = Constraint::Type::CURVE_CURVE_TANGENT; break;
default: dbp("bad constraint type %d", sc->type); return;
}
c.other = (sc->other) ? true : false;
c.other2 = (sc->other2) ? true : false;
+ c.Generate(¶ms);
+ if(!params.IsEmpty()) {
+ for(Param &p : params) {
+ p.h = SK.param.AddAndAssignId(&p);
+ c.valP = p.h;
+ SYS.param.Add(&p);
+ }
+ params.Clear();
+ c.ModifyToSatisfy();
+ }
+
SK.constraint.Add(&c);
}
// Now we're finally ready to solve!
bool andFindBad = ssys->calculateFaileds ? true : false;
- int how = SYS.Solve(&g, &(ssys->dof), &bad, andFindBad, false);
+ SolveResult how = SYS.Solve(&g, NULL, &(ssys->dof), &bad, andFindBad, /*andFindFree=*/false);
switch(how) {
- case System::SOLVED_OKAY:
+ case SolveResult::OKAY:
ssys->result = SLVS_RESULT_OKAY;
break;
- case System::DIDNT_CONVERGE:
+ case SolveResult::DIDNT_CONVERGE:
ssys->result = SLVS_RESULT_DIDNT_CONVERGE;
break;
- case System::REDUNDANT_DIDNT_CONVERGE:
- case System::REDUNDANT_OKAY:
+ case SolveResult::REDUNDANT_DIDNT_CONVERGE:
+ case SolveResult::REDUNDANT_OKAY:
ssys->result = SLVS_RESULT_INCONSISTENT;
break;
- case System::TOO_MANY_UNKNOWNS:
+ case SolveResult::TOO_MANY_UNKNOWNS:
ssys->result = SLVS_RESULT_TOO_MANY_UNKNOWNS;
break;
-
- default: oops();
}
// Write the new parameter values back to our caller.
if(ssys->failed) {
// Copy over any the list of problematic constraints.
for(i = 0; i < ssys->faileds && i < bad.n; i++) {
- ssys->failed[i] = bad.elem[i].v;
+ ssys->failed[i] = bad[i].v;
}
ssys->faileds = bad.n;
}
#include <set>
-void SMesh::Clear(void) {
+void SMesh::Clear() {
l.Clear();
}
t.c = c;
AddTriangle(&t);
}
-void SMesh::AddTriangle(STriangle *st) {
- RgbaColor color = st->meta.color;
- if(color.ToARGB32() != 0 && color.alpha != 255) isTransparent = true;
+void SMesh::AddTriangle(const STriangle *st) {
l.Add(st);
}
-void SMesh::DoBounding(Vector v, Vector *vmax, Vector *vmin) {
+void SMesh::DoBounding(Vector v, Vector *vmax, Vector *vmin) const {
vmax->x = max(vmax->x, v.x);
vmax->y = max(vmax->y, v.y);
vmax->z = max(vmax->z, v.z);
vmin->y = min(vmin->y, v.y);
vmin->z = min(vmin->z, v.z);
}
-void SMesh::GetBounding(Vector *vmax, Vector *vmin) {
+void SMesh::GetBounding(Vector *vmax, Vector *vmin) const {
int i;
*vmin = Vector::From( 1e12, 1e12, 1e12);
*vmax = Vector::From(-1e12, -1e12, -1e12);
for(i = 0; i < l.n; i++) {
- STriangle *st = &(l.elem[i]);
+ const STriangle *st = &(l[i]);
DoBounding(st->a, vmax, vmin);
DoBounding(st->b, vmax, vmin);
DoBounding(st->c, vmax, vmin);
m.l.ClearTags();
int i;
for(i = 0; i < m.l.n; i++) {
- STriangle *tr = &(m.l.elem[i]);
+ STriangle *tr = &(m.l[i]);
if((fabs(n.Dot(tr->a) - d) >= LENGTH_EPS) ||
(fabs(n.Dot(tr->b) - d) >= LENGTH_EPS) ||
// Select the naked edges in our resulting open mesh.
SKdNode *root = SKdNode::From(&m);
root->SnapToMesh(&m);
- root->MakeCertainEdgesInto(sel, SKdNode::NAKED_OR_SELF_INTER_EDGES,
- false, NULL, NULL);
+ root->MakeCertainEdgesInto(sel, EdgeKind::NAKED_OR_SELF_INTER,
+ /*coplanarIsInter=*/false, NULL, NULL);
m.Clear();
}
-void SMesh::MakeCertainEdgesAndOutlinesInto(SEdgeList *sel, SOutlineList *sol, int type) {
+void SMesh::MakeOutlinesInto(SOutlineList *sol, EdgeKind edgeKind) {
SKdNode *root = SKdNode::From(this);
- root->MakeCertainEdgesInto(sel, type, false, NULL, NULL);
- root->MakeOutlinesInto(sol);
+ root->MakeOutlinesInto(sol, edgeKind);
}
//-----------------------------------------------------------------------------
-// When we are called, all of the triangles from l.elem[start] to the end must
+// When we are called, all of the triangles from l[start] to the end must
// be coplanar. So we try to find a set of fewer triangles that covers the
// exact same area, in order to reduce the number of triangles in the mesh.
// We use this after a triangle has been split against the BSP.
void SMesh::Simplify(int start) {
int maxTriangles = (l.n - start) + 10;
- STriMeta meta = l.elem[start].meta;
+ STriMeta meta = l[start].meta;
- STriangle *tout = (STriangle *)AllocTemporary(maxTriangles*sizeof(*tout));
+ STriangle *tout = new STriangle[maxTriangles];
int toutc = 0;
Vector n = Vector::From(0, 0, 0);
- Vector *conv = (Vector *)AllocTemporary(maxTriangles*3*sizeof(*conv));
+ Vector *conv = new Vector[maxTriangles * 3];
int convc = 0;
int start0 = start;
int i, j;
for(i = start; i < l.n; i++) {
- STriangle *tr = &(l.elem[i]);
+ STriangle *tr = &(l[i]);
if(tr->MinAltitude() < LENGTH_EPS) {
tr->tag = 1;
} else {
bool didAdd;
convc = 0;
for(i = start; i < l.n; i++) {
- STriangle *tr = &(l.elem[i]);
+ STriangle *tr = &(l[i]);
if(tr->tag) continue;
tr->tag = 1;
Vector c;
for(i = start; i < l.n; i++) {
- STriangle *tr = &(l.elem[i]);
+ STriangle *tr = &(l[i]);
if(tr->tag) continue;
if((tr->a).Equals(d) && (tr->b).Equals(b)) {
if(fabs(bDot) < LENGTH_EPS && fabs(dDot) < LENGTH_EPS) {
conv[WRAP((j+1), convc)] = c;
// and remove the vertex at j, which is a dup
- memmove(conv+j, conv+j+1,
- (convc - j - 1)*sizeof(conv[0]));
+ std::move(conv+j+1, conv+convc, conv+j);
convc--;
} else if(fabs(bDot) < LENGTH_EPS && dDot > 0) {
conv[j] = c;
conv[WRAP((j+1), convc)] = c;
} else if(bDot > 0 && dDot > 0) {
// conv[j] is unchanged, conv[j+1] goes to [j+2]
- memmove(conv+j+2, conv+j+1,
- (convc - j - 1)*sizeof(conv[0]));
+ std::move_backward(conv+j+1, conv+convc, conv+convc+1);
conv[j+1] = c;
convc++;
} else {
for(i = 0; i < toutc; i++) {
AddTriangle(&(tout[i]));
}
- FreeTemporary(tout);
- FreeTemporary(conv);
+ delete[] tout;
+ delete[] conv;
}
void SMesh::AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3) {
int i;
for(i = 0; i < srcm->l.n; i++) {
- STriangle *st = &(srcm->l.elem[i]);
+ STriangle *st = &(srcm->l[i]);
int pn = l.n;
atLeastOneDiscarded = false;
SBsp3::InsertOrCreate(bsp3, st, this);
SBsp3 *bspb = SBsp3::FromMesh(b);
flipNormal = false;
- keepCoplanar = false;
- AddAgainstBsp(b, bspa);
+ keepInsideOtherShell = false;
- flipNormal = false;
keepCoplanar = true;
+ AddAgainstBsp(b, bspa);
+
+ keepCoplanar = false;
AddAgainstBsp(a, bspb);
}
flipNormal = true;
keepCoplanar = true;
+ keepInsideOtherShell = true;
AddAgainstBsp(b, bspa);
flipNormal = false;
+ keepCoplanar = false;
+ keepInsideOtherShell = false;
+ AddAgainstBsp(a, bspb);
+}
+
+void SMesh::MakeFromIntersectionOf(SMesh *a, SMesh *b) {
+ SBsp3 *bspa = SBsp3::FromMesh(a);
+ SBsp3 *bspb = SBsp3::FromMesh(b);
+
+ keepInsideOtherShell = true;
+ flipNormal = false;
+
keepCoplanar = false;
AddAgainstBsp(a, bspb);
+
+ keepCoplanar = true;
+ AddAgainstBsp(b, bspa);
}
void SMesh::MakeFromCopyOf(SMesh *a) {
- int i;
- for(i = 0; i < a->l.n; i++) {
- AddTriangle(&(a->l.elem[i]));
+ ssassert(this != a, "Can't make from copy of self");
+ for(int i = 0; i < a->l.n; i++) {
+ AddTriangle(&(a->l[i]));
}
}
MakeFromCopyOf(b);
}
-void SMesh::MakeFromTransformationOf(SMesh *a,
- Vector trans, Quaternion q, double scale)
+void SMesh::MakeFromTransformationOf(SMesh *a, Vector trans,
+ Quaternion q, double scale)
{
STriangle *tr;
for(tr = a->l.First(); tr; tr = a->l.NextAfter(tr)) {
}
}
-bool SMesh::IsEmpty(void) {
- return (l.n == 0);
-}
+bool SMesh::IsEmpty() const { return (l.IsEmpty()); }
-uint32_t SMesh::FirstIntersectionWith(Point2d mp) {
- Vector p0 = Vector::From(mp.x, mp.y, 0);
- Vector gn = Vector::From(0, 0, 1);
+uint32_t SMesh::FirstIntersectionWith(Point2d mp) const {
+ Vector rayPoint = SS.GW.UnProjectPoint3(Vector::From(mp.x, mp.y, 0.0));
+ Vector rayDir = SS.GW.UnProjectPoint3(Vector::From(mp.x, mp.y, 1.0)).Minus(rayPoint);
- double maxT = -1e12;
uint32_t face = 0;
-
- int i;
- for(i = 0; i < l.n; i++) {
- STriangle tr = l.elem[i];
- tr.a = SS.GW.ProjectPoint3(tr.a);
- tr.b = SS.GW.ProjectPoint3(tr.b);
- tr.c = SS.GW.ProjectPoint3(tr.c);
-
- Vector n = tr.Normal();
-
- if(n.Dot(gn) < LENGTH_EPS) continue; // back-facing or on edge
-
- if(tr.ContainsPointProjd(gn, p0)) {
- // Let our line have the form r(t) = p0 + gn*t
- double t = -(n.Dot((tr.a).Minus(p0)))/(n.Dot(gn));
- if(t > maxT) {
- maxT = t;
- face = tr.meta.face;
- }
+ double faceT = VERY_NEGATIVE;
+ for(int i = 0; i < l.n; i++) {
+ const STriangle &tr = l[i];
+ if(tr.meta.face == 0) continue;
+
+ double t;
+ if(!tr.Raytrace(rayPoint, rayDir, &t, NULL)) continue;
+ if(t > faceT) {
+ face = tr.meta.face;
+ faceT = t;
}
}
+
return face;
}
-STriangleLl *STriangleLl::Alloc(void)
+Vector SMesh::GetCenterOfMass() const {
+ Vector center = {};
+ double vol = 0.0;
+ for(int i = 0; i < l.n; i++) {
+ const STriangle &tr = l[i];
+ double tvol = tr.SignedVolume();
+ center = center.Plus(tr.a.Plus(tr.b.Plus(tr.c)).ScaledBy(tvol / 4.0));
+ vol += tvol;
+ }
+ return center.ScaledBy(1.0 / vol);
+}
+
+STriangleLl *STriangleLl::Alloc()
{ return (STriangleLl *)AllocTemporary(sizeof(STriangleLl)); }
-SKdNode *SKdNode::Alloc(void)
+SKdNode *SKdNode::Alloc()
{ return (SKdNode *)AllocTemporary(sizeof(SKdNode)); }
SKdNode *SKdNode::From(SMesh *m) {
STriangle *tra = (STriangle *)AllocTemporary((m->l.n) * sizeof(*tra));
for(i = 0; i < m->l.n; i++) {
- tra[i] = m->l.elem[i];
+ tra[i] = m->l[i];
}
srand(0);
return ret;
}
-void SKdNode::ClearTags(void) {
+void SKdNode::ClearTags() const {
if(gt && lt) {
gt->ClearTags();
lt->ClearTags();
}
}
-void SKdNode::MakeMeshInto(SMesh *m) {
+void SKdNode::MakeMeshInto(SMesh *m) const {
if(gt) gt->MakeMeshInto(m);
if(lt) lt->MakeMeshInto(m);
}
}
-void SKdNode::ListTrianglesInto(std::vector<STriangle *> *tl) {
+void SKdNode::ListTrianglesInto(std::vector<STriangle *> *tl) const {
if(gt) gt->ListTrianglesInto(tl);
if(lt) lt->ListTrianglesInto(tl);
bool mightHit = true;
for(k = 0; k < 3; k++) {
- if((tr->a).Element(k) < v.Element(k) - KDTREE_EPS &&
- (tr->b).Element(k) < v.Element(k) - KDTREE_EPS &&
- (tr->c).Element(k) < v.Element(k) - KDTREE_EPS)
+ double trA = (tr->a).Element(k);
+ double trB = (tr->b).Element(k);
+ double trC = (tr->c).Element(k);
+ double vk = v.Element(k);
+ if(trA < vk - KDTREE_EPS &&
+ trB < vk - KDTREE_EPS &&
+ trC < vk - KDTREE_EPS)
{
mightHit = false;
break;
}
- if((tr->a).Element(k) > v.Element(k) + KDTREE_EPS &&
- (tr->b).Element(k) > v.Element(k) + KDTREE_EPS &&
- (tr->c).Element(k) > v.Element(k) + KDTREE_EPS)
+ if(trA > vk + KDTREE_EPS &&
+ trB > vk + KDTREE_EPS &&
+ trC > vk + KDTREE_EPS)
{
mightHit = false;
break;
if(tr->b.Equals(v)) { tr->b = v; continue; }
if(tr->c.Equals(v)) { tr->c = v; continue; }
+ if(tr->IsDegenerate()) {
+ continue;
+ }
+
if(v.OnLineSegment(tr->a, tr->b)) {
STriangle nt = STriangle::From(tr->meta, tr->a, v, tr->c);
extras->AddTriangle(&nt);
void SKdNode::SnapToMesh(SMesh *m) {
int i, j, k;
for(i = 0; i < m->l.n; i++) {
- STriangle *tr = &(m->l.elem[i]);
+ STriangle *tr = &(m->l[i]);
+ if(tr->IsDegenerate()) {
+ continue;
+ }
for(j = 0; j < 3; j++) {
- Vector v = ((j == 0) ? tr->a :
- ((j == 1) ? tr->b :
- tr->c));
+ Vector v = tr->vertices[j];
SMesh extra = {};
SnapToVertex(v, &extra);
for(k = 0; k < extra.l.n; k++) {
STriangle *tra = (STriangle *)AllocTemporary(sizeof(*tra));
- *tra = extra.l.elem[k];
+ *tra = extra.l[k];
AddTriangle(tra);
}
extra.Clear();
//-----------------------------------------------------------------------------
// For all the edges in sel, split them against the given triangle, and test
-// them for occlusion. Keep only the visible segments. sel is both our input
-// and our output.
+// them for occlusion. sel is both our input and our output. tag indicates
+// whether an edge is occluded.
//-----------------------------------------------------------------------------
-void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr, bool removeHidden) {
+void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr) const {
SEdgeList seln = {};
Vector tn = tr->Normal().WithMagnitude(1);
if(tn.z > LENGTH_EPS) {
// If the edge crosses our triangle's plane, then split into above
// and below parts. Note that we must preserve auxA, which contains
- // the style associated with this line.
+ // the style associated with this line, as well as the tag, which
+ // contains the occlusion status.
SEdge *se;
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
double da = (se->a).Dot(tn) - td,
Vector m = Vector::AtIntersectionOfPlaneAndLine(
tn, td,
se->a, se->b, NULL);
- seln.AddEdge(m, se->b, se->auxA);
+ seln.AddEdge(m, se->b, se->auxA, 0, se->tag);
se->b = m;
}
}
for(se = seln.l.First(); se; se = seln.l.NextAfter(se)) {
- sel->AddEdge(se->a, se->b, se->auxA);
+ sel->AddEdge(se->a, se->b, se->auxA, 0, se->tag);
}
seln.Clear();
double dab = (db - da);
Vector spl = ((se->a).ScaledBy( db/dab)).Plus(
(se->b).ScaledBy(-da/dab));
- seln.AddEdge(spl, se->b, se->auxA);
+ seln.AddEdge(spl, se->b, se->auxA, 0, se->tag);
se->b = spl;
}
}
for(se = seln.l.First(); se; se = seln.l.NextAfter(se)) {
// The split pieces are all behind the triangle, since only
// edges behind the triangle got split. So their auxB is 0.
- sel->AddEdge(se->a, se->b, se->auxA, 0);
+ sel->AddEdge(se->a, se->b, se->auxA, 0, se->tag);
}
seln.Clear();
}
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
+ bool occluded;
if(se->auxB) {
// Lies above or on the triangle plane, so triangle doesn't
// occlude it.
- se->tag = 0;
+ occluded = false;
} else {
// Test the segment to see if it lies outside the triangle
// (i.e., outside wrt at least one edge), and keep it only
// then.
Point2d pt = ((se->a).Plus(se->b).ScaledBy(0.5)).ProjectXy();
- se->tag = 1;
+ occluded = true;
for(i = 0; i < 3; i++) {
// If the test point lies on the boundary of our triangle,
// then we still discard the edge.
- if(n[i].Dot(pt) - d[i] > LENGTH_EPS) se->tag = 0;
+ if(n[i].Dot(pt) - d[i] > LENGTH_EPS) occluded = false;
}
}
- if(!removeHidden && se->tag == 1)
- se->auxA = Style::HIDDEN_EDGE;
+
+ if(occluded) {
+ se->tag = 1;
+ }
}
- if(removeHidden)
- sel->l.RemoveTagged();
}
}
//-----------------------------------------------------------------------------
// Given an edge orig, occlusion test it against our mesh. We output an edge
-// list in sel, containing the visible portions of that edge.
+// list in sel, where only invisible portions of the edge are tagged.
//-----------------------------------------------------------------------------
-void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt, bool removeHidden) {
+void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) const {
if(gt && lt) {
double ac = (orig.a).Element(which),
bc = (orig.b).Element(which);
bc < c + KDTREE_EPS ||
which == 2)
{
- lt->OcclusionTestLine(orig, sel, cnt, removeHidden);
+ lt->OcclusionTestLine(orig, sel, cnt);
}
if(ac > c - KDTREE_EPS ||
bc > c - KDTREE_EPS ||
which == 2)
{
- gt->OcclusionTestLine(orig, sel, cnt, removeHidden);
+ gt->OcclusionTestLine(orig, sel, cnt);
}
} else {
STriangleLl *ll;
if(tr->tag == cnt) continue;
- SplitLinesAgainstTriangle(sel, tr, removeHidden);
+ SplitLinesAgainstTriangle(sel, tr);
tr->tag = cnt;
}
}
// with a triangle in the mesh, otherwise not.
//-----------------------------------------------------------------------------
void SKdNode::FindEdgeOn(Vector a, Vector b, int cnt, bool coplanarIsInter,
- EdgeOnInfo *info)
+ EdgeOnInfo *info) const
{
if(gt && lt) {
double ac = a.Element(which),
}
// Record the triangle
info->tr = tr;
- // And record which vertexes a and b correspond to
+ // And record which vertices a and b correspond to
info->ai = a.Equals(tr->a) ? 0 : (a.Equals(tr->b) ? 1 : 2);
info->bi = b.Equals(tr->a) ? 0 : (b.Equals(tr->b) ? 1 : 2);
} else if(((a.Equals(tr->a) && b.Equals(tr->b)) ||
// * emphasized edges (i.e., edges where a triangle from one face joins
// a triangle from a different face)
//-----------------------------------------------------------------------------
-void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, int how, bool coplanarIsInter,
- bool *inter, bool *leaky, int auxA)
+void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, EdgeKind how, bool coplanarIsInter,
+ bool *inter, bool *leaky, int auxA) const
{
if(inter) *inter = false;
if(leaky) *leaky = false;
int cnt = 1234;
for(STriangle *tr : tris) {
for(int j = 0; j < 3; j++) {
- Vector a = (j == 0) ? tr->a : ((j == 1) ? tr->b : tr->c);
- Vector b = (j == 0) ? tr->b : ((j == 1) ? tr->c : tr->a);
+ Vector a = tr->vertices[j];
+ Vector b = tr->vertices[(j + 1) % 3];
SKdNode::EdgeOnInfo info = {};
FindEdgeOn(a, b, cnt, coplanarIsInter, &info);
switch(how) {
- case NAKED_OR_SELF_INTER_EDGES:
+ case EdgeKind::NAKED_OR_SELF_INTER:
+ // there should be one anti-parllel edge
if(info.count != 1) {
- sel->AddEdge(a, b, auxA);
- if(leaky) *leaky = true;
+ // but there may be multiple parallel coincident edges
+ SKdNode::EdgeOnInfo parallelInfo = {};
+ FindEdgeOn(b, a, -cnt, coplanarIsInter, ¶llelInfo);
+ if (info.count != parallelInfo.count) {
+ sel->AddEdge(a, b, auxA);
+ if(leaky) *leaky = true;
+ }
}
if(info.intersectsMesh) {
sel->AddEdge(a, b, auxA);
}
break;
- case SELF_INTER_EDGES:
+ case EdgeKind::SELF_INTER:
if(info.intersectsMesh) {
sel->AddEdge(a, b, auxA);
if(inter) *inter = true;
}
break;
- case TURNING_EDGES:
+ case EdgeKind::TURNING:
if((tr->Normal().z < LENGTH_EPS) &&
(info.count == 1) &&
info.frontFacing)
}
break;
- case EMPHASIZED_EDGES:
+ case EdgeKind::EMPHASIZED:
if(info.count == 1 && tr->meta.face != info.tr->meta.face) {
if(CheckAndAddTrianglePair(&edgeTris, tr, info.tr))
break;
}
break;
- case SHARP_EDGES:
+ case EdgeKind::SHARP:
if(info.count == 1) {
- Vector na0 = (j == 0) ? tr->an :
- ((j == 1) ? tr->bn : tr->cn);
- Vector nb0 = (j == 0) ? tr->bn :
- ((j == 1) ? tr->cn : tr->an);
- Vector na1 = (info.ai == 0) ? info.tr->an :
- ((info.ai == 1) ? info.tr->bn : info.tr->cn);
- Vector nb1 = (info.bi == 0) ? info.tr->an :
- ((info.bi == 1) ? info.tr->bn : info.tr->cn);
- na0 = na0.WithMagnitude(1.0);
- nb0 = nb0.WithMagnitude(1.0);
- na1 = na1.WithMagnitude(1.0);
- nb1 = nb1.WithMagnitude(1.0);
+ Vector na0 = tr->normals[j].WithMagnitude(1.0);
+ Vector nb0 = tr->normals[(j + 1) % 3].WithMagnitude(1.0);
+ Vector na1 = info.tr->normals[info.ai].WithMagnitude(1.0);
+ Vector nb1 = info.tr->normals[info.bi].WithMagnitude(1.0);
if(!((na0.Equals(na1) && nb0.Equals(nb1)) ||
(na0.Equals(nb1) && nb0.Equals(na1)))) {
if(CheckAndAddTrianglePair(&edgeTris, tr, info.tr))
}
}
break;
-
- default: oops();
}
cnt++;
}
}
-void SKdNode::MakeOutlinesInto(SOutlineList *sol)
+void SKdNode::MakeOutlinesInto(SOutlineList *sol, EdgeKind edgeKind) const
{
std::vector<STriangle *> tris;
ClearTags();
int cnt = 1234;
for(STriangle *tr : tris) {
for(int j = 0; j < 3; j++) {
- Vector a = (j == 0) ? tr->a : ((j == 1) ? tr->b : tr->c);
- Vector b = (j == 0) ? tr->b : ((j == 1) ? tr->c : tr->a);
+ Vector a = tr->vertices[j];
+ Vector b = tr->vertices[(j + 1) % 3];
SKdNode::EdgeOnInfo info = {};
FindEdgeOn(a, b, cnt, /*coplanarIsInter=*/false, &info);
if(CheckAndAddTrianglePair(&edgeTris, tr, info.tr))
continue;
- sol->AddEdge(a, b, tr->Normal(), info.tr->Normal());
+ int tag = 0;
+ switch(edgeKind) {
+ case EdgeKind::EMPHASIZED:
+ if(tr->meta.face != info.tr->meta.face) {
+ tag = 1;
+ }
+ break;
+
+ case EdgeKind::SHARP: {
+ Vector na0 = tr->normals[j].WithMagnitude(1.0);
+ Vector nb0 = tr->normals[(j + 1) % 3].WithMagnitude(1.0);
+ Vector na1 = info.tr->normals[info.ai].WithMagnitude(1.0);
+ Vector nb1 = info.tr->normals[info.bi].WithMagnitude(1.0);
+ if(!((na0.Equals(na1) && nb0.Equals(nb1)) ||
+ (na0.Equals(nb1) && nb0.Equals(na1)))) {
+ tag = 1;
+ }
+ }
+ break;
+
+ default:
+ ssassert(false, "Unexpected edge kind");
+ }
+
+ Vector nl = tr->Normal().WithMagnitude(1.0);
+ Vector nr = info.tr->Normal().WithMagnitude(1.0);
+
+ // We don't add edges with the same left and right
+ // normals because they can't produce outlines.
+ if(tag == 0 && nl.Equals(nr)) continue;
+ sol->AddEdge(a, b, nl, nr, tag);
}
}
}
+bool SOutline::IsVisible(Vector projDir) const {
+ double ldot = nl.Dot(projDir);
+ double rdot = nr.Dot(projDir);
+ return (ldot > -LENGTH_EPS) == (rdot < LENGTH_EPS) ||
+ (rdot > -LENGTH_EPS) == (ldot < LENGTH_EPS);
+}
+
void SOutlineList::Clear() {
l.Clear();
}
-void SOutlineList::AddEdge(Vector a, Vector b, Vector nl, Vector nr) {
+void SOutlineList::AddEdge(Vector a, Vector b, Vector nl, Vector nr, int tag) {
SOutline so = {};
so.a = a;
so.b = b;
so.nl = nl;
so.nr = nr;
+ so.tag = tag;
l.Add(&so);
}
-void SOutlineList::FillOutlineTags(Vector projDir) {
- for(SOutline *so = l.First(); so; so = l.NextAfter(so)) {
- so->tag = ((so->nl.Dot(projDir) > 0.0) != (so->nr.Dot(projDir) > 0.0));
+void SOutlineList::ListTaggedInto(SEdgeList *el, int auxA, int auxB) {
+ for(const SOutline &so : l) {
+ if(so.tag == 0) continue;
+ el->AddEdge(so.a, so.b, auxA, auxB);
}
}
l.Add(so);
}
}
+
+void SMesh::PrecomputeTransparency() {
+ std::sort(l.begin(), l.end(),
+ [&](const STriangle &sta, const STriangle &stb) {
+ RgbaColor colora = sta.meta.color,
+ colorb = stb.meta.color;
+ bool opaquea = colora.IsEmpty() || colora.alpha == 255,
+ opaqueb = colorb.IsEmpty() || colorb.alpha == 255;
+
+ if(!opaquea || !opaqueb) isTransparent = true;
+ return (opaquea != opaqueb && opaquea == true);
+ });
+}
+
+void SMesh::RemoveDegenerateTriangles() {
+ for(auto &tr : l) {
+ tr.tag = (int)tr.IsDegenerate();
+ }
+ l.RemoveTagged();
+}
+
+double SMesh::CalculateVolume() const {
+ double vol = 0;
+ for(STriangle tr : l) {
+ // Translate to place vertex A at (x, y, 0)
+ Vector trans = Vector::From(tr.a.x, tr.a.y, 0);
+ tr.a = (tr.a).Minus(trans);
+ tr.b = (tr.b).Minus(trans);
+ tr.c = (tr.c).Minus(trans);
+
+ // Rotate to place vertex B on the y-axis. Depending on
+ // whether the triangle is CW or CCW, C is either to the
+ // right or to the left of the y-axis. This handles the
+ // sign of our normal.
+ Vector u = Vector::From(-tr.b.y, tr.b.x, 0);
+ u = u.WithMagnitude(1);
+ Vector v = Vector::From(tr.b.x, tr.b.y, 0);
+ v = v.WithMagnitude(1);
+ Vector n = Vector::From(0, 0, 1);
+
+ tr.a = (tr.a).DotInToCsys(u, v, n);
+ tr.b = (tr.b).DotInToCsys(u, v, n);
+ tr.c = (tr.c).DotInToCsys(u, v, n);
+
+ n = tr.Normal().WithMagnitude(1);
+
+ // Triangles on edge don't contribute
+ if(fabs(n.z) < LENGTH_EPS) continue;
+
+ // The plane has equation p dot n = a dot n
+ double d = (tr.a).Dot(n);
+ // nx*x + ny*y + nz*z = d
+ // nz*z = d - nx*x - ny*y
+ double A = -n.x/n.z, B = -n.y/n.z, C = d/n.z;
+
+ double mac = tr.c.y/tr.c.x, mbc = (tr.c.y - tr.b.y)/tr.c.x;
+ double xc = tr.c.x, yb = tr.b.y;
+
+ // I asked Maple for
+ // int(int(A*x + B*y +C, y=mac*x..(mbc*x + yb)), x=0..xc);
+ double integral =
+ (1.0/3)*(
+ A*(mbc-mac)+
+ (1.0/2)*B*(mbc*mbc-mac*mac)
+ )*(xc*xc*xc)+
+ (1.0/2)*(A*yb+B*yb*mbc+C*(mbc-mac))*xc*xc+
+ C*yb*xc+
+ (1.0/2)*B*yb*yb*xc;
+
+ vol += integral;
+ }
+ return vol;
+}
+
+double SMesh::CalculateSurfaceArea(const std::vector<uint32_t> &faces) const {
+ double area = 0.0;
+ for(uint32_t f : faces) {
+ for(const STriangle &t : l) {
+ if(f != t.meta.face) continue;
+ area += t.Area();
+ }
+ }
+ return area;
+}
// Useful when splitting, tangent arcing, or removing bezier points.
//-----------------------------------------------------------------------------
void GraphicsWindow::ReplacePointInConstraints(hEntity oldpt, hEntity newpt) {
- int i;
- for(i = 0; i < SK.constraint.n; i++) {
- Constraint *c = &(SK.constraint.elem[i]);
- if(c->ptA.v == oldpt.v) c->ptA = newpt;
- if(c->ptB.v == oldpt.v) c->ptB = newpt;
+ for(auto &c : SK.constraint) {
+ if(c.ptA == oldpt)
+ c.ptA = newpt;
+ if(c.ptB == oldpt)
+ c.ptB = newpt;
}
}
//-----------------------------------------------------------------------------
void GraphicsWindow::RemoveConstraintsForPointBeingDeleted(hEntity hpt) {
SK.constraint.ClearTags();
- for(int i = 0; i < SK.constraint.n; i++) {
- Constraint *c = &(SK.constraint.elem[i]);
- if(c->ptA.v == hpt.v || c->ptB.v == hpt.v) {
- c->tag = 1;
+ for(auto &c : SK.constraint) {
+ if(c.ptA == hpt || c.ptB == hpt) {
+ c.tag = 1;
(SS.deleted.constraints)++;
- if(c->type != Constraint::POINTS_COINCIDENT &&
- c->type != Constraint::HORIZONTAL &&
- c->type != Constraint::VERTICAL)
+ if(c.type != Constraint::Type::POINTS_COINCIDENT &&
+ c.type != Constraint::Type::HORIZONTAL &&
+ c.type != Constraint::Type::VERTICAL)
{
(SS.deleted.nonTrivialConstraints)++;
}
//-----------------------------------------------------------------------------
void GraphicsWindow::FixConstraintsForRequestBeingDeleted(hRequest hr) {
Request *r = SK.GetRequest(hr);
- if(r->group.v != SS.GW.activeGroup.v) return;
+ if(r->group != SS.GW.activeGroup) return;
Entity *e;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
if(!(e->h.isFromRequest())) continue;
- if(e->h.request().v != hr.v) continue;
+ if(e->h.request() != hr) continue;
- if(e->type != Entity::POINT_IN_2D &&
- e->type != Entity::POINT_IN_3D)
+ if(e->type != Entity::Type::POINT_IN_2D &&
+ e->type != Entity::Type::POINT_IN_3D)
{
continue;
}
Constraint *c;
SK.constraint.ClearTags();
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
- if(c->type != Constraint::POINTS_COINCIDENT) continue;
- if(c->group.v != SS.GW.activeGroup.v) continue;
+ if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
+ if(c->group != SS.GW.activeGroup) continue;
- if(c->ptA.v == hpt.v) {
+ if(c->ptA == hpt) {
ld.Add(&(c->ptB));
c->tag = 1;
}
- if(c->ptB.v == hpt.v) {
+ if(c->ptB == hpt) {
ld.Add(&(c->ptA));
c->tag = 1;
}
// those two points were implicitly coincident with each other. By
// deleting hpt (and all constraints that mention it), we will delete
// that relationship. So put it back here now.
- int i;
- for(i = 1; i < ld.n; i++) {
- Constraint::ConstrainCoincident(ld.elem[i-1], ld.elem[i]);
+ for(int i = 1; i < ld.n; i++) {
+ Constraint::ConstrainCoincident(ld[i-1], ld[i]);
}
ld.Clear();
}
void GraphicsWindow::ParametricCurve::MakeFromEntity(hEntity he, bool reverse) {
*this = {};
Entity *e = SK.GetEntity(he);
- if(e->type == Entity::LINE_SEGMENT) {
+ if(e->type == Entity::Type::LINE_SEGMENT) {
isLine = true;
p0 = e->EndpointStart(),
p1 = e->EndpointFinish();
if(reverse) {
swap(p0, p1);
}
- } else if(e->type == Entity::ARC_OF_CIRCLE) {
+ } else if(e->type == Entity::Type::ARC_OF_CIRCLE) {
isLine = false;
p0 = SK.GetEntity(e->point[0])->PointGetNum();
Vector pe = SK.GetEntity(e->point[1])->PointGetNum();
EntityBase *wrkpln = SK.GetEntity(e->workplane)->Normal();
u = wrkpln->NormalU();
v = wrkpln->NormalV();
- } else {
- oops();
- }
+ } else ssassert(false, "Unexpected entity type");
}
-double GraphicsWindow::ParametricCurve::LengthForAuto(void) {
+double GraphicsWindow::ParametricCurve::LengthForAuto() {
if(isLine) {
// Allow a third of the line to disappear with auto radius
return (p1.Minus(p0)).Magnitude() / 3;
return t;
}
}
-hRequest GraphicsWindow::ParametricCurve::CreateRequestTrimmedTo(double t,
- bool extraConstraints, hEntity orig, hEntity arc, bool arcFinish)
+/** Changes or copies the given entity and connects it to the arc.
+ * \param t Where on this parametric curve does it connect to the arc.
+ * \param reuseOrig Should the original entity be modified
+ * \param orig The original entity.
+ * \param arc The arc that will be connected to.
+ * \param arcFinish Whether to connect to the end point of the arc.
+ * \param pointf When changing the original entity, whether the end point should be modified.
+ */
+void GraphicsWindow::ParametricCurve::CreateRequestTrimmedTo(double t,
+ bool reuseOrig, hEntity orig, hEntity arc, bool arcFinish, bool pointf)
{
hRequest hr;
Entity *e;
if(isLine) {
- hr = SS.GW.AddRequest(Request::LINE_SEGMENT, false),
- e = SK.GetEntity(hr.entity(0));
- SK.GetEntity(e->point[0])->PointForceTo(PointAt(t));
- SK.GetEntity(e->point[1])->PointForceTo(PointAt(1));
- ConstrainPointIfCoincident(e->point[0]);
- ConstrainPointIfCoincident(e->point[1]);
- if(extraConstraints) {
- Constraint::Constrain(Constraint::PT_ON_LINE,
+ if (reuseOrig) {
+ e = SK.GetEntity(orig);
+ int i = pointf ? 1 : 0;
+ SK.GetEntity(e->point[i])->PointForceTo(PointAt(t));
+ ConstrainPointIfCoincident(e->point[i]);
+ } else {
+ hr = SS.GW.AddRequest(Request::Type::LINE_SEGMENT, /*rememberForUndo=*/false),
+ e = SK.GetEntity(hr.entity(0));
+ SK.GetEntity(e->point[0])->PointForceTo(PointAt(t));
+ SK.GetEntity(e->point[1])->PointForceTo(PointAt(1));
+ ConstrainPointIfCoincident(e->point[0]);
+ ConstrainPointIfCoincident(e->point[1]);
+ Constraint::Constrain(Constraint::Type::PT_ON_LINE,
hr.entity(1), Entity::NO_ENTITY, orig);
}
- Constraint::Constrain(Constraint::ARC_LINE_TANGENT,
+ Constraint::Constrain(Constraint::Type::ARC_LINE_TANGENT,
Entity::NO_ENTITY, Entity::NO_ENTITY,
- arc, e->h, arcFinish, false);
+ arc, e->h, /*other=*/arcFinish, /*other2=*/false);
} else {
- hr = SS.GW.AddRequest(Request::ARC_OF_CIRCLE, false),
- e = SK.GetEntity(hr.entity(0));
- SK.GetEntity(e->point[0])->PointForceTo(p0);
- if(dtheta > 0) {
- SK.GetEntity(e->point[1])->PointForceTo(PointAt(t));
- SK.GetEntity(e->point[2])->PointForceTo(PointAt(1));
+ if (reuseOrig) {
+ e = SK.GetEntity(orig);
+ int i = pointf ? 2 : 1;
+ SK.GetEntity(e->point[i])->PointForceTo(PointAt(t));
+ ConstrainPointIfCoincident(e->point[i]);
} else {
- SK.GetEntity(e->point[2])->PointForceTo(PointAt(t));
- SK.GetEntity(e->point[1])->PointForceTo(PointAt(1));
+ hr = SS.GW.AddRequest(Request::Type::ARC_OF_CIRCLE, /*rememberForUndo=*/false),
+ e = SK.GetEntity(hr.entity(0));
+ SK.GetEntity(e->point[0])->PointForceTo(p0);
+ if(dtheta > 0) {
+ SK.GetEntity(e->point[1])->PointForceTo(PointAt(t));
+ SK.GetEntity(e->point[2])->PointForceTo(PointAt(1));
+ } else {
+ SK.GetEntity(e->point[2])->PointForceTo(PointAt(t));
+ SK.GetEntity(e->point[1])->PointForceTo(PointAt(1));
+ }
+ ConstrainPointIfCoincident(e->point[0]);
+ ConstrainPointIfCoincident(e->point[1]);
+ ConstrainPointIfCoincident(e->point[2]);
}
- ConstrainPointIfCoincident(e->point[0]);
- ConstrainPointIfCoincident(e->point[1]);
- ConstrainPointIfCoincident(e->point[2]);
// The tangency constraint alone is enough to fully constrain it,
// so there's no need for more.
- Constraint::Constrain(Constraint::CURVE_CURVE_TANGENT,
+ Constraint::Constrain(Constraint::Type::CURVE_CURVE_TANGENT,
Entity::NO_ENTITY, Entity::NO_ENTITY,
- arc, e->h, arcFinish, (dtheta < 0));
+ arc, e->h, /*other=*/arcFinish, /*other2=*/(dtheta < 0));
}
- return hr;
}
//-----------------------------------------------------------------------------
ptv = pt->PointGetNum();
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
- if(e->h.v == pt->h.v) continue;
+ if(e->h == pt->h) continue;
if(!e->IsPoint()) continue;
- if(e->group.v != pt->group.v) continue;
- if(e->workplane.v != pt->workplane.v) continue;
+ if(e->group != pt->group) continue;
+ if(e->workplane != pt->workplane) continue;
ev = e->PointGetNum();
if(!ev.Equals(ptv)) continue;
// non-construction line segments that join at this point, and create a
// tangent arc joining them.
//-----------------------------------------------------------------------------
-void GraphicsWindow::MakeTangentArc(void) {
+void GraphicsWindow::MakeTangentArc() {
if(!LockedInWorkplane()) {
- Error("Must be sketching in workplane to create tangent "
- "arc.");
+ Error(_("Must be sketching in workplane to create tangent arc."));
return;
}
hRequest hreq[2];
hEntity hent[2];
bool pointf[2];
- for(i = 0; i < SK.request.n; i++) {
- Request *r = &(SK.request.elem[i]);
- if(r->group.v != activeGroup.v) continue;
- if(r->workplane.v != ActiveWorkplane().v) continue;
- if(r->construction) continue;
- if(r->type != Request::LINE_SEGMENT &&
- r->type != Request::ARC_OF_CIRCLE)
- {
+ for(auto &r : SK.request) {
+ if(r.group != activeGroup)
+ continue;
+ if(r.workplane != ActiveWorkplane())
+ continue;
+ if(r.construction)
+ continue;
+ if(r.type != Request::Type::LINE_SEGMENT && r.type != Request::Type::ARC_OF_CIRCLE) {
continue;
}
- Entity *e = SK.GetEntity(r->h.entity(0));
+ Entity *e = SK.GetEntity(r.h.entity(0));
Vector ps = e->EndpointStart(),
pf = e->EndpointFinish();
// finish of this entity.
ent[c] = e;
hent[c] = e->h;
- req[c] = r;
- hreq[c] = r->h;
+ req[c] = &r;
+ hreq[c] = r.h;
pointf[c] = (pf.Equals(pshared));
}
c++;
}
}
if(c != 2) {
- Error("To create a tangent arc, select a point where two "
- "non-construction lines or cicles in this group and "
- "workplane join.");
+ Error(_("To create a tangent arc, select a point where two "
+ "non-construction lines or circles in this group and "
+ "workplane join."));
return;
}
tp[1] = t[1];
// And convert those points to parameter values along the curve.
- t[0] += (pa0.Minus(p0)).DivPivoting(t0);
- t[1] += (pa1.Minus(p1)).DivPivoting(t1);
+ t[0] += (pa0.Minus(p0)).DivProjected(t0);
+ t[1] += (pa1.Minus(p1)).DivProjected(t1);
}
// Stupid check for convergence, and for an out of range result (as
if(fabs(tp[0] - t[0]) > 1e-3 || fabs(tp[1] - t[1]) > 1e-3 ||
t[0] < 0.01 || t[1] < 0.01 ||
t[0] > 0.99 || t[1] > 0.99 ||
- isnan(t[0]) || isnan(t[1]))
+ IsReasonable(t[0]) || IsReasonable(t[1]))
{
- Error("Couldn't round this corner. Try a smaller radius, or try "
- "creating the desired geometry by hand with tangency "
- "constraints.");
+ Error(_("Couldn't round this corner. Try a smaller radius, or try "
+ "creating the desired geometry by hand with tangency "
+ "constraints."));
return;
}
SS.UndoRemember();
- hRequest harc = AddRequest(Request::ARC_OF_CIRCLE, false);
+ if (SS.tangentArcModify) {
+ // Delete the coincident constraint for the removed point.
+ SK.constraint.ClearTags();
+ for(i = 0; i < SK.constraint.n; i++) {
+ Constraint *cs = &(SK.constraint[i]);
+ if(cs->group != activeGroup) continue;
+ if(cs->workplane != ActiveWorkplane()) continue;
+ if(cs->type != Constraint::Type::POINTS_COINCIDENT) continue;
+ if (SK.GetEntity(cs->ptA)->PointGetNum().Equals(pshared)) {
+ cs->tag = 1;
+ }
+ }
+ SK.constraint.RemoveTagged();
+ } else {
+ // Make the original entities construction, or delete them
+ // entirely, according to user preference.
+ SK.GetRequest(hreq[0])->construction = true;
+ SK.GetRequest(hreq[1])->construction = true;
+ }
+
+ // Create and position the new tangent arc.
+ hRequest harc = AddRequest(Request::Type::ARC_OF_CIRCLE, /*rememberForUndo=*/false);
Entity *earc = SK.GetEntity(harc.entity(0));
hEntity hearc = earc->h;
earc = NULL;
- pc[0].CreateRequestTrimmedTo(t[0], !SS.tangentArcDeleteOld,
- hent[0], hearc, (b == 1));
- pc[1].CreateRequestTrimmedTo(t[1], !SS.tangentArcDeleteOld,
- hent[1], hearc, (a == 1));
-
- // Now either make the original entities construction, or delete them
- // entirely, according to user preference.
- Request *re;
- SK.request.ClearTags();
- for(re = SK.request.First(); re; re = SK.request.NextAfter(re)) {
- if(re->h.v == hreq[0].v || re->h.v == hreq[1].v) {
- if(SS.tangentArcDeleteOld) {
- re->tag = 1;
- } else {
- re->construction = true;
- }
- }
- }
- if(SS.tangentArcDeleteOld) {
- DeleteTaggedRequests();
- }
-
- SS.ScheduleGenerateAll();
+ // Modify or duplicate the original entities and connect them to the tangent arc.
+ pc[0].CreateRequestTrimmedTo(t[0], SS.tangentArcModify,
+ hent[0], hearc, /*arcFinish=*/(b == 1), pointf[0]);
+ pc[1].CreateRequestTrimmedTo(t[1], SS.tangentArcModify,
+ hent[1], hearc, /*arcFinish=*/(a == 1), pointf[1]);
}
hEntity GraphicsWindow::SplitLine(hEntity he, Vector pinter) {
p1 = SK.GetEntity(hep1)->PointGetNum();
// Add the two line segments this one gets split into.
- hRequest r0i = AddRequest(Request::LINE_SEGMENT, false),
- ri1 = AddRequest(Request::LINE_SEGMENT, false);
+ hRequest r0i = AddRequest(Request::Type::LINE_SEGMENT, /*rememberForUndo=*/false),
+ ri1 = AddRequest(Request::Type::LINE_SEGMENT, /*rememberForUndo=*/false);
// Don't get entities till after adding, realloc issues
Entity *e0i = SK.GetEntity(r0i.entity(0)),
hEntity GraphicsWindow::SplitCircle(hEntity he, Vector pinter) {
Entity *circle = SK.GetEntity(he);
- if(circle->type == Entity::CIRCLE) {
+ if(circle->type == Entity::Type::CIRCLE) {
// Start with an unbroken circle, split it into a 360 degree arc.
Vector center = SK.GetEntity(circle->point[0])->PointGetNum();
circle = NULL; // shortly invalid!
- hRequest hr = AddRequest(Request::ARC_OF_CIRCLE, false);
+ hRequest hr = AddRequest(Request::Type::ARC_OF_CIRCLE, /*rememberForUndo=*/false);
Entity *arc = SK.GetEntity(hr.entity(0));
finish = SK.GetEntity(hf)->PointGetNum();
circle = NULL; // shortly invalid!
- hRequest hr0 = AddRequest(Request::ARC_OF_CIRCLE, false),
- hr1 = AddRequest(Request::ARC_OF_CIRCLE, false);
+ hRequest hr0 = AddRequest(Request::Type::ARC_OF_CIRCLE, /*rememberForUndo=*/false),
+ hr1 = AddRequest(Request::Type::ARC_OF_CIRCLE, /*rememberForUndo=*/false);
Entity *arc0 = SK.GetEntity(hr0.entity(0)),
*arc1 = SK.GetEntity(hr1.entity(0));
double t;
int i, j;
for(i = 0; i < sbl.l.n; i++) {
- SBezier *sb = &(sbl.l.elem[i]);
- if(sb->deg != 3) oops();
+ SBezier *sb = &(sbl.l[i]);
+ ssassert(sb->deg == 3, "Expected a cubic bezier");
- sb->ClosestPointTo(pinter, &t, false);
+ sb->ClosestPointTo(pinter, &t, /*mustConverge=*/false);
if(pinter.Equals(sb->PointAt(t))) {
// Split that segment at the intersection.
SBezier b0i, bi1, b01 = *sb;
b01.SplitAt(t, &b0i, &bi1);
// Add the two cubic segments this one gets split into.
- hRequest r0i = AddRequest(Request::CUBIC, false),
- ri1 = AddRequest(Request::CUBIC, false);
+ hRequest r0i = AddRequest(Request::Type::CUBIC, /*rememberForUndo=*/false),
+ ri1 = AddRequest(Request::Type::CUBIC, /*rememberForUndo=*/false);
// Don't get entities till after adding, realloc issues
Entity *e0i = SK.GetEntity(r0i.entity(0)),
hep1n = ei1->point[3];
hepin = e0i->point[3];
} else {
- hRequest r = AddRequest(Request::CUBIC, false);
+ hRequest r = AddRequest(Request::Type::CUBIC, /*rememberForUndo=*/false);
Entity *e = SK.GetEntity(r.entity(0));
for(j = 0; j <= 3; j++) {
hEntity GraphicsWindow::SplitEntity(hEntity he, Vector pinter) {
Entity *e = SK.GetEntity(he);
- int entityType = e->type;
+ Entity::Type entityType = e->type;
hEntity ret;
if(e->IsCircle()) {
ret = SplitCircle(he, pinter);
- } else if(e->type == Entity::LINE_SEGMENT) {
+ } else if(e->type == Entity::Type::LINE_SEGMENT) {
ret = SplitLine(he, pinter);
- } else if(e->type == Entity::CUBIC || e->type == Entity::CUBIC_PERIODIC) {
+ } else if(e->type == Entity::Type::CUBIC || e->type == Entity::Type::CUBIC_PERIODIC) {
ret = SplitCubic(he, pinter);
} else {
- Error("Couldn't split this entity; lines, circles, or cubics only.");
+ Error(_("Couldn't split this entity; lines, circles, or cubics only."));
return Entity::NO_ENTITY;
}
// Finally, delete the request that generated the original entity.
- int reqType = EntReqTable::GetRequestForEntity(entityType);
+ Request::Type reqType = EntReqTable::GetRequestForEntity(entityType);
SK.request.ClearTags();
- for(int i = 0; i < SK.request.n; i++) {
- Request *r = &(SK.request.elem[i]);
- if(r->group.v != activeGroup.v) continue;
- if(r->type != reqType) continue;
+ for(auto &r : SK.request) {
+ if(r.group != activeGroup)
+ continue;
+ if(r.type != reqType)
+ continue;
// If the user wants to keep the old entities around, they can just
// mark them construction first.
- if(he.v == r->h.entity(0).v && !r->construction) {
- r->tag = 1;
+ if(he == r.h.entity(0) && !r.construction) {
+ r.tag = 1;
break;
}
}
return ret;
}
-void GraphicsWindow::SplitLinesOrCurves(void) {
+void GraphicsWindow::SplitLinesOrCurves() {
if(!LockedInWorkplane()) {
- Error("Must be sketching in workplane to split.");
+ Error(_("Must be sketching in workplane to split."));
return;
}
GroupSelection();
- if(!(gs.n == 2 &&(gs.lineSegments +
- gs.circlesOrArcs +
- gs.cubics +
- gs.periodicCubics) == 2))
- {
- Error("Select two entities that intersect each other (e.g. two lines "
- "or two circles or a circle and a line).");
+ int n = gs.lineSegments + gs.circlesOrArcs + gs.cubics + gs.periodicCubics;
+ if(!((n == 2 && gs.points == 0) || (n == 1 && gs.points == 1))) {
+ Error(_("Select two entities that intersect each other "
+ "(e.g. two lines/circles/arcs or a line/circle/arc and a point)."));
return;
}
+ bool splitAtPoint = (gs.points == 1);
hEntity ha = gs.entity[0],
- hb = gs.entity[1];
+ hb = splitAtPoint ? gs.point[0] : gs.entity[1];
+
Entity *ea = SK.GetEntity(ha),
*eb = SK.GetEntity(hb);
-
- // Compute the possibly-rational Bezier curves for each of these entities
- SBezierList sbla, sblb;
- sbla = {};
- sblb = {};
- ea->GenerateBezierCurves(&sbla);
- eb->GenerateBezierCurves(&sblb);
- // and then compute the points where they intersect, based on those curves.
SPointList inters = {};
- sbla.AllIntersectionsWith(&sblb, &inters);
-
- if(inters.l.n > 0) {
- Vector pi = Vector::From(0, 0, 0);
- // If there's multiple points, then take the one closest to the
- // mouse pointer.
- double dmin = VERY_POSITIVE;
- SPoint *sp;
- for(sp = inters.l.First(); sp; sp = inters.l.NextAfter(sp)) {
- double d = ProjectPoint(sp->p).DistanceTo(currentMousePosition);
- if(d < dmin) {
- dmin = d;
- pi = sp->p;
+ SBezierList sbla = {},
+ sblb = {};
+ Vector pi = Vector::From(0, 0, 0);
+
+ SK.constraint.ClearTags();
+
+ // First, decide the point where we're going to make the split.
+ bool foundInters = false;
+ if(splitAtPoint) {
+ // One of the entities is a point, and this point must be on the other entity.
+ // Verify that a corresponding point-coincident constraint exists for the point/entity.
+ Vector p0, p1;
+ if(ea->type == Entity::Type::LINE_SEGMENT) {
+ p0 = ea->EndpointStart();
+ p1 = ea->EndpointFinish();
+ }
+
+ for(Constraint &c : SK.constraint) {
+ if(c.ptA.request() == hb.request() &&
+ c.entityA.request() == ha.request()) {
+ pi = SK.GetEntity(c.ptA)->PointGetNum();
+
+ if(ea->type == Entity::Type::LINE_SEGMENT && !pi.OnLineSegment(p0, p1)) {
+ // The point isn't between line endpoints, so there isn't an actual
+ // intersection.
+ continue;
+ }
+
+ c.tag = 1;
+ foundInters = true;
+ break;
+ }
+ }
+ } else {
+ // Compute the possibly-rational Bezier curves for each of these non-point entities...
+ ea->GenerateBezierCurves(&sbla);
+ eb->GenerateBezierCurves(&sblb);
+ // ... and then compute the points where they intersect, based on those curves.
+ sbla.AllIntersectionsWith(&sblb, &inters);
+
+ // If there's multiple points, then take the one closest to the mouse pointer.
+ if(!inters.l.IsEmpty()) {
+ double dmin = VERY_POSITIVE;
+ SPoint *sp;
+ for(sp = inters.l.First(); sp; sp = inters.l.NextAfter(sp)) {
+ double d = ProjectPoint(sp->p).DistanceTo(currentMousePosition);
+ if(d < dmin) {
+ dmin = d;
+ pi = sp->p;
+ }
}
}
+ foundInters = true;
+ }
+
+ // Then, actually split the entities.
+ if(foundInters) {
SS.UndoRemember();
+
+ // Remove any constraints we're going to replace.
+ SK.constraint.RemoveTagged();
+
hEntity hia = SplitEntity(ha, pi),
- hib = SplitEntity(hb, pi);
+ hib = {};
// SplitEntity adds the coincident constraints to join the split halves
// of each original entity; and then we add the constraint to join
// the two entities together at the split point.
- if(hia.v && hib.v) {
- Constraint::ConstrainCoincident(hia, hib);
+ if(splitAtPoint) {
+ // Remove datum point, as it has now been superseded by the split point.
+ SK.request.ClearTags();
+ for(Request &r : SK.request) {
+ if(r.h == hb.request()) {
+ if(r.type == Request::Type::DATUM_POINT) {
+ // Delete datum point.
+ r.tag = 1;
+ FixConstraintsForRequestBeingDeleted(r.h);
+ } else {
+ // Add constraint if not datum point, but endpoint of line/arc etc.
+ Constraint::ConstrainCoincident(hia, hb);
+ }
+ break;
+ }
+ }
+ SK.request.RemoveTagged();
+ } else {
+ // Split second non-point entity and add constraint.
+ hib = SplitEntity(hb, pi);
+ if(hia.v && hib.v) {
+ Constraint::ConstrainCoincident(hia, hib);
+ }
}
} else {
- Error("Can't split; no intersection found.");
+ Error(_("Can't split; no intersection found."));
+ return;
}
// All done, clean up and regenerate.
sbla.Clear();
sblb.Clear();
ClearSelection();
- SS.ScheduleGenerateAll();
}
-
// twice as far as the mouse pointer...
List<hEntity> *lhe = &(pending.points);
for(hEntity *hee = lhe->First(); hee; hee = lhe->NextAfter(hee)) {
- if(hee->v == hp.v) {
+ if(*hee == hp) {
// Exact same point.
return;
}
Entity *pe = SK.GetEntity(*hee);
if(pe->type == p->type &&
- pe->type != Entity::POINT_IN_2D &&
- pe->type != Entity::POINT_IN_3D &&
- pe->group.v == p->group.v)
+ pe->type != Entity::Type::POINT_IN_2D &&
+ pe->type != Entity::Type::POINT_IN_3D &&
+ pe->group == p->group)
{
// Transform-type point, from the same group. So it handles the
// same unknowns.
Entity *e = SK.GetEntity(he);
if(e->IsPoint()) {
AddPointToDraggedList(e->h);
- } else if(e->type == Entity::LINE_SEGMENT ||
- e->type == Entity::ARC_OF_CIRCLE ||
- e->type == Entity::CUBIC ||
- e->type == Entity::CUBIC_PERIODIC ||
- e->type == Entity::CIRCLE ||
- e->type == Entity::TTF_TEXT)
+ } else if(e->type == Entity::Type::LINE_SEGMENT ||
+ e->type == Entity::Type::ARC_OF_CIRCLE ||
+ e->type == Entity::Type::CUBIC ||
+ e->type == Entity::Type::CUBIC_PERIODIC ||
+ e->type == Entity::Type::CIRCLE ||
+ e->type == Entity::Type::TTF_TEXT ||
+ e->type == Entity::Type::IMAGE)
{
int pts;
EntReqTable::GetEntityInfo(e->type, e->extraPoints,
}
}
-void GraphicsWindow::StartDraggingBySelection(void) {
+void GraphicsWindow::StartDraggingBySelection() {
List<Selection> *ls = &(selection);
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
if(!s->entity.v) continue;
// The user might select a point, and then click it again to start
// dragging; but the point just got unselected by that click. So drag
// the hovered item too, and they'll always have it.
- if(hover.entity.v) StartDraggingByEntity(hover.entity);
+ if(hover.entity.v) {
+ hEntity dragEntity = ChooseFromHoverToDrag().entity;
+ if(dragEntity != Entity::NO_ENTITY) {
+ StartDraggingByEntity(dragEntity);
+ }
+ }
}
void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
bool middleDown, bool rightDown, bool shiftDown, bool ctrlDown)
{
- if(GraphicsEditControlIsVisible()) return;
+ if(window->IsEditorVisible()) return;
if(context.active) return;
SS.extraLine.draw = false;
}
}
- if(!leftDown && (pending.operation == DRAGGING_POINTS ||
- pending.operation == DRAGGING_MARQUEE))
+ if(!leftDown && (pending.operation == Pending::DRAGGING_POINTS ||
+ pending.operation == Pending::DRAGGING_MARQUEE))
{
ClearPending();
- InvalidateGraphics();
+ Invalidate();
}
Point2d mp = Point2d::From(x, y);
if(!(shiftDown || ctrlDown)) {
double s = 0.3*(PI/180)*scale; // degrees per pixel
- projRight = orig.projRight.RotatedAbout(orig.projUp, -s*dx);
- projUp = orig.projUp.RotatedAbout(orig.projRight, s*dy);
+ if(SS.turntableNav) { // lock the Z to vertical
+ projRight = orig.projRight.RotatedAbout(Vector::From(0, 0, 1), -s * dx);
+ projUp = orig.projUp.RotatedAbout(
+ Vector::From(orig.projRight.x, orig.projRight.y, orig.projRight.y), s * dy);
+ } else {
+ projRight = orig.projRight.RotatedAbout(orig.projUp, -s * dx);
+ projUp = orig.projUp.RotatedAbout(orig.projRight, s * dy);
+ }
NormalizeProjectionVectors();
} else if(ctrlDown) {
orig.mouse.x = x;
orig.mouse.y = y;
- if(SS.TW.shown.screen == TextWindow::SCREEN_EDIT_VIEW) {
+ if(SS.TW.shown.screen == TextWindow::Screen::EDIT_VIEW) {
if(havePainted) {
SS.ScheduleShowTW();
}
}
- InvalidateGraphics();
+ Invalidate();
havePainted = false;
return;
}
- if(pending.operation == 0) {
+ if(pending.operation == Pending::NONE) {
double dm = orig.mouse.DistanceTo(mp);
// If we're currently not doing anything, then see if we should
// start dragging something.
if(leftDown && dm > 3) {
Entity *e = NULL;
- if(hover.entity.v) e = SK.GetEntity(hover.entity);
- if(e && e->type != Entity::WORKPLANE) {
- Entity *e = SK.GetEntity(hover.entity);
- if(e->type == Entity::CIRCLE && selection.n <= 1) {
+ hEntity dragEntity = ChooseFromHoverToDrag().entity;
+ if(dragEntity.v) e = SK.GetEntity(dragEntity);
+ if(e && e->type != Entity::Type::WORKPLANE) {
+ Entity *e = SK.GetEntity(dragEntity);
+ if(e->type == Entity::Type::CIRCLE && selection.n <= 1) {
// Drag the radius.
ClearSelection();
- pending.circle = hover.entity;
- pending.operation = DRAGGING_RADIUS;
+ pending.circle = dragEntity;
+ pending.operation = Pending::DRAGGING_RADIUS;
} else if(e->IsNormal()) {
ClearSelection();
- pending.normal = hover.entity;
- pending.operation = DRAGGING_NORMAL;
+ pending.normal = dragEntity;
+ pending.operation = Pending::DRAGGING_NORMAL;
} else {
if(!hoverWasSelectedOnMousedown) {
// The user clicked an unselected entity, which
ClearSelection();
}
hover.Clear();
- pending.operation = DRAGGING_POINTS;
+ pending.operation = Pending::DRAGGING_POINTS;
}
} else if(hover.constraint.v &&
SK.GetConstraint(hover.constraint)->HasLabel())
{
ClearSelection();
pending.constraint = hover.constraint;
- pending.operation = DRAGGING_CONSTRAINT;
+ pending.operation = Pending::DRAGGING_CONSTRAINT;
}
- if(pending.operation != 0) {
+ if(pending.operation != Pending::NONE) {
// We just started a drag, so remember for the undo before
// the drag changes anything.
SS.UndoRemember();
if(hover.entity.v) {
// Avoid accidentally selecting workplanes when
// starting drags.
- MakeUnselected(hover.entity, false);
+ MakeUnselected(hover.entity, /*coincidentPointTrick=*/false);
hover.Clear();
}
- pending.operation = DRAGGING_MARQUEE;
+ pending.operation = Pending::DRAGGING_MARQUEE;
orig.marqueePoint =
UnProjectPoint(orig.mouseOnButtonDown);
}
// If the user has started an operation from the menu, but not
// completed it, then just do the selection.
- if(pending.operation < FIRST_PENDING) {
+ if(pending.operation == Pending::COMMAND) {
HitTestMakeSelection(mp);
return;
}
// painted since the last time we solved, do nothing, because there's
// no sense solving a frame and not displaying it.
if(!havePainted) {
- if(pending.operation == DRAGGING_POINTS && ctrlDown) {
+ if(pending.operation == Pending::DRAGGING_POINTS && ctrlDown) {
SS.extraLine.ptA = UnProjectPoint(orig.mouseOnButtonDown);
SS.extraLine.ptB = UnProjectPoint(mp);
SS.extraLine.draw = true;
havePainted = false;
switch(pending.operation) {
- case DRAGGING_CONSTRAINT: {
+ case Pending::DRAGGING_CONSTRAINT: {
Constraint *c = SK.constraint.FindById(pending.constraint);
UpdateDraggedNum(&(c->disp.offset), x, y);
orig.mouse = mp;
- InvalidateGraphics();
- break;
+ Invalidate();
+ return;
}
- case DRAGGING_NEW_LINE_POINT:
+ case Pending::DRAGGING_NEW_LINE_POINT:
if(!ctrlDown) {
- SS.GW.pending.suggestion =
- SS.GW.SuggestLineConstraint(SS.GW.pending.request);
+ SS.GW.pending.hasSuggestion =
+ SS.GW.SuggestLineConstraint(SS.GW.pending.request, &SS.GW.pending.suggestion);
} else {
- SS.GW.pending.suggestion = SUGGESTED_NONE;
+ SS.GW.pending.hasSuggestion = false;
}
- case DRAGGING_NEW_POINT:
+ // fallthrough
+ case Pending::DRAGGING_NEW_POINT:
UpdateDraggedPoint(pending.point, x, y);
HitTestMakeSelection(mp);
SS.MarkGroupDirtyByEntity(pending.point);
orig.mouse = mp;
- InvalidateGraphics();
+ Invalidate();
break;
- case DRAGGING_POINTS:
+ case Pending::DRAGGING_POINTS:
if(shiftDown || ctrlDown) {
// Edit the rotation associated with a POINT_N_ROT_TRANS,
// either within (ctrlDown) or out of (shiftDown) the plane
List<hEntity> *lhe = &(pending.points);
for(hEntity *he = lhe->First(); he; he = lhe->NextAfter(he)) {
Entity *e = SK.GetEntity(*he);
- if(e->type != Entity::POINT_N_ROT_TRANS) {
+ if(e->type != Entity::Type::POINT_N_ROT_TRANS) {
if(ctrlDown) {
Vector p = e->PointGetNum();
p = p.Minus(SS.extraLine.ptA);
}
break;
- case DRAGGING_NEW_CUBIC_POINT: {
+ case Pending::DRAGGING_NEW_CUBIC_POINT: {
UpdateDraggedPoint(pending.point, x, y);
HitTestMakeSelection(mp);
hRequest hr = pending.point.request();
- if(pending.point.v == hr.entity(4).v) {
+ if(pending.point == hr.entity(4)) {
// The very first segment; dragging final point drags both
// tangent points.
Vector p0 = SK.GetEntity(hr.entity(1))->PointGetNum(),
SS.MarkGroupDirtyByEntity(pending.point);
break;
}
- case DRAGGING_NEW_ARC_POINT: {
+ case Pending::DRAGGING_NEW_ARC_POINT: {
UpdateDraggedPoint(pending.point, x, y);
HitTestMakeSelection(mp);
SS.MarkGroupDirtyByEntity(pending.point);
break;
}
- case DRAGGING_NEW_RADIUS:
- case DRAGGING_RADIUS: {
+ case Pending::DRAGGING_NEW_RADIUS:
+ case Pending::DRAGGING_RADIUS: {
Entity *circle = SK.GetEntity(pending.circle);
Vector center = SK.GetEntity(circle->point[0])->PointGetNum();
Point2d c2 = ProjectPoint(center);
break;
}
- case DRAGGING_NORMAL: {
+ case Pending::DRAGGING_NORMAL: {
Entity *normal = SK.GetEntity(pending.normal);
Vector p = SK.GetEntity(normal->point[0])->PointGetNum();
Point2d p2 = ProjectPoint(p);
break;
}
- case DRAGGING_MARQUEE:
+ case Pending::DRAGGING_MARQUEE:
orig.mouse = mp;
- InvalidateGraphics();
- break;
-
- default: oops();
- }
+ Invalidate();
+ return;
- if(pending.operation != 0 &&
- pending.operation != DRAGGING_CONSTRAINT &&
- pending.operation != DRAGGING_MARQUEE)
- {
- SS.GenerateAll();
-
- // Activate degraded mode, and regenerate display items without edges.
- if(activeGroup.v != 0) {
- bool showEdges = SS.GW.showEdges;
- SS.GW.showEdges = false;
- SK.GetGroup(activeGroup)->GenerateDisplayItems();
- SS.GW.showEdges = showEdges;
- isDegraded = true;
- }
+ case Pending::NONE:
+ case Pending::COMMAND:
+ ssassert(false, "Unexpected pending operation");
}
}
-void GraphicsWindow::ClearPending(void) {
+void GraphicsWindow::ClearPending(bool scheduleShowTW) {
pending.points.Clear();
+ pending.requests.Clear();
pending = {};
- SS.ScheduleShowTW();
+ if(scheduleShowTW) {
+ SS.ScheduleShowTW();
+ }
+}
- // If degraded mode was enabled, we need to regenerate again to get edges back.
- if(isDegraded) {
- isDegraded = false;
- SK.GetGroup(activeGroup)->displayDirty = true;
+bool GraphicsWindow::IsFromPending(hRequest r) {
+ for(auto &req : pending.requests) {
+ if(req == r) return true;
+ }
+ return false;
+}
+
+void GraphicsWindow::AddToPending(hRequest r) {
+ pending.requests.Add(&r);
+}
+
+void GraphicsWindow::ReplacePending(hRequest before, hRequest after) {
+ for(auto &req : pending.requests) {
+ if(req == before) {
+ req = after;
+ }
}
}
void GraphicsWindow::MouseMiddleOrRightDown(double x, double y) {
- if(GraphicsEditControlIsVisible()) return;
+ if(window->IsEditorVisible()) return;
orig.offset = offset;
orig.projUp = projUp;
orig.startedMoving = false;
}
-void GraphicsWindow::ContextMenuListStyles(void) {
- CreateContextSubmenu();
- Style *s;
- bool empty = true;
- for(s = SK.style.First(); s; s = SK.style.NextAfter(s)) {
- if(s->h.v < Style::FIRST_CUSTOM) continue;
-
- AddContextMenuItem(s->DescriptionString().c_str(), CMNU_FIRST_STYLE + s->h.v);
- empty = false;
- }
-
- if(!empty) AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
-
- AddContextMenuItem("No Style", CMNU_NO_STYLE);
- AddContextMenuItem("Newly Created Custom Style...", CMNU_NEW_CUSTOM_STYLE);
-}
-
void GraphicsWindow::MouseRightUp(double x, double y) {
SS.extraLine.draw = false;
- InvalidateGraphics();
+ Invalidate();
// Don't show a context menu if the user is right-clicking the toolbar,
// or if they are finishing a pan.
if(context.active) return;
- if(pending.operation == DRAGGING_NEW_LINE_POINT) {
- if(SS.GW.pending.suggestion != SUGGESTED_NONE) {
- Constraint::Constrain(SS.GW.pending.suggestion,
- Entity::NO_ENTITY, Entity::NO_ENTITY, pending.request.entity(0));
- }
+ if(pending.operation == Pending::DRAGGING_NEW_LINE_POINT && pending.hasSuggestion) {
+ Constraint::TryConstrain(SS.GW.pending.suggestion,
+ Entity::NO_ENTITY, Entity::NO_ENTITY, pending.request.entity(0));
}
- if(pending.operation == DRAGGING_NEW_LINE_POINT ||
- pending.operation == DRAGGING_NEW_CUBIC_POINT)
+ if(pending.operation == Pending::DRAGGING_NEW_LINE_POINT ||
+ pending.operation == Pending::DRAGGING_NEW_CUBIC_POINT)
{
// Special case; use a right click to stop drawing lines, since
// a left click would draw another one. This is quicker and more
v = v.Plus(projRight.ScaledBy(x/scale));
v = v.Plus(projUp.ScaledBy(y/scale));
+ Platform::MenuRef menu = Platform::CreateMenu();
context.active = true;
if(!hover.IsEmpty()) {
GroupSelection();
bool itemsSelected = (gs.n > 0 || gs.constraints > 0);
- int addAfterPoint = -1;
-
if(itemsSelected) {
if(gs.stylables > 0) {
- ContextMenuListStyles();
- AddContextMenuItem("Assign to Style", CONTEXT_SUBMENU);
+ Platform::MenuRef styleMenu = menu->AddSubMenu(_("Assign to Style"));
+
+ bool empty = true;
+ for(const Style &s : SK.style) {
+ if(s.h.v < Style::FIRST_CUSTOM) continue;
+
+ uint32_t v = s.h.v;
+
+ styleMenu->AddItem(s.DescriptionString(), [v]() {
+ Style::AssignSelectionToStyle(v);
+ });
+ empty = false;
+ }
+
+ if(!empty) styleMenu->AddSeparator();
+
+ styleMenu->AddItem(_("No Style"), []() {
+ Style::AssignSelectionToStyle(0);
+ });
+ styleMenu->AddItem(_("Newly Created Custom Style..."), [this]() {
+ uint32_t vs = Style::CreateCustomStyle();
+ Style::AssignSelectionToStyle(vs);
+ ForceTextWindowShown();
+ });
}
if(gs.n + gs.constraints == 1) {
- AddContextMenuItem("Group Info", CMNU_GROUP_INFO);
+ menu->AddItem(_("Group Info"), [this]() {
+ hGroup hg;
+ if(gs.entities == 1) {
+ hg = SK.GetEntity(gs.entity[0])->group;
+ } else if(gs.points == 1) {
+ hg = SK.GetEntity(gs.point[0])->group;
+ } else if(gs.constraints == 1) {
+ hg = SK.GetConstraint(gs.constraint[0])->group;
+ } else {
+ return;
+ }
+ ClearSelection();
+
+ SS.TW.GoToScreen(TextWindow::Screen::GROUP_INFO);
+ SS.TW.shown.group = hg;
+ SS.ScheduleShowTW();
+ ForceTextWindowShown();
+ });
}
if(gs.n + gs.constraints == 1 && gs.stylables == 1) {
- AddContextMenuItem("Style Info", CMNU_STYLE_INFO);
+ menu->AddItem(_("Style Info"), [this]() {
+ hStyle hs;
+ if(gs.entities == 1) {
+ hs = Style::ForEntity(gs.entity[0]);
+ } else if(gs.points == 1) {
+ hs = Style::ForEntity(gs.point[0]);
+ } else if(gs.constraints == 1) {
+ hs = SK.GetConstraint(gs.constraint[0])->GetStyle();
+ } else {
+ return;
+ }
+ ClearSelection();
+
+ SS.TW.GoToScreen(TextWindow::Screen::STYLE_INFO);
+ SS.TW.shown.style = hs;
+ SS.ScheduleShowTW();
+ ForceTextWindowShown();
+ });
}
if(gs.withEndpoints > 0) {
- AddContextMenuItem("Select Edge Chain", CMNU_SELECT_CHAIN);
+ menu->AddItem(_("Select Edge Chain"),
+ []() { MenuEdit(Command::SELECT_CHAIN); });
}
if(gs.constraints == 1 && gs.n == 0) {
Constraint *c = SK.GetConstraint(gs.constraint[0]);
- if(c->HasLabel() && c->type != Constraint::COMMENT) {
- AddContextMenuItem("Toggle Reference Dimension",
- CMNU_REFERENCE_DIM);
+ if(c->HasLabel() && c->type != Constraint::Type::COMMENT) {
+ menu->AddItem(_("Toggle Reference Dimension"),
+ []() { Constraint::MenuConstrain(Command::REFERENCE); });
}
- if(c->type == Constraint::ANGLE ||
- c->type == Constraint::EQUAL_ANGLE)
+ if(c->type == Constraint::Type::ANGLE ||
+ c->type == Constraint::Type::EQUAL_ANGLE)
{
- AddContextMenuItem("Other Supplementary Angle",
- CMNU_OTHER_ANGLE);
+ menu->AddItem(_("Other Supplementary Angle"),
+ []() { Constraint::MenuConstrain(Command::OTHER_ANGLE); });
}
}
if(gs.constraintLabels > 0 || gs.points > 0) {
- AddContextMenuItem("Snap to Grid", CMNU_SNAP_TO_GRID);
+ menu->AddItem(_("Snap to Grid"),
+ []() { MenuEdit(Command::SNAP_TO_GRID); });
}
-
if(gs.points == 1 && gs.point[0].isFromRequest()) {
Request *r = SK.GetRequest(gs.point[0].request());
int index = r->IndexOfPoint(gs.point[0]);
- if((r->type == Request::CUBIC && (index > 1 && index < r->extraPoints + 2)) ||
- r->type == Request::CUBIC_PERIODIC) {
- AddContextMenuItem("Remove Spline Point", CMNU_REMOVE_SPLINE_PT);
+ if((r->type == Request::Type::CUBIC && (index > 1 && index < r->extraPoints + 2)) ||
+ r->type == Request::Type::CUBIC_PERIODIC) {
+ menu->AddItem(_("Remove Spline Point"), [this, r]() {
+ int index = r->IndexOfPoint(gs.point[0]);
+ ssassert(r->extraPoints != 0,
+ "Expected a bezier with interior control points");
+
+ SS.UndoRemember();
+ Entity *e = SK.GetEntity(r->h.entity(0));
+
+ // First, fix point-coincident constraints involving this point.
+ // Then, remove all other constraints, since they would otherwise
+ // jump to an adjacent one and mess up the bezier after generation.
+ FixConstraintsForPointBeingDeleted(e->point[index]);
+ RemoveConstraintsForPointBeingDeleted(e->point[index]);
+
+ for(int i = index; i < MAX_POINTS_IN_ENTITY - 1; i++) {
+ if(e->point[i + 1].v == 0) break;
+ Entity *p0 = SK.GetEntity(e->point[i]);
+ Entity *p1 = SK.GetEntity(e->point[i + 1]);
+ ReplacePointInConstraints(p1->h, p0->h);
+ p0->PointForceTo(p1->PointGetNum());
+ }
+ r->extraPoints--;
+ SS.MarkGroupDirtyByEntity(gs.point[0]);
+ ClearSelection();
+ });
}
}
if(gs.entities == 1 && gs.entity[0].isFromRequest()) {
Request *r = SK.GetRequest(gs.entity[0].request());
- if(r->type == Request::CUBIC || r->type == Request::CUBIC_PERIODIC) {
+ if(r->type == Request::Type::CUBIC || r->type == Request::Type::CUBIC_PERIODIC) {
Entity *e = SK.GetEntity(gs.entity[0]);
- e->GetDistance(Point2d::From(x, y));
- addAfterPoint = e->dogd.data;
- if(addAfterPoint == -1) oops();
+ int addAfterPoint = e->GetPositionOfPoint(GetCamera(), Point2d::From(x, y));
+ ssassert(addAfterPoint != -1, "Expected a nearest bezier point to be located");
// Skip derivative point.
- if(r->type == Request::CUBIC) addAfterPoint++;
- AddContextMenuItem("Add Spline Point", CMNU_ADD_SPLINE_PT);
+ if(r->type == Request::Type::CUBIC) addAfterPoint++;
+ menu->AddItem(_("Add Spline Point"), [this, r, addAfterPoint, v]() {
+ int pointCount = r->extraPoints +
+ ((r->type == Request::Type::CUBIC_PERIODIC) ? 3 : 4);
+ if(pointCount >= MAX_POINTS_IN_ENTITY) {
+ Error(_("Cannot add spline point: maximum number of points reached."));
+ return;
+ }
+
+ SS.UndoRemember();
+ r->extraPoints++;
+ SS.MarkGroupDirtyByEntity(gs.entity[0]);
+ SS.GenerateAll(SolveSpaceUI::Generate::REGEN);
+
+ Entity *e = SK.GetEntity(r->h.entity(0));
+ for(int i = MAX_POINTS_IN_ENTITY; i > addAfterPoint + 1; i--) {
+ Entity *p0 = SK.entity.FindByIdNoOops(e->point[i]);
+ if(p0 == NULL) continue;
+ Entity *p1 = SK.GetEntity(e->point[i - 1]);
+ ReplacePointInConstraints(p1->h, p0->h);
+ p0->PointForceTo(p1->PointGetNum());
+ }
+ Entity *p = SK.GetEntity(e->point[addAfterPoint + 1]);
+ p->PointForceTo(v);
+ SS.MarkGroupDirtyByEntity(gs.entity[0]);
+ ClearSelection();
+ });
}
}
+ if(gs.entities == gs.n) {
+ menu->AddItem(_("Toggle Construction"),
+ []() { MenuRequest(Command::CONSTRUCTION); });
+ }
if(gs.points == 1) {
Entity *p = SK.GetEntity(gs.point[0]);
Constraint *c;
IdList<Constraint,hConstraint> *lc = &(SK.constraint);
for(c = lc->First(); c; c = lc->NextAfter(c)) {
- if(c->type != Constraint::POINTS_COINCIDENT) continue;
- if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) {
+ if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
+ if(c->ptA == p->h || c->ptB == p->h) {
break;
}
}
if(c) {
- AddContextMenuItem("Delete Point-Coincident Constraint",
- CMNU_DEL_COINCIDENT);
+ menu->AddItem(_("Delete Point-Coincident Constraint"), [this, p]() {
+ if(!p->IsPoint()) return;
+
+ SS.UndoRemember();
+ SK.constraint.ClearTags();
+ Constraint *c;
+ for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
+ if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
+ if(c->ptA == p->h || c->ptB == p->h) {
+ c->tag = 1;
+ }
+ }
+ SK.constraint.RemoveTagged();
+ ClearSelection();
+ });
}
}
- AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
+ menu->AddSeparator();
if(LockedInWorkplane()) {
- AddContextMenuItem("Cut", CMNU_CUT_SEL);
- AddContextMenuItem("Copy", CMNU_COPY_SEL);
+ menu->AddItem(_("Cut"),
+ []() { MenuClipboard(Command::CUT); });
+ menu->AddItem(_("Copy"),
+ []() { MenuClipboard(Command::COPY); });
}
+ } else {
+ menu->AddItem(_("Select All"),
+ []() { MenuEdit(Command::SELECT_ALL); });
}
- if((SS.clipboard.r.n > 0 || SS.clipboard.c.n > 0) && LockedInWorkplane()) {
- AddContextMenuItem("Paste", CMNU_PASTE);
- AddContextMenuItem("Paste Transformed...", CMNU_PASTE_XFRM);
+ if((!SS.clipboard.r.IsEmpty() || !SS.clipboard.c.IsEmpty()) && LockedInWorkplane()) {
+ menu->AddItem(_("Paste"),
+ []() { MenuClipboard(Command::PASTE); });
+ menu->AddItem(_("Paste Transformed..."),
+ []() { MenuClipboard(Command::PASTE_TRANSFORM); });
}
if(itemsSelected) {
- AddContextMenuItem("Delete", CMNU_DELETE_SEL);
- AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
- AddContextMenuItem("Unselect All", CMNU_UNSELECT_ALL);
+ menu->AddItem(_("Delete"),
+ []() { MenuClipboard(Command::DELETE); });
+ menu->AddSeparator();
+ menu->AddItem(_("Unselect All"),
+ []() { MenuEdit(Command::UNSELECT_ALL); });
}
// If only one item is selected, then it must be the one that we just
// selected from the hovered item; in which case unselect all and hovered
// are equivalent.
if(!hover.IsEmpty() && selection.n > 1) {
- AddContextMenuItem("Unselect Hovered", CMNU_UNSELECT_HOVERED);
- }
-
- int ret = ShowContextMenu();
- switch(ret) {
- case CMNU_UNSELECT_ALL:
- MenuEdit(MNU_UNSELECT_ALL);
- break;
-
- case CMNU_UNSELECT_HOVERED:
+ menu->AddItem(_("Unselect Hovered"), [this] {
if(!hover.IsEmpty()) {
- MakeUnselected(&hover, true);
- }
- break;
-
- case CMNU_SELECT_CHAIN:
- MenuEdit(MNU_SELECT_CHAIN);
- break;
-
- case CMNU_CUT_SEL:
- MenuClipboard(MNU_CUT);
- break;
-
- case CMNU_COPY_SEL:
- MenuClipboard(MNU_COPY);
- break;
-
- case CMNU_PASTE:
- MenuClipboard(MNU_PASTE);
- break;
-
- case CMNU_PASTE_XFRM:
- MenuClipboard(MNU_PASTE_TRANSFORM);
- break;
-
- case CMNU_DELETE_SEL:
- MenuClipboard(MNU_DELETE);
- break;
-
- case CMNU_REFERENCE_DIM:
- Constraint::MenuConstrain(MNU_REFERENCE);
- break;
-
- case CMNU_OTHER_ANGLE:
- Constraint::MenuConstrain(MNU_OTHER_ANGLE);
- break;
-
- case CMNU_DEL_COINCIDENT: {
- SS.UndoRemember();
- if(!gs.point[0].v) break;
- Entity *p = SK.GetEntity(gs.point[0]);
- if(!p->IsPoint()) break;
-
- SK.constraint.ClearTags();
- Constraint *c;
- for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
- if(c->type != Constraint::POINTS_COINCIDENT) continue;
- if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) {
- c->tag = 1;
- }
+ MakeUnselected(&hover, /*coincidentPointTrick=*/true);
}
- SK.constraint.RemoveTagged();
- ClearSelection();
- break;
- }
-
- case CMNU_SNAP_TO_GRID:
- MenuEdit(MNU_SNAP_TO_GRID);
- break;
-
- case CMNU_REMOVE_SPLINE_PT: {
- hRequest hr = gs.point[0].request();
- Request *r = SK.GetRequest(hr);
-
- int index = r->IndexOfPoint(gs.point[0]);
- if(r->extraPoints == 0) oops();
-
- SS.UndoRemember();
- Entity *e = SK.GetEntity(r->h.entity(0));
-
- // First, fix point-coincident constraints involving this point.
- // Then, remove all other constraints, since they would otherwise
- // jump to an adjacent one and mess up the bezier after generation.
- FixConstraintsForPointBeingDeleted(e->point[index]);
- RemoveConstraintsForPointBeingDeleted(e->point[index]);
-
- for(int i = index; i < MAX_POINTS_IN_ENTITY - 1; i++) {
- if(e->point[i + 1].v == 0) break;
- Entity *p0 = SK.GetEntity(e->point[i]);
- Entity *p1 = SK.GetEntity(e->point[i + 1]);
- ReplacePointInConstraints(p1->h, p0->h);
- p0->PointForceTo(p1->PointGetNum());
- }
- r->extraPoints--;
- SS.MarkGroupDirtyByEntity(gs.point[0]);
- SS.ScheduleGenerateAll();
- ClearSelection();
- break;
- }
-
- case CMNU_ADD_SPLINE_PT: {
- hRequest hr = gs.entity[0].request();
- Request *r = SK.GetRequest(hr);
-
- int pointCount = r->extraPoints + ((r->type == Request::CUBIC_PERIODIC) ? 3 : 4);
- if(pointCount < MAX_POINTS_IN_ENTITY) {
- SS.UndoRemember();
- r->extraPoints++;
- SS.MarkGroupDirtyByEntity(gs.entity[0]);
- SS.GenerateAll(SolveSpaceUI::GENERATE_REGEN);
-
- Entity *e = SK.GetEntity(r->h.entity(0));
- for(int i = MAX_POINTS_IN_ENTITY; i > addAfterPoint + 1; i--) {
- Entity *p0 = SK.entity.FindByIdNoOops(e->point[i]);
- if(p0 == NULL) continue;
- Entity *p1 = SK.GetEntity(e->point[i - 1]);
- ReplacePointInConstraints(p1->h, p0->h);
- p0->PointForceTo(p1->PointGetNum());
- }
- Entity *p = SK.GetEntity(e->point[addAfterPoint + 1]);
- p->PointForceTo(v);
- SS.MarkGroupDirtyByEntity(gs.entity[0]);
- SS.ScheduleGenerateAll();
- ClearSelection();
- } else {
- Error("Cannot add spline point: maximum number of points reached.");
- }
- break;
- }
-
- case CMNU_GROUP_INFO: {
- hGroup hg;
- if(gs.entities == 1) {
- hg = SK.GetEntity(gs.entity[0])->group;
- } else if(gs.points == 1) {
- hg = SK.GetEntity(gs.point[0])->group;
- } else if(gs.constraints == 1) {
- hg = SK.GetConstraint(gs.constraint[0])->group;
- } else {
- break;
- }
- ClearSelection();
-
- SS.TW.GoToScreen(TextWindow::SCREEN_GROUP_INFO);
- SS.TW.shown.group = hg;
- SS.ScheduleShowTW();
- ForceTextWindowShown();
- break;
- }
-
- case CMNU_STYLE_INFO: {
- hStyle hs;
- if(gs.entities == 1) {
- hs = Style::ForEntity(gs.entity[0]);
- } else if(gs.points == 1) {
- hs = Style::ForEntity(gs.point[0]);
- } else if(gs.constraints == 1) {
- hs = SK.GetConstraint(gs.constraint[0])->GetStyle();
- } else {
- break;
- }
- ClearSelection();
-
- SS.TW.GoToScreen(TextWindow::SCREEN_STYLE_INFO);
- SS.TW.shown.style = hs;
- SS.ScheduleShowTW();
- ForceTextWindowShown();
- break;
- }
-
- case CMNU_NEW_CUSTOM_STYLE: {
- uint32_t v = Style::CreateCustomStyle();
- Style::AssignSelectionToStyle(v);
- ForceTextWindowShown();
- break;
- }
-
- case CMNU_NO_STYLE:
- Style::AssignSelectionToStyle(0);
- break;
+ });
+ }
- default:
- if(ret >= CMNU_FIRST_STYLE) {
- Style::AssignSelectionToStyle(ret - CMNU_FIRST_STYLE);
- } else {
- // otherwise it was cancelled, so do nothing
- contextMenuCancelTime = GetMilliseconds();
- }
- break;
+ if(itemsSelected) {
+ menu->AddSeparator();
+ menu->AddItem(_("Zoom to Fit"),
+ []() { MenuView(Command::ZOOM_TO_FIT); });
}
+ menu->PopUp();
+
context.active = false;
SS.ScheduleShowTW();
}
-hRequest GraphicsWindow::AddRequest(int type) {
- return AddRequest(type, true);
+hRequest GraphicsWindow::AddRequest(Request::Type type) {
+ return AddRequest(type, /*rememberForUndo=*/true);
}
-hRequest GraphicsWindow::AddRequest(int type, bool rememberForUndo) {
+hRequest GraphicsWindow::AddRequest(Request::Type type, bool rememberForUndo) {
if(rememberForUndo) SS.UndoRemember();
Request r = {};
r.group = activeGroup;
Group *g = SK.GetGroup(activeGroup);
- if(g->type == Group::DRAWING_3D || g->type == Group::DRAWING_WORKPLANE) {
+ if(g->type == Group::Type::DRAWING_3D || g->type == Group::Type::DRAWING_WORKPLANE) {
r.construction = false;
} else {
r.construction = true;
// place this request's entities where the mouse is can do so. But
// we mustn't try to solve until reasonable values have been supplied
// for these new parameters, or else we'll get a numerical blowup.
- SS.GenerateAll(SolveSpaceUI::GENERATE_REGEN);
+ r.Generate(&SK.entity, &SK.param);
SS.MarkGroupDirty(r.group);
return r.h;
}
-bool GraphicsWindow::ConstrainPointByHovered(hEntity pt) {
+Vector GraphicsWindow::SnapToEntityByScreenPoint(Point2d pp, hEntity he) {
+ Entity *e = SK.GetEntity(he);
+ if(e->IsPoint()) return e->PointGetNum();
+ SEdgeList *edges = e->GetOrGenerateEdges();
+
+ double minD = -1.0f;
+ double k;
+ const SEdge *edge = NULL;
+ for(const auto &e : edges->l) {
+ Point2d p0 = ProjectPoint(e.a);
+ Point2d p1 = ProjectPoint(e.b);
+ Point2d dir = p1.Minus(p0);
+ double d = pp.DistanceToLine(p0, dir, /*asSegment=*/true);
+ if(minD > 0.0 && d > minD) continue;
+ minD = d;
+ k = pp.Minus(p0).Dot(dir) / dir.Dot(dir);
+ edge = &e;
+ }
+ if(edge == NULL) return UnProjectPoint(pp);
+ return edge->a.Plus(edge->b.Minus(edge->a).ScaledBy(k));
+}
+
+bool GraphicsWindow::ConstrainPointByHovered(hEntity pt, const Point2d *projected) {
if(!hover.entity.v) return false;
+ Entity *point = SK.GetEntity(pt);
Entity *e = SK.GetEntity(hover.entity);
if(e->IsPoint()) {
+ point->PointForceTo(e->PointGetNum());
Constraint::ConstrainCoincident(e->h, pt);
return true;
}
if(e->IsCircle()) {
- Constraint::Constrain(Constraint::PT_ON_CIRCLE,
+ if(projected != NULL) {
+ Vector snapPos = SnapToEntityByScreenPoint(*projected, e->h);
+ point->PointForceTo(snapPos);
+ }
+ Constraint::Constrain(Constraint::Type::PT_ON_CIRCLE,
pt, Entity::NO_ENTITY, e->h);
return true;
}
- if(e->type == Entity::LINE_SEGMENT) {
- Constraint::Constrain(Constraint::PT_ON_LINE,
+ if(e->type == Entity::Type::LINE_SEGMENT) {
+ if(projected != NULL) {
+ Vector snapPos = SnapToEntityByScreenPoint(*projected, e->h);
+ point->PointForceTo(snapPos);
+ }
+ Constraint::Constrain(Constraint::Type::PT_ON_LINE,
pt, Entity::NO_ENTITY, e->h);
return true;
}
return false;
}
-void GraphicsWindow::MouseLeftDown(double mx, double my) {
+bool GraphicsWindow::MouseEvent(Platform::MouseEvent event) {
+ using Platform::MouseEvent;
+
+ double width, height;
+ window->GetContentSize(&width, &height);
+
+ event.x = event.x - width / 2;
+ event.y = height / 2 - event.y;
+
+ switch(event.type) {
+ case MouseEvent::Type::MOTION:
+ this->MouseMoved(event.x, event.y,
+ event.button == MouseEvent::Button::LEFT,
+ event.button == MouseEvent::Button::MIDDLE,
+ event.button == MouseEvent::Button::RIGHT,
+ event.shiftDown,
+ event.controlDown);
+ break;
+
+ case MouseEvent::Type::PRESS:
+ if(event.button == MouseEvent::Button::LEFT) {
+ this->MouseLeftDown(event.x, event.y, event.shiftDown, event.controlDown);
+ } else if(event.button == MouseEvent::Button::MIDDLE ||
+ event.button == MouseEvent::Button::RIGHT) {
+ this->MouseMiddleOrRightDown(event.x, event.y);
+ }
+ break;
+
+ case MouseEvent::Type::DBL_PRESS:
+ if(event.button == MouseEvent::Button::LEFT) {
+ this->MouseLeftDoubleClick(event.x, event.y);
+ }
+ break;
+
+ case MouseEvent::Type::RELEASE:
+ if(event.button == MouseEvent::Button::LEFT) {
+ this->MouseLeftUp(event.x, event.y, event.shiftDown, event.controlDown);
+ } else if(event.button == MouseEvent::Button::RIGHT) {
+ this->MouseRightUp(event.x, event.y);
+ }
+ break;
+
+ case MouseEvent::Type::SCROLL_VERT:
+ this->MouseScroll(event.x, event.y, (int)event.scrollDelta);
+ break;
+
+ case MouseEvent::Type::LEAVE:
+ this->MouseLeave();
+ break;
+ }
+
+ return true;
+}
+
+void GraphicsWindow::MouseLeftDown(double mx, double my, bool shiftDown, bool ctrlDown) {
orig.mouseDown = true;
- if(GraphicsEditControlIsVisible()) {
+ if(window->IsEditorVisible()) {
orig.mouse = Point2d::From(mx, my);
orig.mouseOnButtonDown = orig.mouse;
- HideGraphicsEditControl();
+ window->HideEditor();
return;
}
SS.TW.HideEditControl();
}
// This will be clobbered by MouseMoved below.
- SuggestedConstraint constraintSuggestion = SS.GW.pending.suggestion;
+ bool hasConstraintSuggestion = pending.hasSuggestion;
+ Constraint::Type constraintSuggestion = pending.suggestion;
// Make sure the hover is up to date.
- MouseMoved(mx, my, false, false, false, false, false);
+ MouseMoved(mx, my, /*leftDown=*/false, /*middleDown=*/false, /*rightDown=*/false,
+ /*shiftDown=*/false, /*ctrlDown=*/false);
orig.mouse.x = mx;
orig.mouse.y = my;
orig.mouseOnButtonDown = orig.mouse;
+ Point2d mouse = Point2d::From(mx, my);
// The current mouse location
Vector v = offset.ScaledBy(-1);
hRequest hr = {};
hConstraint hc = {};
switch(pending.operation) {
- case MNU_DATUM_POINT:
- hr = AddRequest(Request::DATUM_POINT);
- SK.GetEntity(hr.entity(0))->PointForceTo(v);
- ConstrainPointByHovered(hr.entity(0));
-
- ClearSuper();
- break;
+ case Pending::COMMAND:
+ switch(pending.command) {
+ case Command::DATUM_POINT:
+ hr = AddRequest(Request::Type::DATUM_POINT);
+ SK.GetEntity(hr.entity(0))->PointForceTo(v);
+ ConstrainPointByHovered(hr.entity(0), &mouse);
+
+ ClearSuper();
+ break;
- case MNU_LINE_SEGMENT:
- case MNU_CONSTR_SEGMENT:
- hr = AddRequest(Request::LINE_SEGMENT);
- SK.GetRequest(hr)->construction = (pending.operation == MNU_CONSTR_SEGMENT);
- SK.GetEntity(hr.entity(1))->PointForceTo(v);
- ConstrainPointByHovered(hr.entity(1));
+ case Command::LINE_SEGMENT:
+ case Command::CONSTR_SEGMENT:
+ hr = AddRequest(Request::Type::LINE_SEGMENT);
+ SK.GetRequest(hr)->construction = (pending.command == Command::CONSTR_SEGMENT);
+ SK.GetEntity(hr.entity(1))->PointForceTo(v);
+ ConstrainPointByHovered(hr.entity(1), &mouse);
+
+ ClearSuper();
+ AddToPending(hr);
+
+ pending.operation = Pending::DRAGGING_NEW_LINE_POINT;
+ pending.request = hr;
+ pending.point = hr.entity(2);
+ pending.description = _("click next point of line, or press Esc");
+ SK.GetEntity(pending.point)->PointForceTo(v);
+ break;
- ClearSuper();
+ case Command::RECTANGLE: {
+ if(!SS.GW.LockedInWorkplane()) {
+ Error(_("Can't draw rectangle in 3d; first, activate a workplane "
+ "with Sketch -> In Workplane."));
+ ClearSuper();
+ break;
+ }
+ hRequest lns[4];
+ int i;
+ SS.UndoRemember();
+ for(i = 0; i < 4; i++) {
+ lns[i] = AddRequest(Request::Type::LINE_SEGMENT, /*rememberForUndo=*/false);
+ AddToPending(lns[i]);
+ }
+ for(i = 0; i < 4; i++) {
+ Constraint::ConstrainCoincident(
+ lns[i].entity(1), lns[(i+1)%4].entity(2));
+ SK.GetEntity(lns[i].entity(1))->PointForceTo(v);
+ SK.GetEntity(lns[i].entity(2))->PointForceTo(v);
+ }
+ for(i = 0; i < 4; i++) {
+ Constraint::Constrain(
+ (i % 2) ? Constraint::Type::HORIZONTAL : Constraint::Type::VERTICAL,
+ Entity::NO_ENTITY, Entity::NO_ENTITY,
+ lns[i].entity(0));
+ }
+ if(ConstrainPointByHovered(lns[2].entity(1), &mouse)) {
+ Vector pos = SK.GetEntity(lns[2].entity(1))->PointGetNum();
+ for(i = 0; i < 4; i++) {
+ SK.GetEntity(lns[i].entity(1))->PointForceTo(pos);
+ SK.GetEntity(lns[i].entity(2))->PointForceTo(pos);
+ }
+ }
- pending.operation = DRAGGING_NEW_LINE_POINT;
- pending.request = hr;
- pending.point = hr.entity(2);
- pending.description = "click next point of line, or press Esc";
- SK.GetEntity(pending.point)->PointForceTo(v);
- break;
+ pending.operation = Pending::DRAGGING_NEW_POINT;
+ pending.point = lns[1].entity(2);
+ pending.description = _("click to place other corner of rectangle");
+ hr = lns[0];
+ break;
+ }
+ case Command::CIRCLE:
+ hr = AddRequest(Request::Type::CIRCLE);
+ // Centered where we clicked
+ SK.GetEntity(hr.entity(1))->PointForceTo(v);
+ // Normal to the screen
+ SK.GetEntity(hr.entity(32))->NormalForceTo(
+ Quaternion::From(SS.GW.projRight, SS.GW.projUp));
+ // Initial radius zero
+ SK.GetEntity(hr.entity(64))->DistanceForceTo(0);
+
+ ConstrainPointByHovered(hr.entity(1), &mouse);
+
+ ClearSuper();
+
+ pending.operation = Pending::DRAGGING_NEW_RADIUS;
+ pending.circle = hr.entity(0);
+ pending.description = _("click to set radius");
+ break;
- case MNU_RECTANGLE: {
- if(!SS.GW.LockedInWorkplane()) {
- Error("Can't draw rectangle in 3d; select a workplane first.");
- ClearSuper();
- break;
- }
- hRequest lns[4];
- int i;
- SS.UndoRemember();
- for(i = 0; i < 4; i++) {
- lns[i] = AddRequest(Request::LINE_SEGMENT, false);
- }
- for(i = 0; i < 4; i++) {
- Constraint::ConstrainCoincident(
- lns[i].entity(1), lns[(i+1)%4].entity(2));
- SK.GetEntity(lns[i].entity(1))->PointForceTo(v);
- SK.GetEntity(lns[i].entity(2))->PointForceTo(v);
- }
- for(i = 0; i < 4; i++) {
- Constraint::Constrain(
- (i % 2) ? Constraint::HORIZONTAL : Constraint::VERTICAL,
- Entity::NO_ENTITY, Entity::NO_ENTITY,
- lns[i].entity(0));
- }
- ConstrainPointByHovered(lns[2].entity(1));
+ case Command::ARC: {
+ if(!SS.GW.LockedInWorkplane()) {
+ Error(_("Can't draw arc in 3d; first, activate a workplane "
+ "with Sketch -> In Workplane."));
+ ClearPending();
+ break;
+ }
+ hr = AddRequest(Request::Type::ARC_OF_CIRCLE);
+ // This fudge factor stops us from immediately failing to solve
+ // because of the arc's implicit (equal radius) tangent.
+ Vector adj = SS.GW.projRight.WithMagnitude(2/SS.GW.scale);
+ SK.GetEntity(hr.entity(1))->PointForceTo(v.Minus(adj));
+ SK.GetEntity(hr.entity(2))->PointForceTo(v);
+ SK.GetEntity(hr.entity(3))->PointForceTo(v);
+ ConstrainPointByHovered(hr.entity(2), &mouse);
+
+ ClearSuper();
+ AddToPending(hr);
+
+ pending.operation = Pending::DRAGGING_NEW_ARC_POINT;
+ pending.point = hr.entity(3);
+ pending.description = _("click to place point");
+ break;
+ }
+ case Command::CUBIC:
+ hr = AddRequest(Request::Type::CUBIC);
+ SK.GetEntity(hr.entity(1))->PointForceTo(v);
+ SK.GetEntity(hr.entity(2))->PointForceTo(v);
+ SK.GetEntity(hr.entity(3))->PointForceTo(v);
+ SK.GetEntity(hr.entity(4))->PointForceTo(v);
+ ConstrainPointByHovered(hr.entity(1), &mouse);
+
+ ClearSuper();
+ AddToPending(hr);
+
+ pending.operation = Pending::DRAGGING_NEW_CUBIC_POINT;
+ pending.point = hr.entity(4);
+ pending.description = _("click next point of cubic, or press Esc");
+ break;
- pending.operation = DRAGGING_NEW_POINT;
- pending.point = lns[1].entity(2);
- pending.description = "click to place other corner of rectangle";
- hr = lns[0];
- break;
- }
- case MNU_CIRCLE:
- hr = AddRequest(Request::CIRCLE);
- // Centered where we clicked
- SK.GetEntity(hr.entity(1))->PointForceTo(v);
- // Normal to the screen
- SK.GetEntity(hr.entity(32))->NormalForceTo(
- Quaternion::From(SS.GW.projRight, SS.GW.projUp));
- // Initial radius zero
- SK.GetEntity(hr.entity(64))->DistanceForceTo(0);
-
- ConstrainPointByHovered(hr.entity(1));
-
- ClearSuper();
-
- pending.operation = DRAGGING_NEW_RADIUS;
- pending.circle = hr.entity(0);
- pending.description = "click to set radius";
- break;
+ case Command::WORKPLANE:
+ if(LockedInWorkplane()) {
+ Error(_("Sketching in a workplane already; sketch in 3d before "
+ "creating new workplane."));
+ ClearSuper();
+ break;
+ }
+ hr = AddRequest(Request::Type::WORKPLANE);
+ SK.GetEntity(hr.entity(1))->PointForceTo(v);
+ SK.GetEntity(hr.entity(32))->NormalForceTo(
+ Quaternion::From(SS.GW.projRight, SS.GW.projUp));
+ ConstrainPointByHovered(hr.entity(1), &mouse);
- case MNU_ARC: {
- if(!SS.GW.LockedInWorkplane()) {
- Error("Can't draw arc in 3d; select a workplane first.");
- ClearPending();
- break;
- }
- hr = AddRequest(Request::ARC_OF_CIRCLE);
- // This fudge factor stops us from immediately failing to solve
- // because of the arc's implicit (equal radius) tangent.
- Vector adj = SS.GW.projRight.WithMagnitude(2/SS.GW.scale);
- SK.GetEntity(hr.entity(1))->PointForceTo(v.Minus(adj));
- SK.GetEntity(hr.entity(2))->PointForceTo(v);
- SK.GetEntity(hr.entity(3))->PointForceTo(v);
- ConstrainPointByHovered(hr.entity(2));
-
- ClearSuper();
-
- pending.operation = DRAGGING_NEW_ARC_POINT;
- pending.point = hr.entity(3);
- pending.description = "click to place point";
- break;
- }
- case MNU_CUBIC:
- hr = AddRequest(Request::CUBIC);
- SK.GetEntity(hr.entity(1))->PointForceTo(v);
- SK.GetEntity(hr.entity(2))->PointForceTo(v);
- SK.GetEntity(hr.entity(3))->PointForceTo(v);
- SK.GetEntity(hr.entity(4))->PointForceTo(v);
- ConstrainPointByHovered(hr.entity(1));
-
- ClearSuper();
-
- pending.operation = DRAGGING_NEW_CUBIC_POINT;
- pending.point = hr.entity(4);
- pending.description = "click next point of cubic, or press Esc";
- break;
+ ClearSuper();
+ break;
- case MNU_WORKPLANE:
- if(LockedInWorkplane()) {
- Error("Sketching in a workplane already; sketch in 3d before "
- "creating new workplane.");
- ClearSuper();
- break;
- }
- hr = AddRequest(Request::WORKPLANE);
- SK.GetEntity(hr.entity(1))->PointForceTo(v);
- SK.GetEntity(hr.entity(32))->NormalForceTo(
- Quaternion::From(SS.GW.projRight, SS.GW.projUp));
- ConstrainPointByHovered(hr.entity(1));
+ case Command::TTF_TEXT: {
+ if(!SS.GW.LockedInWorkplane()) {
+ Error(_("Can't draw text in 3d; first, activate a workplane "
+ "with Sketch -> In Workplane."));
+ ClearSuper();
+ break;
+ }
+ hr = AddRequest(Request::Type::TTF_TEXT);
+ AddToPending(hr);
+ Request *r = SK.GetRequest(hr);
+ r->str = "Abc";
+ r->font = "BitstreamVeraSans-Roman-builtin.ttf";
+
+ for(int i = 1; i <= 4; i++) {
+ SK.GetEntity(hr.entity(i))->PointForceTo(v);
+ }
- ClearSuper();
- break;
+ pending.operation = Pending::DRAGGING_NEW_POINT;
+ pending.point = hr.entity(3);
+ pending.description = _("click to place bottom right of text");
+ break;
+ }
- case MNU_TTF_TEXT: {
- if(!SS.GW.LockedInWorkplane()) {
- Error("Can't draw text in 3d; select a workplane first.");
- ClearSuper();
- break;
- }
- hr = AddRequest(Request::TTF_TEXT);
- Request *r = SK.GetRequest(hr);
- r->str = "Abc";
- r->font = "arial.ttf";
+ case Command::IMAGE: {
+ if(!SS.GW.LockedInWorkplane()) {
+ Error(_("Can't draw image in 3d; first, activate a workplane "
+ "with Sketch -> In Workplane."));
+ ClearSuper();
+ break;
+ }
+ hr = AddRequest(Request::Type::IMAGE);
+ AddToPending(hr);
+ Request *r = SK.GetRequest(hr);
+ r->file = pending.filename;
- SK.GetEntity(hr.entity(1))->PointForceTo(v);
- SK.GetEntity(hr.entity(2))->PointForceTo(v);
+ for(int i = 1; i <= 4; i++) {
+ SK.GetEntity(hr.entity(i))->PointForceTo(v);
+ }
- pending.operation = DRAGGING_NEW_POINT;
- pending.point = hr.entity(2);
- pending.description = "click to place bottom left of text";
- break;
- }
+ pending.operation = Pending::DRAGGING_NEW_POINT;
+ pending.point = hr.entity(3);
+ pending.description = "click to place bottom right of image";
+ break;
+ }
- case MNU_COMMENT: {
- ClearSuper();
- Constraint c = {};
- c.group = SS.GW.activeGroup;
- c.workplane = SS.GW.ActiveWorkplane();
- c.type = Constraint::COMMENT;
- c.disp.offset = v;
- c.comment = "NEW COMMENT -- DOUBLE-CLICK TO EDIT";
- hc = Constraint::AddConstraint(&c);
+ case Command::COMMENT: {
+ ClearSuper();
+ Constraint c = {};
+ c.group = SS.GW.activeGroup;
+ c.workplane = SS.GW.ActiveWorkplane();
+ c.type = Constraint::Type::COMMENT;
+ c.disp.offset = v;
+ c.comment = _("NEW COMMENT -- DOUBLE-CLICK TO EDIT");
+ hc = Constraint::AddConstraint(&c);
+ break;
+ }
+ default: ssassert(false, "Unexpected pending menu id");
+ }
break;
- }
- case DRAGGING_RADIUS:
- case DRAGGING_NEW_POINT:
- // The MouseMoved event has already dragged it as desired.
+ case Pending::DRAGGING_RADIUS:
ClearPending();
break;
- case DRAGGING_NEW_ARC_POINT:
- ConstrainPointByHovered(pending.point);
+ case Pending::DRAGGING_NEW_POINT:
+ case Pending::DRAGGING_NEW_ARC_POINT:
+ ConstrainPointByHovered(pending.point, &mouse);
ClearPending();
break;
- case DRAGGING_NEW_CUBIC_POINT: {
+ case Pending::DRAGGING_NEW_CUBIC_POINT: {
hRequest hr = pending.point.request();
Request *r = SK.GetRequest(hr);
- if(hover.entity.v == hr.entity(1).v && r->extraPoints >= 2) {
+ if(hover.entity == hr.entity(1) && r->extraPoints >= 2) {
// They want the endpoints coincident, which means a periodic
// spline instead.
- r->type = Request::CUBIC_PERIODIC;
+ r->type = Request::Type::CUBIC_PERIODIC;
// Remove the off-curve control points, which are no longer
// needed here; so move [2,ep+1] down, skipping first pt.
int i;
r->extraPoints -= 2;
// And we're done.
SS.MarkGroupDirty(r->group);
- SS.ScheduleGenerateAll();
ClearPending();
break;
}
- if(ConstrainPointByHovered(pending.point)) {
+ if(ConstrainPointByHovered(pending.point, &mouse)) {
ClearPending();
break;
}
}
(SK.GetRequest(hr)->extraPoints)++;
- SS.GenerateAll(SolveSpaceUI::GENERATE_REGEN);
+ SS.GenerateAll(SolveSpaceUI::Generate::REGEN);
int ep = r->extraPoints;
Vector last = SK.GetEntity(hr.entity(3+ep))->PointGetNum();
break;
}
- case DRAGGING_NEW_LINE_POINT: {
- // Constrain the line segment horizontal or vertical if close enough
- if(constraintSuggestion != SUGGESTED_NONE) {
- Constraint::Constrain(constraintSuggestion,
- Entity::NO_ENTITY, Entity::NO_ENTITY, pending.request.entity(0));
- }
-
+ case Pending::DRAGGING_NEW_LINE_POINT: {
if(hover.entity.v) {
Entity *e = SK.GetEntity(hover.entity);
if(e->IsPoint()) {
}
}
- if(ConstrainPointByHovered(pending.point)) {
+ bool doneDragging = ConstrainPointByHovered(pending.point, &mouse);
+
+ // Constrain the line segment horizontal or vertical if close enough
+ if(hasConstraintSuggestion) {
+ Constraint::TryConstrain(constraintSuggestion,
+ Entity::NO_ENTITY, Entity::NO_ENTITY, pending.request.entity(0));
+ }
+
+ if(doneDragging) {
ClearPending();
break;
}
// Create a new line segment, so that we continue drawing.
- hRequest hr = AddRequest(Request::LINE_SEGMENT);
+ hRequest hr = AddRequest(Request::Type::LINE_SEGMENT);
+ ReplacePending(pending.request, hr);
SK.GetRequest(hr)->construction = SK.GetRequest(pending.request)->construction;
- SK.GetEntity(hr.entity(1))->PointForceTo(v);
// Displace the second point of the new line segment slightly,
// to avoid creating zero-length edge warnings.
SK.GetEntity(hr.entity(2))->PointForceTo(
// Constrain the line segments to share an endpoint
Constraint::ConstrainCoincident(pending.point, hr.entity(1));
+ Vector pendingPos = SK.GetEntity(pending.point)->PointGetNum();
+ SK.GetEntity(hr.entity(1))->PointForceTo(pendingPos);
// And drag an endpoint of the new line segment
- pending.operation = DRAGGING_NEW_LINE_POINT;
+ pending.operation = Pending::DRAGGING_NEW_LINE_POINT;
pending.request = hr;
pending.point = hr.entity(2);
- pending.description = "click next point of line, or press Esc";
+ pending.description = _("click next point of line, or press Esc");
break;
}
- case 0:
+ case Pending::NONE:
default:
ClearPending();
if(!hover.IsEmpty()) {
- hoverWasSelectedOnMousedown = IsSelected(&hover);
- MakeSelected(&hover);
+ if(!ctrlDown) {
+ hoverWasSelectedOnMousedown = IsSelected(&hover);
+ MakeSelected(&hover);
+ } else {
+ MakeUnselected(&hover, /*coincidentPointTrick=*/true);
+ }
}
break;
}
// Activate group with newly created request/constraint
Group *g = NULL;
if(hr.v != 0) {
- Request *req = SK.GetRequest(hr);
- g = SK.GetGroup(req->group);
+ g = SK.GetGroup(SK.GetRequest(hr)->group);
}
if(hc.v != 0) {
- Constraint *c = SK.GetConstraint(hc);
- g = SK.GetGroup(c->group);
+ g = SK.GetGroup(SK.GetConstraint(hc)->group);
}
if(g != NULL) {
g->visible = true;
}
SS.ScheduleShowTW();
- InvalidateGraphics();
+ Invalidate();
}
-void GraphicsWindow::MouseLeftUp(double mx, double my) {
+void GraphicsWindow::MouseLeftUp(double mx, double my, bool shiftDown, bool ctrlDown) {
orig.mouseDown = false;
hoverWasSelectedOnMousedown = false;
switch(pending.operation) {
- case DRAGGING_POINTS:
+ case Pending::DRAGGING_POINTS:
SS.extraLine.draw = false;
// fall through
- case DRAGGING_CONSTRAINT:
- case DRAGGING_NORMAL:
- case DRAGGING_RADIUS:
+ case Pending::DRAGGING_CONSTRAINT:
+ case Pending::DRAGGING_NORMAL:
+ case Pending::DRAGGING_RADIUS:
ClearPending();
- InvalidateGraphics();
+ Invalidate();
break;
- case DRAGGING_MARQUEE:
+ case Pending::DRAGGING_MARQUEE:
SelectByMarquee();
ClearPending();
- InvalidateGraphics();
+ Invalidate();
break;
- case 0:
- // We need to clear the selection here, and not in the mouse down
- // event, since a mouse down without anything hovered could also
- // be the start of marquee selection. But don't do that on the
- // left click to cancel a context menu. The time delay is an ugly
- // hack.
- if(hover.IsEmpty() &&
- (contextMenuCancelTime == 0 ||
- (GetMilliseconds() - contextMenuCancelTime) > 200))
- {
+ case Pending::NONE:
+ if(hover.IsEmpty() && !ctrlDown) {
ClearSelection();
}
break;
}
}
-void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) {
- if(GraphicsEditControlIsVisible()) return;
- SS.TW.HideEditControl();
-
- if(hover.constraint.v) {
- constraintBeingEdited = hover.constraint;
- ClearSuper();
+void GraphicsWindow::EditConstraint(hConstraint constraint) {
+ constraintBeingEdited = constraint;
+ ClearSuper();
- Constraint *c = SK.GetConstraint(constraintBeingEdited);
- if(!c->HasLabel()) {
- // Not meaningful to edit a constraint without a dimension
- return;
- }
- if(c->reference) {
- // Not meaningful to edit a reference dimension
- return;
- }
-
- Vector p3 = c->GetLabelPos();
- Point2d p2 = ProjectPoint(p3);
+ Constraint *c = SK.GetConstraint(constraintBeingEdited);
+ if(!c->HasLabel()) {
+ // Not meaningful to edit a constraint without a dimension
+ return;
+ }
+ if(c->reference) {
+ // Not meaningful to edit a reference dimension
+ return;
+ }
- std::string editValue;
- int editMinWidthChar;
- switch(c->type) {
- case Constraint::COMMENT:
- editValue = c->comment;
- editMinWidthChar = 30;
- break;
+ Vector p3 = c->GetLabelPos(GetCamera());
+ Point2d p2 = ProjectPoint(p3);
- case Constraint::ANGLE:
- case Constraint::LENGTH_RATIO:
- editValue = ssprintf("%.3f", c->valA);
- editMinWidthChar = 5;
- break;
+ std::string editValue;
+ std::string editPlaceholder;
+ switch(c->type) {
+ case Constraint::Type::COMMENT:
+ editValue = c->comment;
+ editPlaceholder = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+ break;
- default: {
- double v = fabs(c->valA);
+ default: {
+ double value = fabs(c->valA);
- // If displayed as radius, also edit as radius.
- if(c->type == Constraint::DIAMETER && c->other)
- v /= 2;
+ // If displayed as radius, also edit as radius.
+ if(c->type == Constraint::Type::DIAMETER && c->other)
+ value /= 2;
- std::string def = SS.MmToString(v);
- double eps = 1e-12;
- if(fabs(SS.StringToMm(def) - v) < eps) {
- // Show value with default number of digits after decimal,
- // which is at least enough to represent it exactly.
- editValue = def;
- } else {
- // Show value with as many digits after decimal as
- // required to represent it exactly, up to 10.
- v /= SS.MmPerUnit();
- int i;
- for(i = 0; i <= 10; i++) {
- editValue = ssprintf("%.*f", i, v);
- if(fabs(std::stod(editValue) - v) < eps) break;
- }
- }
- editMinWidthChar = 5;
- break;
+ // Try showing value with default number of digits after decimal first.
+ if(c->type == Constraint::Type::LENGTH_RATIO) {
+ editValue = ssprintf("%.3f", value);
+ } else if(c->type == Constraint::Type::ANGLE) {
+ editValue = SS.DegreeToString(value);
+ } else {
+ editValue = SS.MmToString(value);
+ value /= SS.MmPerUnit();
}
+ // If that's not enough to represent it exactly, show the value with as many
+ // digits after decimal as required, up to 10.
+ int digits = 0;
+ while(fabs(std::stod(editValue) - value) > 1e-10) {
+ editValue = ssprintf("%.*f", digits, value);
+ digits++;
+ }
+ editPlaceholder = "10.000000";
+ break;
}
- hStyle hs = c->disp.style;
- if(hs.v == 0) hs.v = Style::CONSTRAINT;
- ShowGraphicsEditControl((int)p2.x, (int)p2.y,
- ssglStrFontSize(Style::TextHeight(hs)) * scale,
- editMinWidthChar, editValue);
+ }
+
+ double width, height;
+ window->GetContentSize(&width, &height);
+ hStyle hs = c->disp.style;
+ if(hs.v == 0) hs.v = Style::CONSTRAINT;
+ double capHeight = Style::TextHeight(hs);
+ double fontHeight = VectorFont::Builtin()->GetHeight(capHeight);
+ double editMinWidth = VectorFont::Builtin()->GetWidth(capHeight, editPlaceholder);
+ window->ShowEditor(p2.x + width / 2, height / 2 - p2.y,
+ fontHeight, editMinWidth,
+ /*isMonospace=*/false, editValue);
+}
+
+void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) {
+ if(window->IsEditorVisible()) return;
+ SS.TW.HideEditControl();
+
+ if(hover.constraint.v) {
+ EditConstraint(hover.constraint);
}
}
-void GraphicsWindow::EditControlDone(const char *s) {
- HideGraphicsEditControl();
+void GraphicsWindow::EditControlDone(const std::string &s) {
+ window->HideEditor();
+ window->Invalidate();
+
Constraint *c = SK.GetConstraint(constraintBeingEdited);
- if(c->type == Constraint::COMMENT) {
+ if(c->type == Constraint::Type::COMMENT) {
SS.UndoRemember();
c->comment = s;
return;
}
- Expr *e = Expr::From(s, true);
- if(e) {
+ if(Expr *e = Expr::From(s, true)) {
SS.UndoRemember();
switch(c->type) {
- case Constraint::PROJ_PT_DISTANCE:
- case Constraint::PT_LINE_DISTANCE:
- case Constraint::PT_FACE_DISTANCE:
- case Constraint::PT_PLANE_DISTANCE:
- case Constraint::LENGTH_DIFFERENCE: {
+ case Constraint::Type::PROJ_PT_DISTANCE:
+ case Constraint::Type::PT_LINE_DISTANCE:
+ case Constraint::Type::PT_FACE_DISTANCE:
+ case Constraint::Type::PT_PLANE_DISTANCE:
+ case Constraint::Type::LENGTH_DIFFERENCE: {
// The sign is not displayed to the user, but this is a signed
// distance internally. To flip the sign, the user enters a
// negative distance.
}
break;
}
- case Constraint::ANGLE:
- case Constraint::LENGTH_RATIO:
+ case Constraint::Type::ANGLE:
+ case Constraint::Type::LENGTH_RATIO:
// These don't get the units conversion for distance, and
// they're always positive
c->valA = fabs(e->Eval());
break;
- case Constraint::DIAMETER:
+ case Constraint::Type::DIAMETER:
c->valA = fabs(SS.ExprToMm(e));
// If displayed and edited as radius, convert back
break;
}
SS.MarkGroupDirty(c->group);
- SS.GenerateAll();
- }
-}
-
-bool GraphicsWindow::KeyDown(int c) {
- if(c == '\b') {
- // Treat backspace identically to escape.
- MenuEdit(MNU_UNSELECT_ALL);
- return true;
}
-
- return false;
}
void GraphicsWindow::MouseScroll(double x, double y, int delta) {
offset = offset.Plus(projRight.ScaledBy(rightf - righti));
offset = offset.Plus(projUp.ScaledBy(upf - upi));
- if(SS.TW.shown.screen == TextWindow::SCREEN_EDIT_VIEW) {
+ if(SS.TW.shown.screen == TextWindow::Screen::EDIT_VIEW) {
if(havePainted) {
SS.ScheduleShowTW();
}
}
havePainted = false;
- InvalidateGraphics();
+ Invalidate();
}
-void GraphicsWindow::MouseLeave(void) {
+void GraphicsWindow::MouseLeave() {
// Un-hover everything when the mouse leaves our window, unless there's
// currently a context menu shown.
if(!context.active) {
hover.Clear();
- toolbarTooltipped = 0;
- toolbarHovered = 0;
- PaintGraphics();
+ toolbarHovered = Command::NONE;
+ Invalidate();
}
SS.extraLine.draw = false;
}
-void GraphicsWindow::SpaceNavigatorMoved(double tx, double ty, double tz,
- double rx, double ry, double rz,
- bool shiftDown)
-{
+void GraphicsWindow::SixDofEvent(Platform::SixDofEvent event) {
+ if(event.type == Platform::SixDofEvent::Type::RELEASE) {
+ ZoomToFit(/*includingInvisibles=*/false, /*useSelection=*/true);
+ Invalidate();
+ return;
+ }
+
if(!havePainted) return;
Vector out = projRight.Cross(projUp);
// rotation vector is axis of rotation, and its magnitude is angle
- Vector aa = Vector::From(rx, ry, rz);
+ Vector aa = Vector::From(event.rotationX, event.rotationY, event.rotationZ);
// but it's given with respect to screen projection frame
aa = aa.ScaleOutOfCsys(projRight, projUp, out);
double aam = aa.Magnitude();
if(gs.points == 1 && gs.n == 1) e = SK.GetEntity(gs.point [0]);
if(gs.entities == 1 && gs.n == 1) e = SK.GetEntity(gs.entity[0]);
if(e) g = SK.GetGroup(e->group);
- if(g && g->type == Group::LINKED && !shiftDown) {
+ if(g && g->type == Group::Type::LINKED && !event.shiftDown) {
// Apply the transformation to a linked part. Gain down the Z
// axis, since it's hard to see what you're doing on that one since
// it's normal to the screen.
- Vector t = projRight.ScaledBy(tx/scale).Plus(
- projUp .ScaledBy(ty/scale).Plus(
- out .ScaledBy(0.1*tz/scale)));
+ Vector t = projRight.ScaledBy(event.translationX/scale).Plus(
+ projUp .ScaledBy(event.translationY/scale).Plus(
+ out .ScaledBy(0.1*event.translationZ/scale)));
Quaternion q = Quaternion::From(aa, aam);
// If we go five seconds without SpaceNavigator input, or if we've
// switched groups, then consider that a new action and save an undo
// point.
int64_t now = GetMilliseconds();
- if(now - lastSpaceNavigatorTime > 5000 ||
- lastSpaceNavigatorGroup.v != g->h.v)
+ if(now - last6DofTime > 5000 ||
+ last6DofGroup != g->h)
{
SS.UndoRemember();
}
g->TransformImportedBy(t, q);
- lastSpaceNavigatorTime = now;
- lastSpaceNavigatorGroup = g->h;
+ last6DofTime = now;
+ last6DofGroup = g->h;
SS.MarkGroupDirty(g->h);
- SS.ScheduleGenerateAll();
} else {
// Apply the transformation to the view of the everything. The
// x and y components are translation; but z component is scale,
// not translation, or else it would do nothing in a parallel
// projection
- offset = offset.Plus(projRight.ScaledBy(tx/scale));
- offset = offset.Plus(projUp.ScaledBy(ty/scale));
- scale *= exp(0.001*tz);
+ offset = offset.Plus(projRight.ScaledBy(event.translationX/scale));
+ offset = offset.Plus(projUp.ScaledBy(event.translationY/scale));
+ scale *= exp(0.001*event.translationZ);
if(aam > 0.0) {
projRight = projRight.RotatedAbout(aa, -aam);
}
havePainted = false;
- InvalidateGraphics();
-}
-
-void GraphicsWindow::SpaceNavigatorButtonUp(void) {
- ZoomToFit(/*includingInvisibles=*/false, /*useSelection=*/true);
- InvalidateGraphics();
+ Invalidate();
}
-
--- /dev/null
+//-----------------------------------------------------------------------------
+// Our main() function for the command-line interface.
+//
+// Copyright 2016 whitequark
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+
+static void ShowUsage(const std::string &cmd) {
+ fprintf(stderr, "Usage: %s <command> <options> <filename> [filename...]", cmd.c_str());
+//-----------------------------------------------------------------------------> 80 col */
+ fprintf(stderr, R"(
+ When run, performs an action specified by <command> on every <filename>.
+
+Common options:
+ -o, --output <pattern>
+ For an input file <name>.slvs, replaces the '%%' symbol in <pattern>
+ with <name> and uses it as output file. For example, when using
+ --output %%-2d.png for input files f/a.slvs and f/b.slvs, output files
+ f/a-2d.png and f/b-2d.png will be written.
+ -v, --view <direction>
+ Selects the camera direction. <direction> can be one of "top", "bottom",
+ "left", "right", "front", "back", or "isometric".
+ -t, --chord-tol <tolerance>
+ Selects the chord tolerance, used for converting exact curves to
+ piecewise linear, and exact surfaces into triangle meshes.
+ For export commands, the unit is mm, and the default is 1.0 mm.
+ For non-export commands, the unit is %%, and the default is 1.0 %%.
+ -b, --bg-color <on|off>
+ Whether to export the background colour in vector formats. Defaults to off.
+
+Commands:
+ thumbnail --output <pattern> --size <size> --view <direction>
+ [--chord-tol <tolerance>]
+ Outputs a rendered view of the sketch, like the SolveSpace GUI would.
+ <size> is <width>x<height>, in pixels. Graphics acceleration is
+ not used, and the output may look slightly different from the GUI.
+ export-view --output <pattern> --view <direction> [--chord-tol <tolerance>]
+ [--bg-color <on|off>]
+ Exports a view of the sketch, in a 2d vector format.
+ export-wireframe --output <pattern> [--chord-tol <tolerance>]
+ Exports a wireframe of the sketch, in a 3d vector format.
+ export-mesh --output <pattern> [--chord-tol <tolerance>]
+ Exports a triangle mesh of solids in the sketch, with exact surfaces
+ being triangulated first.
+ export-surfaces --output <pattern>
+ Exports exact surfaces of solids in the sketch, if any.
+ regenerate [--chord-tol <tolerance>]
+ Reloads all imported files, regenerates the sketch, and saves it.
+ Note that, although this is not an export command, it uses absolute
+ chord tolerance, and can be used to prepare assemblies for export.
+)");
+
+ auto FormatListFromFileFilters = [](const std::vector<Platform::FileFilter> &filters) {
+ std::string descr;
+ for(auto filter : filters) {
+ descr += "\n ";
+ descr += filter.name;
+ descr += " (";
+ bool first = true;
+ for(auto extension : filter.extensions) {
+ if(!first) {
+ descr += ", ";
+ }
+ descr += extension;
+ first = false;
+ }
+ descr += ")";
+ }
+ return descr;
+ };
+
+ fprintf(stderr, R"(
+File formats:
+ thumbnail:%s
+ export-view:%s
+ export-wireframe:%s
+ export-mesh:%s
+ export-surfaces:%s
+)", FormatListFromFileFilters(Platform::RasterFileFilters).c_str(),
+ FormatListFromFileFilters(Platform::VectorFileFilters).c_str(),
+ FormatListFromFileFilters(Platform::Vector3dFileFilters).c_str(),
+ FormatListFromFileFilters(Platform::MeshFileFilters).c_str(),
+ FormatListFromFileFilters(Platform::SurfaceFileFilters).c_str());
+}
+
+static bool RunCommand(const std::vector<std::string> args) {
+ if(args.size() < 2) return false;
+
+ for(const std::string &arg : args) {
+ if(arg == "--help" || arg == "-h") {
+ ShowUsage(args[0]);
+ return true;
+ }
+ }
+
+ std::function<void(const Platform::Path &)> runner;
+
+ std::vector<Platform::Path> inputFiles;
+ auto ParseInputFile = [&](size_t &argn) {
+ std::string arg = args[argn];
+ if(arg[0] != '-') {
+ inputFiles.push_back(Platform::Path::From(arg));
+ return true;
+ } else return false;
+ };
+
+ std::string outputPattern;
+ auto ParseOutputPattern = [&](size_t &argn) {
+ if(argn + 1 < args.size() && (args[argn] == "--output" ||
+ args[argn] == "-o")) {
+ argn++;
+ outputPattern = args[argn];
+ return true;
+ } else return false;
+ };
+
+ Vector projUp = {}, projRight = {};
+ auto ParseViewDirection = [&](size_t &argn) {
+ if(argn + 1 < args.size() && (args[argn] == "--view" ||
+ args[argn] == "-v")) {
+ argn++;
+ if(args[argn] == "top") {
+ projRight = Vector::From(1, 0, 0);
+ projUp = Vector::From(0, 0, -1);
+ } else if(args[argn] == "bottom") {
+ projRight = Vector::From(1, 0, 0);
+ projUp = Vector::From(0, 0, 1);
+ } else if(args[argn] == "left") {
+ projRight = Vector::From(0, 0, 1);
+ projUp = Vector::From(0, 1, 0);
+ } else if(args[argn] == "right") {
+ projRight = Vector::From(0, 0, -1);
+ projUp = Vector::From(0, 1, 0);
+ } else if(args[argn] == "front") {
+ projRight = Vector::From(1, 0, 0);
+ projUp = Vector::From(0, 1, 0);
+ } else if(args[argn] == "back") {
+ projRight = Vector::From(-1, 0, 0);
+ projUp = Vector::From(0, 1, 0);
+ } else if(args[argn] == "isometric") {
+ projRight = Vector::From(0.707, 0.000, -0.707);
+ projUp = Vector::From(-0.408, 0.816, -0.408);
+ } else {
+ fprintf(stderr, "Unrecognized view direction '%s'\n", args[argn].c_str());
+ }
+ return true;
+ } else return false;
+ };
+
+ double chordTol = 1.0;
+ auto ParseChordTolerance = [&](size_t &argn) {
+ if(argn + 1 < args.size() && (args[argn] == "--chord-tol" ||
+ args[argn] == "-t")) {
+ argn++;
+ if(sscanf(args[argn].c_str(), "%lf", &chordTol) == 1) {
+ return true;
+ } else return false;
+ } else return false;
+ };
+
+ bool bg_color = false;
+ auto ParseBgColor = [&](size_t &argn) {
+ if(argn + 1 < args.size() && (args[argn] == "--bg-color" ||
+ args[argn] == "-b")) {
+ argn++;
+ if(args[argn] == "on") {
+ bg_color = true;
+ return true;
+ } else if(args[argn] == "off") {
+ bg_color = false;
+ return true;
+ } else return false;
+ } else return false;
+ };
+
+ unsigned width = 0, height = 0;
+ if(args[1] == "thumbnail") {
+ auto ParseSize = [&](size_t &argn) {
+ if(argn + 1 < args.size() && args[argn] == "--size") {
+ argn++;
+ if(sscanf(args[argn].c_str(), "%ux%u", &width, &height) == 2) {
+ return true;
+ } else return false;
+ } else return false;
+ };
+
+ for(size_t argn = 2; argn < args.size(); argn++) {
+ if(!(ParseInputFile(argn) ||
+ ParseOutputPattern(argn) ||
+ ParseViewDirection(argn) ||
+ ParseChordTolerance(argn) ||
+ ParseSize(argn))) {
+ fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str());
+ return false;
+ }
+ }
+
+ if(width == 0 || height == 0) {
+ fprintf(stderr, "Non-zero viewport size must be specified.\n");
+ return false;
+ }
+
+ if(EXACT(projUp.Magnitude() == 0 || projRight.Magnitude() == 0)) {
+ fprintf(stderr, "View direction must be specified.\n");
+ return false;
+ }
+
+ runner = [&](const Platform::Path &output) {
+ Camera camera = {};
+ camera.pixelRatio = 1;
+ camera.gridFit = true;
+ camera.width = width;
+ camera.height = height;
+ camera.projUp = projUp;
+ camera.projRight = projRight;
+
+ SS.GW.projUp = projUp;
+ SS.GW.projRight = projRight;
+ SS.GW.scale = SS.GW.ZoomToFit(camera);
+ camera.scale = SS.GW.scale;
+ camera.offset = SS.GW.offset;
+ SS.GenerateAll();
+
+ CairoPixmapRenderer pixmapCanvas;
+ pixmapCanvas.antialias = true;
+ pixmapCanvas.SetLighting(SS.GW.GetLighting());
+ pixmapCanvas.SetCamera(camera);
+ pixmapCanvas.Init();
+
+ pixmapCanvas.StartFrame();
+ SS.GW.Draw(&pixmapCanvas);
+ pixmapCanvas.FlushFrame();
+ pixmapCanvas.FinishFrame();
+ pixmapCanvas.ReadFrame()->WritePng(output, /*flip=*/true);
+
+ pixmapCanvas.Clear();
+ };
+ } else if(args[1] == "export-view") {
+ for(size_t argn = 2; argn < args.size(); argn++) {
+ if(!(ParseInputFile(argn) ||
+ ParseOutputPattern(argn) ||
+ ParseViewDirection(argn) ||
+ ParseChordTolerance(argn) ||
+ ParseBgColor(argn))) {
+ fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str());
+ return false;
+ }
+ }
+
+ if(EXACT(projUp.Magnitude() == 0 || projRight.Magnitude() == 0)) {
+ fprintf(stderr, "View direction must be specified.\n");
+ return false;
+ }
+
+ runner = [&](const Platform::Path &output) {
+ SS.GW.projRight = projRight;
+ SS.GW.projUp = projUp;
+ SS.exportChordTol = chordTol;
+ SS.exportBackgroundColor = bg_color;
+
+ SS.ExportViewOrWireframeTo(output, /*exportWireframe=*/false);
+ };
+ } else if(args[1] == "export-wireframe") {
+ for(size_t argn = 2; argn < args.size(); argn++) {
+ if(!(ParseInputFile(argn) ||
+ ParseOutputPattern(argn) ||
+ ParseChordTolerance(argn))) {
+ fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str());
+ return false;
+ }
+ }
+
+ runner = [&](const Platform::Path &output) {
+ SS.exportChordTol = chordTol;
+
+ SS.ExportViewOrWireframeTo(output, /*exportWireframe=*/true);
+ };
+ } else if(args[1] == "export-mesh") {
+ for(size_t argn = 2; argn < args.size(); argn++) {
+ if(!(ParseInputFile(argn) ||
+ ParseOutputPattern(argn) ||
+ ParseChordTolerance(argn))) {
+ fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str());
+ return false;
+ }
+ }
+
+ runner = [&](const Platform::Path &output) {
+ SS.exportChordTol = chordTol;
+
+ SS.ExportMeshTo(output);
+ };
+ } else if(args[1] == "export-surfaces") {
+ for(size_t argn = 2; argn < args.size(); argn++) {
+ if(!(ParseInputFile(argn) ||
+ ParseOutputPattern(argn))) {
+ fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str());
+ return false;
+ }
+ }
+
+ runner = [&](const Platform::Path &output) {
+ StepFileWriter sfw = {};
+ sfw.ExportSurfacesTo(output);
+ };
+ } else if(args[1] == "regenerate") {
+ for(size_t argn = 2; argn < args.size(); argn++) {
+ if(!(ParseInputFile(argn) ||
+ ParseChordTolerance(argn))) {
+ fprintf(stderr, "Unrecognized option '%s'.\n", args[argn].c_str());
+ return false;
+ }
+ }
+
+ outputPattern = "%.slvs";
+
+ runner = [&](const Platform::Path &output) {
+ SS.exportChordTol = chordTol;
+ SS.exportMode = true;
+
+ SS.SaveToFile(output);
+ };
+ } else {
+ fprintf(stderr, "Unrecognized command '%s'.\n", args[1].c_str());
+ return false;
+ }
+
+ if(outputPattern.empty()) {
+ fprintf(stderr, "An output pattern must be specified.\n");
+ return false;
+ } else if(outputPattern.find('%') == std::string::npos && inputFiles.size() > 1) {
+ fprintf(stderr,
+ "Output pattern must include a %% symbol when using multiple inputs!\n");
+ return false;
+ }
+
+ if(inputFiles.empty()) {
+ fprintf(stderr, "At least one input file must be specified.\n");
+ return false;
+ }
+
+ for(const Platform::Path &inputFile : inputFiles) {
+ Platform::Path absInputFile = inputFile.Expand(/*fromCurrentDirectory=*/true);
+
+ Platform::Path outputFile = Platform::Path::From(outputPattern);
+ size_t replaceAt = outputFile.raw.find('%');
+ if(replaceAt != std::string::npos) {
+ Platform::Path outputSubst = inputFile.Parent();
+ if(outputSubst.IsEmpty()) {
+ outputSubst = Platform::Path::From(inputFile.FileStem());
+ } else {
+ outputSubst = outputSubst.Join(inputFile.FileStem());
+ }
+ outputFile.raw.replace(replaceAt, 1, outputSubst.raw);
+ }
+ Platform::Path absOutputFile = outputFile.Expand(/*fromCurrentDirectory=*/true);
+
+ SS.Init();
+ if(!SS.LoadFromFile(absInputFile)) {
+ fprintf(stderr, "Cannot load '%s'!\n", inputFile.raw.c_str());
+ return false;
+ }
+ SS.AfterNewFile();
+ runner(absOutputFile);
+ SK.Clear();
+ SS.Clear();
+
+ fprintf(stderr, "Written '%s'.\n", outputFile.raw.c_str());
+ }
+
+ return true;
+}
+
+int main(int argc, char **argv) {
+ std::vector<std::string> args = Platform::InitCli(argc, argv);
+
+ if(args.size() == 1) {
+ ShowUsage(args[0]);
+ return 0;
+ }
+
+ if(!RunCommand(args)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Our main() function for the graphical interface.
+//
+// Copyright 2018 <whitequark@whitequark.org>
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+#if defined(WIN32)
+# include <windows.h>
+#endif
+
+using namespace SolveSpace;
+
+int main(int argc, char** argv) {
+ std::vector<std::string> args = Platform::InitGui(argc, argv);
+
+ Platform::Open3DConnexion();
+ SS.Init();
+
+ if(args.size() >= 2) {
+ if(args.size() > 2) {
+ dbp("Only the first file passed on command line will be opened.");
+ }
+
+ SS.Load(Platform::Path::From(args.back()));
+ }
+
+ Platform::RunGui();
+
+ Platform::Close3DConnexion();
+ SS.Clear();
+ SK.Clear();
+ Platform::ClearGui();
+
+ return 0;
+}
+
+#if defined(WIN32)
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine, INT nCmdShow) {
+ return main(0, NULL);
+}
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------
+// Platform-dependent GUI functionality that has only minor differences.
+//
+// Copyright 2018 whitequark
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+
+namespace SolveSpace {
+namespace Platform {
+
+//-----------------------------------------------------------------------------
+// Keyboard events
+//-----------------------------------------------------------------------------
+
+std::string AcceleratorDescription(const KeyboardEvent &accel) {
+ std::string label;
+ if(accel.controlDown) {
+#ifdef __APPLE__
+ label += "⌘+";
+#else
+ label += "Ctrl+";
+#endif
+ }
+
+ if(accel.shiftDown) {
+ label += "Shift+";
+ }
+
+ switch(accel.key) {
+ case KeyboardEvent::Key::FUNCTION:
+ label += ssprintf("F%d", accel.num);
+ break;
+
+ case KeyboardEvent::Key::CHARACTER:
+ if(accel.chr == '\t') {
+ label += "Tab";
+ } else if(accel.chr == ' ') {
+ label += "Space";
+ } else if(accel.chr == '\x1b') {
+ label += "Esc";
+ } else if(accel.chr == '\x7f') {
+ label += "Del";
+ } else if(accel.chr != 0) {
+ label += toupper((char)(accel.chr & 0xff));
+ }
+ break;
+ }
+
+ return label;
+}
+
+//-----------------------------------------------------------------------------
+// Settings
+//-----------------------------------------------------------------------------
+
+void Settings::FreezeBool(const std::string &key, bool value) {
+ FreezeInt(key, (int)value);
+}
+
+bool Settings::ThawBool(const std::string &key, bool defaultValue) {
+ return ThawInt(key, (int)defaultValue) != 0;
+}
+
+void Settings::FreezeColor(const std::string &key, RgbaColor value) {
+ FreezeInt(key, value.ToPackedInt());
+}
+
+RgbaColor Settings::ThawColor(const std::string &key, RgbaColor defaultValue) {
+ return RgbaColor::FromPackedInt(ThawInt(key, defaultValue.ToPackedInt()));
+}
+
+//-----------------------------------------------------------------------------
+// File dialogs
+//-----------------------------------------------------------------------------
+
+void FileDialog::AddFilter(const FileFilter &filter) {
+ AddFilter(Translate("file-type", filter.name.c_str()), filter.extensions);
+}
+
+void FileDialog::AddFilters(const std::vector<FileFilter> &filters) {
+ for(auto filter : filters) AddFilter(filter);
+}
+
+std::vector<FileFilter> SolveSpaceModelFileFilters = {
+ { CN_("file-type", "SolveSpace models"), { "slvs" } },
+};
+
+std::vector<FileFilter> SolveSpaceLinkFileFilters = {
+ { CN_("file-type", "SolveSpace models"), { "slvs" } },
+ { CN_("file-type", "IDF circuit board"), { "emn" } },
+};
+
+std::vector<FileFilter> RasterFileFilters = {
+ { CN_("file-type", "PNG image"), { "png" } },
+};
+
+std::vector<FileFilter> MeshFileFilters = {
+ { CN_("file-type", "STL mesh"), { "stl" } },
+ { CN_("file-type", "Wavefront OBJ mesh"), { "obj" } },
+ { CN_("file-type", "Three.js-compatible mesh, with viewer"), { "html" } },
+ { CN_("file-type", "Three.js-compatible mesh, mesh only"), { "js" } },
+ { CN_("file-type", "Q3D Object file"), { "q3do" } },
+ { CN_("file-type", "VRML text file"), { "wrl" } },
+};
+
+std::vector<FileFilter> SurfaceFileFilters = {
+ { CN_("file-type", "STEP file"), { "step", "stp" } },
+};
+
+std::vector<FileFilter> VectorFileFilters = {
+ { CN_("file-type", "PDF file"), { "pdf" } },
+ { CN_("file-type", "Encapsulated PostScript"), { "eps", "ps" } },
+ { CN_("file-type", "Scalable Vector Graphics"), { "svg" } },
+ { CN_("file-type", "STEP file"), { "step", "stp" } },
+ { CN_("file-type", "DXF file (AutoCAD 2007)"), { "dxf" } },
+ { CN_("file-type", "HPGL file"), { "plt", "hpgl" } },
+ { CN_("file-type", "G Code"), { "ngc", "txt" } },
+};
+
+std::vector<FileFilter> Vector3dFileFilters = {
+ { CN_("file-type", "STEP file"), { "step", "stp" } },
+ { CN_("file-type", "DXF file (AutoCAD 2007)"), { "dxf" } },
+};
+
+std::vector<FileFilter> ImportFileFilters = {
+ { CN_("file-type", "AutoCAD DXF and DWG files"), { "dxf", "dwg" } },
+};
+
+std::vector<FileFilter> CsvFileFilters = {
+ { CN_("file-type", "Comma-separated values"), { "csv" } },
+};
+
+
+}
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// An abstraction for platform-dependent GUI functionality.
+//
+// Copyright 2018 whitequark
+//-----------------------------------------------------------------------------
+
+#ifndef SOLVESPACE_GUI_H
+#define SOLVESPACE_GUI_H
+
+class RgbaColor;
+
+namespace Platform {
+
+//-----------------------------------------------------------------------------
+// Events
+//-----------------------------------------------------------------------------
+
+// A mouse input event.
+class MouseEvent {
+public:
+ enum class Type {
+ MOTION,
+ PRESS,
+ DBL_PRESS,
+ RELEASE,
+ SCROLL_VERT,
+ LEAVE,
+ };
+
+ enum class Button {
+ NONE,
+ LEFT,
+ MIDDLE,
+ RIGHT,
+ };
+
+ Type type;
+ double x;
+ double y;
+ bool shiftDown;
+ bool controlDown;
+ union {
+ Button button; // for Type::{MOTION,PRESS,DBL_PRESS,RELEASE}
+ double scrollDelta; // for Type::SCROLL_VERT
+ };
+};
+
+// A 3-DOF input event.
+struct SixDofEvent {
+ enum class Type {
+ MOTION,
+ PRESS,
+ RELEASE,
+ };
+
+ enum class Button {
+ FIT,
+ };
+
+ Type type;
+ bool shiftDown;
+ bool controlDown;
+ double translationX, translationY, translationZ; // for Type::MOTION
+ double rotationX, rotationY, rotationZ; // for Type::MOTION
+ Button button; // for Type::{PRESS,RELEASE}
+};
+
+// A keyboard input event.
+struct KeyboardEvent {
+ enum class Type {
+ PRESS,
+ RELEASE,
+ };
+
+ enum class Key {
+ CHARACTER,
+ FUNCTION,
+ };
+
+ Type type;
+ Key key;
+ union {
+ char32_t chr; // for Key::CHARACTER
+ int num; // for Key::FUNCTION
+ };
+ bool shiftDown;
+ bool controlDown;
+
+ bool Equals(const KeyboardEvent &other) {
+ return type == other.type && key == other.key &&
+ shiftDown == other.shiftDown && controlDown == other.controlDown &&
+ ((key == Key::CHARACTER && chr == other.chr) ||
+ (key == Key::FUNCTION && num == other.num));
+ }
+};
+
+std::string AcceleratorDescription(const KeyboardEvent &accel);
+
+//-----------------------------------------------------------------------------
+// Interfaces
+//-----------------------------------------------------------------------------
+
+// Handling fatal errors.
+[[noreturn]]
+void FatalError(const std::string &message);
+
+// A native settings store.
+class Settings {
+public:
+ virtual ~Settings() = default;
+
+ virtual void FreezeInt(const std::string &key, uint32_t value) = 0;
+ virtual uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) = 0;
+
+ virtual void FreezeFloat(const std::string &key, double value) = 0;
+ virtual double ThawFloat(const std::string &key, double defaultValue = 0.0) = 0;
+
+ virtual void FreezeString(const std::string &key, const std::string &value) = 0;
+ virtual std::string ThawString(const std::string &key,
+ const std::string &defaultValue = "") = 0;
+
+ virtual void FreezeBool(const std::string &key, bool value);
+ virtual bool ThawBool(const std::string &key, bool defaultValue = false);
+
+ virtual void FreezeColor(const std::string &key, RgbaColor value);
+ virtual RgbaColor ThawColor(const std::string &key, RgbaColor defaultValue);
+};
+
+typedef std::shared_ptr<Settings> SettingsRef;
+
+SettingsRef GetSettings();
+
+// A native single-shot timer.
+class Timer {
+public:
+ std::function<void()> onTimeout;
+
+ virtual ~Timer() = default;
+
+ virtual void RunAfter(unsigned milliseconds) = 0;
+ virtual void RunAfterNextFrame() { RunAfter(1); }
+ virtual void RunAfterProcessingEvents() { RunAfter(0); }
+};
+
+typedef std::shared_ptr<Timer> TimerRef;
+
+TimerRef CreateTimer();
+
+// A native menu item.
+class MenuItem {
+public:
+ enum class Indicator {
+ NONE,
+ CHECK_MARK,
+ RADIO_MARK,
+ };
+
+ std::function<void()> onTrigger;
+
+ virtual ~MenuItem() = default;
+
+ virtual void SetAccelerator(KeyboardEvent accel) = 0;
+ virtual void SetIndicator(Indicator type) = 0;
+ virtual void SetEnabled(bool enabled) = 0;
+ virtual void SetActive(bool active) = 0;
+};
+
+typedef std::shared_ptr<MenuItem> MenuItemRef;
+
+// A native menu.
+class Menu {
+public:
+ virtual ~Menu() = default;
+
+ virtual std::shared_ptr<MenuItem> AddItem(
+ const std::string &label, std::function<void()> onTrigger = std::function<void()>(),
+ bool mnemonics = true) = 0;
+ virtual std::shared_ptr<Menu> AddSubMenu(const std::string &label) = 0;
+ virtual void AddSeparator() = 0;
+
+ virtual void PopUp() = 0;
+
+ virtual void Clear() = 0;
+};
+
+typedef std::shared_ptr<Menu> MenuRef;
+
+// A native menu bar.
+class MenuBar {
+public:
+ virtual ~MenuBar() = default;
+
+ virtual std::shared_ptr<Menu> AddSubMenu(const std::string &label) = 0;
+
+ virtual void Clear() = 0;
+};
+
+typedef std::shared_ptr<MenuBar> MenuBarRef;
+
+MenuRef CreateMenu();
+MenuBarRef GetOrCreateMainMenu(bool *unique);
+
+// A native top-level window, with an OpenGL context, and an editor overlay.
+class Window {
+public:
+ enum class Kind {
+ TOPLEVEL,
+ TOOL,
+ };
+
+ enum class Cursor {
+ POINTER,
+ HAND
+ };
+
+ std::function<void()> onClose;
+ std::function<void(bool)> onFullScreen;
+ std::function<bool(MouseEvent)> onMouseEvent;
+ std::function<void(SixDofEvent)> onSixDofEvent;
+ std::function<bool(KeyboardEvent)> onKeyboardEvent;
+ std::function<void(std::string)> onEditingDone;
+ std::function<void(double)> onScrollbarAdjusted;
+ std::function<void()> onRender;
+
+ virtual ~Window() = default;
+
+ // Returns physical display DPI.
+ virtual double GetPixelDensity() = 0;
+ // Returns raster graphics and coordinate scale (already applied on the platform side),
+ // i.e. size of logical pixel in physical pixels, or device pixel ratio.
+ virtual int GetDevicePixelRatio() = 0;
+ // Returns (fractional) font scale, to be applied on top of (integral) device pixel ratio.
+ virtual double GetDeviceFontScale() {
+ return GetPixelDensity() / GetDevicePixelRatio() / 96.0;
+ }
+
+ virtual bool IsVisible() = 0;
+ virtual void SetVisible(bool visible) = 0;
+ virtual void Focus() = 0;
+
+ virtual bool IsFullScreen() = 0;
+ virtual void SetFullScreen(bool fullScreen) = 0;
+
+ virtual void SetTitle(const std::string &title) = 0;
+ virtual bool SetTitleForFilename(const Path &filename) { return false; }
+
+ virtual void SetMenuBar(MenuBarRef menuBar) = 0;
+
+ virtual void GetContentSize(double *width, double *height) = 0;
+ virtual void SetMinContentSize(double width, double height) = 0;
+
+ virtual void FreezePosition(SettingsRef settings, const std::string &key) = 0;
+ virtual void ThawPosition(SettingsRef settings, const std::string &key) = 0;
+
+ virtual void SetCursor(Cursor cursor) = 0;
+ virtual void SetTooltip(const std::string &text, double x, double y,
+ double width, double height) = 0;
+
+ virtual bool IsEditorVisible() = 0;
+ virtual void ShowEditor(double x, double y, double fontHeight, double minWidth,
+ bool isMonospace, const std::string &text) = 0;
+ virtual void HideEditor() = 0;
+
+ virtual void SetScrollbarVisible(bool visible) = 0;
+ virtual void ConfigureScrollbar(double min, double max, double pageSize) = 0;
+ virtual double GetScrollbarPosition() = 0;
+ virtual void SetScrollbarPosition(double pos) = 0;
+
+ virtual void Invalidate() = 0;
+};
+
+typedef std::shared_ptr<Window> WindowRef;
+
+WindowRef CreateWindow(Window::Kind kind = Window::Kind::TOPLEVEL,
+ WindowRef parentWindow = NULL);
+
+// 3DConnexion support.
+void Open3DConnexion();
+void Close3DConnexion();
+void Request3DConnexionEventsForWindow(WindowRef window);
+
+// A native dialog that asks for one choice out of several.
+class MessageDialog {
+public:
+ enum class Type {
+ INFORMATION,
+ QUESTION,
+ WARNING,
+ ERROR
+ };
+
+ enum class Response {
+ NONE,
+ OK,
+ YES,
+ NO,
+ CANCEL
+ };
+
+ std::function<void(Response)> onResponse;
+
+ virtual ~MessageDialog() = default;
+
+ virtual void SetType(Type type) = 0;
+ virtual void SetTitle(std::string title) = 0;
+ virtual void SetMessage(std::string message) = 0;
+ virtual void SetDescription(std::string description) = 0;
+
+ virtual void AddButton(std::string label, Response response, bool isDefault = false) = 0;
+
+ virtual Response RunModal() = 0;
+ virtual void ShowModal() {
+ Response response = RunModal();
+ if(onResponse) {
+ onResponse(response);
+ }
+ }
+};
+
+typedef std::shared_ptr<MessageDialog> MessageDialogRef;
+
+MessageDialogRef CreateMessageDialog(WindowRef parentWindow);
+
+// A file filter.
+struct FileFilter {
+ std::string name;
+ std::vector<std::string> extensions;
+};
+
+// SolveSpace's native file format
+extern std::vector<FileFilter> SolveSpaceModelFileFilters;
+// SolveSpace's linkable file formats
+extern std::vector<FileFilter> SolveSpaceLinkFileFilters;
+// Raster image
+extern std::vector<FileFilter> RasterFileFilters;
+// Triangle mesh
+extern std::vector<FileFilter> MeshFileFilters;
+// NURBS surfaces
+extern std::vector<FileFilter> SurfaceFileFilters;
+// 2d vector (lines and curves) format
+extern std::vector<FileFilter> VectorFileFilters;
+// 3d vector (wireframe lines and curves) format
+extern std::vector<FileFilter> Vector3dFileFilters;
+// Any importable format
+extern std::vector<FileFilter> ImportFileFilters;
+// Comma-separated value, like a spreadsheet would use
+extern std::vector<FileFilter> CsvFileFilters;
+
+// A native dialog that asks to choose a file.
+class FileDialog {
+public:
+ virtual ~FileDialog() = default;
+
+ virtual void SetTitle(std::string title) = 0;
+ virtual void SetCurrentName(std::string name) = 0;
+
+ virtual Platform::Path GetFilename() = 0;
+ virtual void SetFilename(Platform::Path path) = 0;
+
+ virtual void AddFilter(std::string name, std::vector<std::string> extensions) = 0;
+ void AddFilter(const FileFilter &filter);
+ void AddFilters(const std::vector<FileFilter> &filters);
+
+ virtual void FreezeChoices(SettingsRef settings, const std::string &key) = 0;
+ virtual void ThawChoices(SettingsRef settings, const std::string &key) = 0;
+
+ virtual bool RunModal() = 0;
+};
+
+typedef std::shared_ptr<FileDialog> FileDialogRef;
+
+FileDialogRef CreateOpenFileDialog(WindowRef parentWindow);
+FileDialogRef CreateSaveFileDialog(WindowRef parentWindow);
+
+//-----------------------------------------------------------------------------
+// Application-wide APIs
+//-----------------------------------------------------------------------------
+
+std::vector<Platform::Path> GetFontFiles();
+void OpenInBrowser(const std::string &url);
+
+std::vector<std::string> InitGui(int argc, char **argv);
+void RunGui();
+void ExitGui();
+void ClearGui();
+
+}
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------
+// The GTK-based implementation of platform-dependent GUI functionality.
+//
+// Copyright 2018 whitequark
+//-----------------------------------------------------------------------------
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <json-c/json_object.h>
+#include <json-c/json_util.h>
+#include <glibmm/convert.h>
+#include <glibmm/main.h>
+#include <gtkmm/box.h>
+#include <gtkmm/checkmenuitem.h>
+#include <gtkmm/cssprovider.h>
+#include <gtkmm/entry.h>
+#include <gtkmm/filechooserdialog.h>
+#include <gtkmm/fixed.h>
+#include <gtkmm/glarea.h>
+#include <gtkmm/main.h>
+#include <gtkmm/menu.h>
+#include <gtkmm/menubar.h>
+#include <gtkmm/messagedialog.h>
+#include <gtkmm/scrollbar.h>
+#include <gtkmm/separatormenuitem.h>
+#include <gtkmm/tooltip.h>
+#include <gtkmm/window.h>
+
+#include "config.h"
+#if defined(HAVE_GTK_FILECHOOSERNATIVE)
+# include <gtkmm/filechoosernative.h>
+#endif
+
+#if defined(HAVE_SPACEWARE)
+# include <spnav.h>
+# include <gdk/gdkx.h>
+#endif
+
+#include "solvespace.h"
+
+namespace SolveSpace {
+namespace Platform {
+
+//-----------------------------------------------------------------------------
+// Utility functions
+//-----------------------------------------------------------------------------
+
+static std::string PrepareMnemonics(std::string label) {
+ std::replace(label.begin(), label.end(), '&', '_');
+ return label;
+}
+
+static std::string PrepareTitle(const std::string &title) {
+ return title + " — SolveSpace";
+}
+
+//-----------------------------------------------------------------------------
+// Fatal errors
+//-----------------------------------------------------------------------------
+
+void FatalError(const std::string &message) {
+ fprintf(stderr, "%s", message.c_str());
+ abort();
+}
+
+//-----------------------------------------------------------------------------
+// Settings
+//-----------------------------------------------------------------------------
+
+class SettingsImplGtk final : public Settings {
+public:
+ // Why aren't we using GSettings? Two reasons. It doesn't allow to easily see whether
+ // the setting had the default value, and it requires to install a schema globally.
+ Path _path;
+ json_object *_json = NULL;
+
+ static Path GetConfigPath() {
+ Path configHome;
+ if(getenv("XDG_CONFIG_HOME")) {
+ configHome = Path::From(getenv("XDG_CONFIG_HOME"));
+ } else if(getenv("HOME")) {
+ configHome = Path::From(getenv("HOME")).Join(".config");
+ } else {
+ dbp("neither XDG_CONFIG_HOME nor HOME are set");
+ return Path::From("");
+ }
+ if(!configHome.IsEmpty()) {
+ configHome = configHome.Join("solvespace");
+ }
+
+ const char *configHomeC = configHome.raw.c_str();
+ struct stat st;
+ if(stat(configHomeC, &st)) {
+ if(errno == ENOENT) {
+ if(mkdir(configHomeC, 0777)) {
+ dbp("cannot mkdir %s: %s", configHomeC, strerror(errno));
+ return Path::From("");
+ }
+ } else {
+ dbp("cannot stat %s: %s", configHomeC, strerror(errno));
+ return Path::From("");
+ }
+ } else if(!S_ISDIR(st.st_mode)) {
+ dbp("%s is not a directory", configHomeC);
+ return Path::From("");
+ }
+
+ return configHome.Join("settings.json");
+ }
+
+ SettingsImplGtk() {
+ _path = GetConfigPath();
+ if(_path.IsEmpty()) {
+ dbp("settings will not be saved");
+ } else {
+ _json = json_object_from_file(_path.raw.c_str());
+ if(!_json && errno != ENOENT) {
+ dbp("cannot load settings: %s", strerror(errno));
+ }
+ }
+
+ if(_json == NULL) {
+ _json = json_object_new_object();
+ }
+ }
+
+ ~SettingsImplGtk() override {
+ if(!_path.IsEmpty()) {
+ // json-c <0.12 has the first argument non-const
+ if(json_object_to_file_ext((char *)_path.raw.c_str(), _json,
+ JSON_C_TO_STRING_PRETTY)) {
+ dbp("cannot save settings: %s", strerror(errno));
+ }
+ }
+
+ json_object_put(_json);
+ }
+
+ void FreezeInt(const std::string &key, uint32_t value) override {
+ struct json_object *jsonValue = json_object_new_int(value);
+ json_object_object_add(_json, key.c_str(), jsonValue);
+ }
+
+ uint32_t ThawInt(const std::string &key, uint32_t defaultValue) override {
+ struct json_object *jsonValue;
+ if(json_object_object_get_ex(_json, key.c_str(), &jsonValue)) {
+ return json_object_get_int(jsonValue);
+ }
+ return defaultValue;
+ }
+
+ void FreezeBool(const std::string &key, bool value) override {
+ struct json_object *jsonValue = json_object_new_boolean(value);
+ json_object_object_add(_json, key.c_str(), jsonValue);
+ }
+
+ bool ThawBool(const std::string &key, bool defaultValue) override {
+ struct json_object *jsonValue;
+ if(json_object_object_get_ex(_json, key.c_str(), &jsonValue)) {
+ return json_object_get_boolean(jsonValue);
+ }
+ return defaultValue;
+ }
+
+ void FreezeFloat(const std::string &key, double value) override {
+ struct json_object *jsonValue = json_object_new_double(value);
+ json_object_object_add(_json, key.c_str(), jsonValue);
+ }
+
+ double ThawFloat(const std::string &key, double defaultValue) override {
+ struct json_object *jsonValue;
+ if(json_object_object_get_ex(_json, key.c_str(), &jsonValue)) {
+ return json_object_get_double(jsonValue);
+ }
+ return defaultValue;
+ }
+
+ void FreezeString(const std::string &key, const std::string &value) override {
+ struct json_object *jsonValue = json_object_new_string(value.c_str());
+ json_object_object_add(_json, key.c_str(), jsonValue);
+ }
+
+ std::string ThawString(const std::string &key,
+ const std::string &defaultValue = "") override {
+ struct json_object *jsonValue;
+ if(json_object_object_get_ex(_json, key.c_str(), &jsonValue)) {
+ return json_object_get_string(jsonValue);
+ }
+ return defaultValue;
+ }
+};
+
+SettingsRef GetSettings() {
+ static std::shared_ptr<SettingsImplGtk> settings;
+ if(!settings) {
+ settings = std::make_shared<SettingsImplGtk>();
+ }
+ return settings;
+}
+
+//-----------------------------------------------------------------------------
+// Timers
+//-----------------------------------------------------------------------------
+
+class TimerImplGtk final : public Timer {
+public:
+ sigc::connection _connection;
+
+ void RunAfter(unsigned milliseconds) override {
+ if(!_connection.empty()) {
+ _connection.disconnect();
+ }
+
+ auto handler = [this]() {
+ if(this->onTimeout) {
+ this->onTimeout();
+ }
+ return false;
+ };
+ _connection = Glib::signal_timeout().connect(handler, milliseconds);
+ }
+};
+
+TimerRef CreateTimer() {
+ return std::make_shared<TimerImplGtk>();
+}
+
+//-----------------------------------------------------------------------------
+// GTK menu extensions
+//-----------------------------------------------------------------------------
+
+class GtkMenuItem : public Gtk::CheckMenuItem {
+ Platform::MenuItem *_receiver;
+ bool _has_indicator;
+ bool _synthetic_event;
+
+public:
+ GtkMenuItem(Platform::MenuItem *receiver) :
+ _receiver(receiver), _has_indicator(false), _synthetic_event(false) {
+ }
+
+ void set_accel_key(const Gtk::AccelKey &accel_key) {
+ Gtk::CheckMenuItem::set_accel_key(accel_key);
+ }
+
+ bool has_indicator() const {
+ return _has_indicator;
+ }
+
+ void set_has_indicator(bool has_indicator) {
+ _has_indicator = has_indicator;
+ }
+
+ void set_active(bool active) {
+ if(Gtk::CheckMenuItem::get_active() == active)
+ return;
+
+ _synthetic_event = true;
+ Gtk::CheckMenuItem::set_active(active);
+ _synthetic_event = false;
+ }
+
+protected:
+ void on_activate() override {
+ Gtk::CheckMenuItem::on_activate();
+
+ if(!_synthetic_event && _receiver->onTrigger) {
+ _receiver->onTrigger();
+ }
+ }
+
+ void draw_indicator_vfunc(const Cairo::RefPtr<Cairo::Context> &cr) override {
+ if(_has_indicator) {
+ Gtk::CheckMenuItem::draw_indicator_vfunc(cr);
+ }
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Menus
+//-----------------------------------------------------------------------------
+
+class MenuItemImplGtk final : public MenuItem {
+public:
+ GtkMenuItem gtkMenuItem;
+
+ MenuItemImplGtk() : gtkMenuItem(this) {}
+
+ void SetAccelerator(KeyboardEvent accel) override {
+ guint accelKey = 0;
+ if(accel.key == KeyboardEvent::Key::CHARACTER) {
+ if(accel.chr == '\t') {
+ accelKey = GDK_KEY_Tab;
+ } else if(accel.chr == '\x1b') {
+ accelKey = GDK_KEY_Escape;
+ } else if(accel.chr == '\x7f') {
+ accelKey = GDK_KEY_Delete;
+ } else {
+ accelKey = gdk_unicode_to_keyval(accel.chr);
+ }
+ } else if(accel.key == KeyboardEvent::Key::FUNCTION) {
+ accelKey = GDK_KEY_F1 + accel.num - 1;
+ }
+
+ Gdk::ModifierType accelMods = {};
+ if(accel.shiftDown) {
+ accelMods |= Gdk::SHIFT_MASK;
+ }
+ if(accel.controlDown) {
+ accelMods |= Gdk::CONTROL_MASK;
+ }
+
+ gtkMenuItem.set_accel_key(Gtk::AccelKey(accelKey, accelMods));
+ }
+
+ void SetIndicator(Indicator type) override {
+ switch(type) {
+ case Indicator::NONE:
+ gtkMenuItem.set_has_indicator(false);
+ break;
+
+ case Indicator::CHECK_MARK:
+ gtkMenuItem.set_has_indicator(true);
+ gtkMenuItem.set_draw_as_radio(false);
+ break;
+
+ case Indicator::RADIO_MARK:
+ gtkMenuItem.set_has_indicator(true);
+ gtkMenuItem.set_draw_as_radio(true);
+ break;
+ }
+ }
+
+ void SetActive(bool active) override {
+ ssassert(gtkMenuItem.has_indicator(),
+ "Cannot change state of a menu item without indicator");
+ gtkMenuItem.set_active(active);
+ }
+
+ void SetEnabled(bool enabled) override {
+ gtkMenuItem.set_sensitive(enabled);
+ }
+};
+
+class MenuImplGtk final : public Menu {
+public:
+ Gtk::Menu gtkMenu;
+ std::vector<std::shared_ptr<MenuItemImplGtk>> menuItems;
+ std::vector<std::shared_ptr<MenuImplGtk>> subMenus;
+
+ MenuItemRef AddItem(const std::string &label,
+ std::function<void()> onTrigger = NULL,
+ bool mnemonics = true) override {
+ auto menuItem = std::make_shared<MenuItemImplGtk>();
+ menuItems.push_back(menuItem);
+
+ menuItem->gtkMenuItem.set_label(mnemonics ? PrepareMnemonics(label) : label);
+ menuItem->gtkMenuItem.set_use_underline(mnemonics);
+ menuItem->gtkMenuItem.show();
+ menuItem->onTrigger = onTrigger;
+ gtkMenu.append(menuItem->gtkMenuItem);
+
+ return menuItem;
+ }
+
+ MenuRef AddSubMenu(const std::string &label) override {
+ auto menuItem = std::make_shared<MenuItemImplGtk>();
+ menuItems.push_back(menuItem);
+
+ auto subMenu = std::make_shared<MenuImplGtk>();
+ subMenus.push_back(subMenu);
+
+ menuItem->gtkMenuItem.set_label(PrepareMnemonics(label));
+ menuItem->gtkMenuItem.set_use_underline(true);
+ menuItem->gtkMenuItem.set_submenu(subMenu->gtkMenu);
+ menuItem->gtkMenuItem.show_all();
+ gtkMenu.append(menuItem->gtkMenuItem);
+
+ return subMenu;
+ }
+
+ void AddSeparator() override {
+ Gtk::SeparatorMenuItem *gtkMenuItem = Gtk::manage(new Gtk::SeparatorMenuItem());
+ gtkMenuItem->show();
+ gtkMenu.append(*Gtk::manage(gtkMenuItem));
+ }
+
+ void PopUp() override {
+ Glib::RefPtr<Glib::MainLoop> loop = Glib::MainLoop::create();
+ auto signal = gtkMenu.signal_deactivate().connect([&]() { loop->quit(); });
+
+ gtkMenu.show_all();
+ gtkMenu.popup(0, GDK_CURRENT_TIME);
+ loop->run();
+ signal.disconnect();
+ }
+
+ void Clear() override {
+ gtkMenu.foreach([&](Gtk::Widget &w) { gtkMenu.remove(w); });
+ menuItems.clear();
+ subMenus.clear();
+ }
+};
+
+MenuRef CreateMenu() {
+ return std::make_shared<MenuImplGtk>();
+}
+
+class MenuBarImplGtk final : public MenuBar {
+public:
+ Gtk::MenuBar gtkMenuBar;
+ std::vector<std::shared_ptr<MenuImplGtk>> subMenus;
+
+ MenuRef AddSubMenu(const std::string &label) override {
+ auto subMenu = std::make_shared<MenuImplGtk>();
+ subMenus.push_back(subMenu);
+
+ Gtk::MenuItem *gtkMenuItem = Gtk::manage(new Gtk::MenuItem);
+ gtkMenuItem->set_label(PrepareMnemonics(label));
+ gtkMenuItem->set_use_underline(true);
+ gtkMenuItem->set_submenu(subMenu->gtkMenu);
+ gtkMenuItem->show_all();
+ gtkMenuBar.append(*gtkMenuItem);
+
+ return subMenu;
+ }
+
+ void Clear() override {
+ gtkMenuBar.foreach([&](Gtk::Widget &w) { gtkMenuBar.remove(w); });
+ subMenus.clear();
+ }
+};
+
+MenuBarRef GetOrCreateMainMenu(bool *unique) {
+ *unique = false;
+ return std::make_shared<MenuBarImplGtk>();
+}
+
+//-----------------------------------------------------------------------------
+// GTK GL and window extensions
+//-----------------------------------------------------------------------------
+
+class GtkGLWidget : public Gtk::GLArea {
+ Window *_receiver;
+
+public:
+ GtkGLWidget(Platform::Window *receiver) : _receiver(receiver) {
+ set_has_depth_buffer(true);
+ set_can_focus(true);
+ set_events(Gdk::POINTER_MOTION_MASK |
+ Gdk::BUTTON_PRESS_MASK |
+ Gdk::BUTTON_RELEASE_MASK |
+ Gdk::BUTTON_MOTION_MASK |
+ Gdk::SCROLL_MASK |
+ Gdk::LEAVE_NOTIFY_MASK |
+ Gdk::KEY_PRESS_MASK |
+ Gdk::KEY_RELEASE_MASK);
+ }
+
+protected:
+ // Work around a bug fixed in GTKMM 3.22:
+ // https://mail.gnome.org/archives/gtkmm-list/2016-April/msg00020.html
+ Glib::RefPtr<Gdk::GLContext> on_create_context() override {
+ return get_window()->create_gl_context();
+ }
+
+ bool on_render(const Glib::RefPtr<Gdk::GLContext> &context) override {
+ if(_receiver->onRender) {
+ _receiver->onRender();
+ }
+ return true;
+ }
+
+ bool process_pointer_event(MouseEvent::Type type, double x, double y,
+ guint state, guint button = 0, int scroll_delta = 0) {
+ MouseEvent event = {};
+ event.type = type;
+ event.x = x;
+ event.y = y;
+ if(button == 1 || (state & GDK_BUTTON1_MASK) != 0) {
+ event.button = MouseEvent::Button::LEFT;
+ } else if(button == 2 || (state & GDK_BUTTON2_MASK) != 0) {
+ event.button = MouseEvent::Button::MIDDLE;
+ } else if(button == 3 || (state & GDK_BUTTON3_MASK) != 0) {
+ event.button = MouseEvent::Button::RIGHT;
+ }
+ if((state & GDK_SHIFT_MASK) != 0) {
+ event.shiftDown = true;
+ }
+ if((state & GDK_CONTROL_MASK) != 0) {
+ event.controlDown = true;
+ }
+ if(scroll_delta != 0) {
+ event.scrollDelta = scroll_delta;
+ }
+
+ if(_receiver->onMouseEvent) {
+ return _receiver->onMouseEvent(event);
+ }
+
+ return false;
+ }
+
+ bool on_motion_notify_event(GdkEventMotion *gdk_event) override {
+ if(process_pointer_event(MouseEvent::Type::MOTION,
+ gdk_event->x, gdk_event->y, gdk_event->state))
+ return true;
+
+ return Gtk::GLArea::on_motion_notify_event(gdk_event);
+ }
+
+ bool on_button_press_event(GdkEventButton *gdk_event) override {
+ MouseEvent::Type type;
+ if(gdk_event->type == GDK_BUTTON_PRESS) {
+ type = MouseEvent::Type::PRESS;
+ } else if(gdk_event->type == GDK_2BUTTON_PRESS) {
+ type = MouseEvent::Type::DBL_PRESS;
+ } else {
+ return Gtk::GLArea::on_button_press_event(gdk_event);
+ }
+
+ if(process_pointer_event(type, gdk_event->x, gdk_event->y,
+ gdk_event->state, gdk_event->button))
+ return true;
+
+ return Gtk::GLArea::on_button_press_event(gdk_event);
+ }
+
+ bool on_button_release_event(GdkEventButton *gdk_event) override {
+ if(process_pointer_event(MouseEvent::Type::RELEASE,
+ gdk_event->x, gdk_event->y,
+ gdk_event->state, gdk_event->button))
+ return true;
+
+ return Gtk::GLArea::on_button_release_event(gdk_event);
+ }
+
+ bool on_scroll_event(GdkEventScroll *gdk_event) override {
+ int delta;
+ if(gdk_event->delta_y < 0 || gdk_event->direction == GDK_SCROLL_UP) {
+ delta = 1;
+ } else if(gdk_event->delta_y > 0 || gdk_event->direction == GDK_SCROLL_DOWN) {
+ delta = -1;
+ } else {
+ return false;
+ }
+
+ if(process_pointer_event(MouseEvent::Type::SCROLL_VERT,
+ gdk_event->x, gdk_event->y,
+ gdk_event->state, 0, delta))
+ return true;
+
+ return Gtk::GLArea::on_scroll_event(gdk_event);
+ }
+
+ bool on_leave_notify_event(GdkEventCrossing *gdk_event) override {
+ if(process_pointer_event(MouseEvent::Type::LEAVE,
+ gdk_event->x, gdk_event->y, gdk_event->state))
+ return true;
+
+ return Gtk::GLArea::on_leave_notify_event(gdk_event);
+ }
+
+ bool process_key_event(KeyboardEvent::Type type, GdkEventKey *gdk_event) {
+ KeyboardEvent event = {};
+ event.type = type;
+
+ Gdk::ModifierType mod_mask = get_modifier_mask(Gdk::MODIFIER_INTENT_DEFAULT_MOD_MASK);
+ if((gdk_event->state & mod_mask) & ~(GDK_SHIFT_MASK|GDK_CONTROL_MASK)) {
+ return false;
+ }
+
+ event.shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0;
+ event.controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0;
+
+ char32_t chr = gdk_keyval_to_unicode(gdk_keyval_to_lower(gdk_event->keyval));
+ if(chr != 0) {
+ event.key = KeyboardEvent::Key::CHARACTER;
+ event.chr = chr;
+ } else if(gdk_event->keyval >= GDK_KEY_F1 &&
+ gdk_event->keyval <= GDK_KEY_F12) {
+ event.key = KeyboardEvent::Key::FUNCTION;
+ event.num = gdk_event->keyval - GDK_KEY_F1 + 1;
+ } else {
+ return false;
+ }
+
+ if(_receiver->onKeyboardEvent) {
+ return _receiver->onKeyboardEvent(event);
+ }
+
+ return false;
+ }
+
+ bool on_key_press_event(GdkEventKey *gdk_event) override {
+ if(process_key_event(KeyboardEvent::Type::PRESS, gdk_event))
+ return true;
+
+ return Gtk::GLArea::on_key_press_event(gdk_event);
+ }
+
+ bool on_key_release_event(GdkEventKey *gdk_event) override {
+ if(process_key_event(KeyboardEvent::Type::RELEASE, gdk_event))
+ return true;
+
+ return Gtk::GLArea::on_key_release_event(gdk_event);
+ }
+};
+
+class GtkEditorOverlay : public Gtk::Fixed {
+ Window *_receiver;
+ GtkGLWidget _gl_widget;
+ Gtk::Entry _entry;
+
+public:
+ GtkEditorOverlay(Platform::Window *receiver) : _receiver(receiver), _gl_widget(receiver) {
+ add(_gl_widget);
+
+ _entry.set_no_show_all(true);
+ _entry.set_has_frame(false);
+ add(_entry);
+
+ _entry.signal_activate().
+ connect(sigc::mem_fun(this, &GtkEditorOverlay::on_activate));
+ }
+
+ bool is_editing() const {
+ return _entry.is_visible();
+ }
+
+ void start_editing(int x, int y, int font_height, int min_width, bool is_monospace,
+ const std::string &val) {
+ Pango::FontDescription font_desc;
+ font_desc.set_family(is_monospace ? "monospace" : "normal");
+ font_desc.set_absolute_size(font_height * Pango::SCALE);
+ _entry.override_font(font_desc);
+
+ // The y coordinate denotes baseline.
+ Pango::FontMetrics font_metrics = get_pango_context()->get_metrics(font_desc);
+ y -= font_metrics.get_ascent() / Pango::SCALE;
+
+ Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(get_pango_context());
+ layout->set_font_description(font_desc);
+ // Add one extra char width to avoid scrolling.
+ layout->set_text(val + " ");
+ int width = layout->get_logical_extents().get_width();
+
+ Gtk::Border margin = _entry.get_style_context()->get_margin();
+ Gtk::Border border = _entry.get_style_context()->get_border();
+ Gtk::Border padding = _entry.get_style_context()->get_padding();
+ move(_entry,
+ x - margin.get_left() - border.get_left() - padding.get_left(),
+ y - margin.get_top() - border.get_top() - padding.get_top());
+
+ int fitWidth = width / Pango::SCALE + padding.get_left() + padding.get_right();
+ _entry.set_size_request(max(fitWidth, min_width), -1);
+ queue_resize();
+
+ _entry.set_text(val);
+
+ if(!_entry.is_visible()) {
+ _entry.show();
+ _entry.grab_focus();
+
+ // We grab the input for ourselves and not the entry to still have
+ // the pointer events go through the underlay.
+ add_modal_grab();
+ }
+ }
+
+ void stop_editing() {
+ if(_entry.is_visible()) {
+ remove_modal_grab();
+ _entry.hide();
+ _gl_widget.grab_focus();
+ }
+ }
+
+ GtkGLWidget &get_gl_widget() {
+ return _gl_widget;
+ }
+
+protected:
+ bool on_key_press_event(GdkEventKey *gdk_event) override {
+ if(is_editing()) {
+ if(gdk_event->keyval == GDK_KEY_Escape) {
+ return _gl_widget.event((GdkEvent *)gdk_event);
+ } else {
+ _entry.event((GdkEvent *)gdk_event);
+ }
+ return true;
+ }
+
+ return Gtk::Fixed::on_key_press_event(gdk_event);
+ }
+
+ bool on_key_release_event(GdkEventKey *gdk_event) override {
+ if(is_editing()) {
+ _entry.event((GdkEvent *)gdk_event);
+ return true;
+ }
+
+ return Gtk::Fixed::on_key_release_event(gdk_event);
+ }
+
+ void on_size_allocate(Gtk::Allocation& allocation) override {
+ Gtk::Fixed::on_size_allocate(allocation);
+
+ _gl_widget.size_allocate(allocation);
+
+ int width, height, min_height, natural_height;
+ _entry.get_size_request(width, height);
+ _entry.get_preferred_height(min_height, natural_height);
+
+ Gtk::Allocation entry_rect = _entry.get_allocation();
+ entry_rect.set_width(width);
+ entry_rect.set_height(natural_height);
+ _entry.size_allocate(entry_rect);
+ }
+
+ void on_activate() {
+ if(_receiver->onEditingDone) {
+ _receiver->onEditingDone(_entry.get_text());
+ }
+ }
+};
+
+class GtkWindow : public Gtk::Window {
+ Platform::Window *_receiver;
+ Gtk::VBox _vbox;
+ Gtk::MenuBar *_menu_bar = NULL;
+ Gtk::HBox _hbox;
+ GtkEditorOverlay _editor_overlay;
+ Gtk::VScrollbar _scrollbar;
+ bool _is_under_cursor = false;
+ bool _is_fullscreen = false;
+ std::string _tooltip_text;
+ Gdk::Rectangle _tooltip_area;
+
+public:
+ GtkWindow(Platform::Window *receiver) : _receiver(receiver), _editor_overlay(receiver) {
+ _hbox.pack_start(_editor_overlay, /*expand=*/true, /*fill=*/true);
+ _hbox.pack_end(_scrollbar, /*expand=*/false, /*fill=*/false);
+ _vbox.pack_end(_hbox, /*expand=*/true, /*fill=*/true);
+ add(_vbox);
+
+ _vbox.show();
+ _hbox.show();
+ _editor_overlay.show();
+ get_gl_widget().show();
+
+ _scrollbar.get_adjustment()->signal_value_changed().
+ connect(sigc::mem_fun(this, &GtkWindow::on_scrollbar_value_changed));
+
+ get_gl_widget().set_has_tooltip(true);
+ get_gl_widget().signal_query_tooltip().
+ connect(sigc::mem_fun(this, &GtkWindow::on_query_tooltip));
+ }
+
+ bool is_full_screen() const {
+ return _is_fullscreen;
+ }
+
+ Gtk::MenuBar *get_menu_bar() const {
+ return _menu_bar;
+ }
+
+ void set_menu_bar(Gtk::MenuBar *menu_bar) {
+ if(_menu_bar) {
+ _vbox.remove(*_menu_bar);
+ }
+ _menu_bar = menu_bar;
+ if(_menu_bar) {
+ _menu_bar->show_all();
+ _vbox.pack_start(*_menu_bar, /*expand=*/false, /*fill=*/false);
+ }
+ }
+
+ GtkEditorOverlay &get_editor_overlay() {
+ return _editor_overlay;
+ }
+
+ GtkGLWidget &get_gl_widget() {
+ return _editor_overlay.get_gl_widget();
+ }
+
+ Gtk::VScrollbar &get_scrollbar() {
+ return _scrollbar;
+ }
+
+ void set_tooltip(const std::string &text, const Gdk::Rectangle &rect) {
+ if(_tooltip_text != text) {
+ _tooltip_text = text;
+ _tooltip_area = rect;
+ get_gl_widget().trigger_tooltip_query();
+ }
+ }
+
+protected:
+ bool on_query_tooltip(int x, int y, bool keyboard_tooltip,
+ const Glib::RefPtr<Gtk::Tooltip> &tooltip) {
+ tooltip->set_text(_tooltip_text);
+ tooltip->set_tip_area(_tooltip_area);
+ return !_tooltip_text.empty() && (keyboard_tooltip || _is_under_cursor);
+ }
+
+ bool on_enter_notify_event(GdkEventCrossing* gdk_event) override {
+ _is_under_cursor = true;
+
+ return true;
+ }
+
+ bool on_leave_notify_event(GdkEventCrossing* gdk_event) override {
+ _is_under_cursor = false;
+
+ return true;
+ }
+
+ bool on_delete_event(GdkEventAny* gdk_event) override {
+ if(_receiver->onClose) {
+ _receiver->onClose();
+ return true;
+ }
+
+ return false;
+ }
+
+ bool on_window_state_event(GdkEventWindowState *gdk_event) override {
+ _is_fullscreen = gdk_event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
+ if(_receiver->onFullScreen) {
+ _receiver->onFullScreen(_is_fullscreen);
+ }
+
+ return true;
+ }
+
+ void on_scrollbar_value_changed() {
+ if(_receiver->onScrollbarAdjusted) {
+ _receiver->onScrollbarAdjusted(_scrollbar.get_adjustment()->get_value());
+ }
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Windows
+//-----------------------------------------------------------------------------
+
+class WindowImplGtk final : public Window {
+public:
+ GtkWindow gtkWindow;
+ MenuBarRef menuBar;
+
+ WindowImplGtk(Window::Kind kind) : gtkWindow(this) {
+ switch(kind) {
+ case Kind::TOPLEVEL:
+ break;
+
+ case Kind::TOOL:
+ gtkWindow.set_type_hint(Gdk::WINDOW_TYPE_HINT_UTILITY);
+ gtkWindow.set_skip_taskbar_hint(true);
+ gtkWindow.set_skip_pager_hint(true);
+ break;
+ }
+
+ auto icon = LoadPng("freedesktop/solvespace-48x48.png");
+ auto gdkIcon =
+ Gdk::Pixbuf::create_from_data(&icon->data[0], Gdk::COLORSPACE_RGB,
+ icon->format == Pixmap::Format::RGBA, 8,
+ icon->width, icon->height, icon->stride);
+ gtkWindow.set_icon(gdkIcon->copy());
+ }
+
+ double GetPixelDensity() override {
+ return gtkWindow.get_screen()->get_resolution();
+ }
+
+ int GetDevicePixelRatio() override {
+ return gtkWindow.get_scale_factor();
+ }
+
+ bool IsVisible() override {
+ return gtkWindow.is_visible();
+ }
+
+ void SetVisible(bool visible) override {
+ if(visible) {
+ gtkWindow.show();
+ } else {
+ gtkWindow.hide();
+ }
+ }
+
+ void Focus() override {
+ gtkWindow.present();
+ }
+
+ bool IsFullScreen() override {
+ return gtkWindow.is_full_screen();
+ }
+
+ void SetFullScreen(bool fullScreen) override {
+ if(fullScreen) {
+ gtkWindow.fullscreen();
+ } else {
+ gtkWindow.unfullscreen();
+ }
+ }
+
+ void SetTitle(const std::string &title) override {
+ gtkWindow.set_title(PrepareTitle(title));
+ }
+
+ void SetMenuBar(MenuBarRef newMenuBar) override {
+ if(newMenuBar) {
+ Gtk::MenuBar *gtkMenuBar = &((MenuBarImplGtk*)&*newMenuBar)->gtkMenuBar;
+ gtkWindow.set_menu_bar(gtkMenuBar);
+ } else {
+ gtkWindow.set_menu_bar(NULL);
+ }
+ menuBar = newMenuBar;
+ }
+
+ void GetContentSize(double *width, double *height) override {
+ *width = gtkWindow.get_gl_widget().get_allocated_width();
+ *height = gtkWindow.get_gl_widget().get_allocated_height();
+ }
+
+ void SetMinContentSize(double width, double height) override {
+ gtkWindow.get_gl_widget().set_size_request((int)width, (int)height);
+ }
+
+ void FreezePosition(SettingsRef settings, const std::string &key) override {
+ if(!gtkWindow.is_visible()) return;
+
+ int left, top, width, height;
+ gtkWindow.get_position(left, top);
+ gtkWindow.get_size(width, height);
+ bool isMaximized = gtkWindow.is_maximized();
+
+ settings->FreezeInt(key + "_Left", left);
+ settings->FreezeInt(key + "_Top", top);
+ settings->FreezeInt(key + "_Width", width);
+ settings->FreezeInt(key + "_Height", height);
+ settings->FreezeBool(key + "_Maximized", isMaximized);
+ }
+
+ void ThawPosition(SettingsRef settings, const std::string &key) override {
+ int left, top, width, height;
+ gtkWindow.get_position(left, top);
+ gtkWindow.get_size(width, height);
+
+ left = settings->ThawInt(key + "_Left", left);
+ top = settings->ThawInt(key + "_Top", top);
+ width = settings->ThawInt(key + "_Width", width);
+ height = settings->ThawInt(key + "_Height", height);
+
+ gtkWindow.move(left, top);
+ gtkWindow.resize(width, height);
+
+ if(settings->ThawBool(key + "_Maximized", false)) {
+ gtkWindow.maximize();
+ }
+ }
+
+ void SetCursor(Cursor cursor) override {
+ Gdk::CursorType gdkCursorType;
+ switch(cursor) {
+ case Cursor::POINTER: gdkCursorType = Gdk::ARROW; break;
+ case Cursor::HAND: gdkCursorType = Gdk::HAND1; break;
+ default: ssassert(false, "Unexpected cursor");
+ }
+
+ auto gdkWindow = gtkWindow.get_gl_widget().get_window();
+ if(gdkWindow) {
+ gdkWindow->set_cursor(Gdk::Cursor::create(gdkCursorType));
+ }
+ }
+
+ void SetTooltip(const std::string &text, double x, double y,
+ double width, double height) override {
+ gtkWindow.set_tooltip(text, { (int)x, (int)y, (int)width, (int)height });
+ }
+
+ bool IsEditorVisible() override {
+ return gtkWindow.get_editor_overlay().is_editing();
+ }
+
+ void ShowEditor(double x, double y, double fontHeight, double minWidth,
+ bool isMonospace, const std::string &text) override {
+ gtkWindow.get_editor_overlay().start_editing(
+ (int)x, (int)y, (int)fontHeight, (int)minWidth, isMonospace, text);
+ }
+
+ void HideEditor() override {
+ gtkWindow.get_editor_overlay().stop_editing();
+ }
+
+ void SetScrollbarVisible(bool visible) override {
+ if(visible) {
+ gtkWindow.get_scrollbar().show();
+ } else {
+ gtkWindow.get_scrollbar().hide();
+ }
+ }
+
+ void ConfigureScrollbar(double min, double max, double pageSize) override {
+ auto adjustment = gtkWindow.get_scrollbar().get_adjustment();
+ adjustment->configure(adjustment->get_value(), min, max, 1, 4, pageSize);
+ }
+
+ double GetScrollbarPosition() override {
+ return gtkWindow.get_scrollbar().get_adjustment()->get_value();
+ }
+
+ void SetScrollbarPosition(double pos) override {
+ return gtkWindow.get_scrollbar().get_adjustment()->set_value(pos);
+ }
+
+ void Invalidate() override {
+ gtkWindow.get_gl_widget().queue_render();
+ }
+};
+
+WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) {
+ auto window = std::make_shared<WindowImplGtk>(kind);
+ if(parentWindow) {
+ window->gtkWindow.set_transient_for(
+ std::static_pointer_cast<WindowImplGtk>(parentWindow)->gtkWindow);
+ }
+ return window;
+}
+
+//-----------------------------------------------------------------------------
+// 3DConnexion support
+//-----------------------------------------------------------------------------
+
+void Open3DConnexion() {}
+void Close3DConnexion() {}
+
+#if defined(HAVE_SPACEWARE) && defined(GDK_WINDOWING_X11)
+static GdkFilterReturn GdkSpnavFilter(GdkXEvent *gdkXEvent, GdkEvent *gdkEvent, gpointer data) {
+ XEvent *xEvent = (XEvent *)gdkXEvent;
+ WindowImplGtk *window = (WindowImplGtk *)data;
+
+ spnav_event spnavEvent;
+ if(!spnav_x11_event(xEvent, &spnavEvent)) {
+ return GDK_FILTER_CONTINUE;
+ }
+
+ switch(spnavEvent.type) {
+ case SPNAV_EVENT_MOTION: {
+ SixDofEvent event = {};
+ event.type = SixDofEvent::Type::MOTION;
+ event.translationX = (double)spnavEvent.motion.x;
+ event.translationY = (double)spnavEvent.motion.y;
+ event.translationZ = (double)spnavEvent.motion.z * -1.0;
+ event.rotationX = (double)spnavEvent.motion.rx * 0.001;
+ event.rotationY = (double)spnavEvent.motion.ry * 0.001;
+ event.rotationZ = (double)spnavEvent.motion.rz * -0.001;
+ event.shiftDown = xEvent->xmotion.state & ShiftMask;
+ event.controlDown = xEvent->xmotion.state & ControlMask;
+ if(window->onSixDofEvent) {
+ window->onSixDofEvent(event);
+ }
+ break;
+ }
+
+ case SPNAV_EVENT_BUTTON:
+ SixDofEvent event = {};
+ if(spnavEvent.button.press) {
+ event.type = SixDofEvent::Type::PRESS;
+ } else {
+ event.type = SixDofEvent::Type::RELEASE;
+ }
+ switch(spnavEvent.button.bnum) {
+ case 0: event.button = SixDofEvent::Button::FIT; break;
+ default: return GDK_FILTER_REMOVE;
+ }
+ event.shiftDown = xEvent->xmotion.state & ShiftMask;
+ event.controlDown = xEvent->xmotion.state & ControlMask;
+ if(window->onSixDofEvent) {
+ window->onSixDofEvent(event);
+ }
+ break;
+ }
+
+ return GDK_FILTER_REMOVE;
+}
+
+void Request3DConnexionEventsForWindow(WindowRef window) {
+ std::shared_ptr<WindowImplGtk> windowImpl =
+ std::static_pointer_cast<WindowImplGtk>(window);
+
+ Glib::RefPtr<Gdk::Window> gdkWindow = windowImpl->gtkWindow.get_window();
+ if(GDK_IS_X11_DISPLAY(gdkWindow->get_display()->gobj())) {
+ gdkWindow->add_filter(GdkSpnavFilter, windowImpl.get());
+ spnav_x11_open(gdk_x11_get_default_xdisplay(),
+ gdk_x11_window_get_xid(gdkWindow->gobj()));
+ }
+}
+#else
+void Request3DConnexionEventsForWindow(WindowRef window) {}
+#endif
+
+//-----------------------------------------------------------------------------
+// Message dialogs
+//-----------------------------------------------------------------------------
+
+class MessageDialogImplGtk;
+
+static std::vector<std::shared_ptr<MessageDialogImplGtk>> shownMessageDialogs;
+
+class MessageDialogImplGtk final : public MessageDialog,
+ public std::enable_shared_from_this<MessageDialogImplGtk> {
+public:
+ Gtk::Image gtkImage;
+ Gtk::MessageDialog gtkDialog;
+
+ MessageDialogImplGtk(Gtk::Window &parent)
+ : gtkDialog(parent, "", /*use_markup=*/false, Gtk::MESSAGE_INFO,
+ Gtk::BUTTONS_NONE, /*modal=*/true)
+ {
+ SetTitle("Message");
+ }
+
+ void SetType(Type type) override {
+ switch(type) {
+ case Type::INFORMATION:
+ gtkImage.set_from_icon_name("dialog-information", Gtk::ICON_SIZE_DIALOG);
+ break;
+
+ case Type::QUESTION:
+ gtkImage.set_from_icon_name("dialog-question", Gtk::ICON_SIZE_DIALOG);
+ break;
+
+ case Type::WARNING:
+ gtkImage.set_from_icon_name("dialog-warning", Gtk::ICON_SIZE_DIALOG);
+ break;
+
+ case Type::ERROR:
+ gtkImage.set_from_icon_name("dialog-error", Gtk::ICON_SIZE_DIALOG);
+ break;
+ }
+ gtkDialog.set_image(gtkImage);
+ }
+
+ void SetTitle(std::string title) override {
+ gtkDialog.set_title(PrepareTitle(title));
+ }
+
+ void SetMessage(std::string message) override {
+ gtkDialog.set_message(message);
+ }
+
+ void SetDescription(std::string description) override {
+ gtkDialog.set_secondary_text(description);
+ }
+
+ void AddButton(std::string label, Response response, bool isDefault) override {
+ int responseId = 0;
+ switch(response) {
+ case Response::NONE: ssassert(false, "Unexpected response");
+ case Response::OK: responseId = Gtk::RESPONSE_OK; break;
+ case Response::YES: responseId = Gtk::RESPONSE_YES; break;
+ case Response::NO: responseId = Gtk::RESPONSE_NO; break;
+ case Response::CANCEL: responseId = Gtk::RESPONSE_CANCEL; break;
+ }
+ gtkDialog.add_button(PrepareMnemonics(label), responseId);
+ if(isDefault) {
+ gtkDialog.set_default_response(responseId);
+ }
+ }
+
+ Response ProcessResponse(int gtkResponse) {
+ Response response;
+ switch(gtkResponse) {
+ case Gtk::RESPONSE_OK: response = Response::OK; break;
+ case Gtk::RESPONSE_YES: response = Response::YES; break;
+ case Gtk::RESPONSE_NO: response = Response::NO; break;
+ case Gtk::RESPONSE_CANCEL: response = Response::CANCEL; break;
+
+ case Gtk::RESPONSE_NONE:
+ case Gtk::RESPONSE_CLOSE:
+ case Gtk::RESPONSE_DELETE_EVENT:
+ response = Response::NONE;
+ break;
+
+ default: ssassert(false, "Unexpected response");
+ }
+
+ if(onResponse) {
+ onResponse(response);
+ }
+ return response;
+ }
+
+ void ShowModal() override {
+ gtkDialog.signal_hide().connect([this] {
+ auto it = std::remove(shownMessageDialogs.begin(), shownMessageDialogs.end(),
+ shared_from_this());
+ shownMessageDialogs.erase(it);
+ });
+ shownMessageDialogs.push_back(shared_from_this());
+
+ gtkDialog.signal_response().connect([this](int gtkResponse) {
+ ProcessResponse(gtkResponse);
+ gtkDialog.hide();
+ });
+ gtkDialog.show();
+ }
+
+ Response RunModal() override {
+ return ProcessResponse(gtkDialog.run());
+ }
+};
+
+MessageDialogRef CreateMessageDialog(WindowRef parentWindow) {
+ return std::make_shared<MessageDialogImplGtk>(
+ std::static_pointer_cast<WindowImplGtk>(parentWindow)->gtkWindow);
+}
+
+//-----------------------------------------------------------------------------
+// File dialogs
+//-----------------------------------------------------------------------------
+
+class FileDialogImplGtk : public FileDialog {
+public:
+ Gtk::FileChooser *gtkChooser;
+ std::vector<std::string> extensions;
+
+ void InitFileChooser(Gtk::FileChooser &chooser) {
+ gtkChooser = &chooser;
+ gtkChooser->property_filter().signal_changed().
+ connect(sigc::mem_fun(this, &FileDialogImplGtk::FilterChanged));
+ }
+
+ void SetCurrentName(std::string name) override {
+ gtkChooser->set_current_name(name);
+ }
+
+ Platform::Path GetFilename() override {
+ return Path::From(gtkChooser->get_filename());
+ }
+
+ void SetFilename(Platform::Path path) override {
+ gtkChooser->set_filename(path.raw);
+ }
+
+ void AddFilter(std::string name, std::vector<std::string> extensions) override {
+ Glib::RefPtr<Gtk::FileFilter> gtkFilter = Gtk::FileFilter::create();
+ Glib::ustring desc;
+ for(auto extension : extensions) {
+ Glib::ustring pattern = "*";
+ if(!extension.empty()) {
+ pattern = "*." + extension;
+ gtkFilter->add_pattern(pattern);
+ gtkFilter->add_pattern(Glib::ustring(pattern).uppercase());
+ }
+ if(!desc.empty()) {
+ desc += ", ";
+ }
+ desc += pattern;
+ }
+ gtkFilter->set_name(name + " (" + desc + ")");
+
+ this->extensions.push_back(extensions.front());
+ gtkChooser->add_filter(gtkFilter);
+ }
+
+ std::string GetExtension() {
+ auto filters = gtkChooser->list_filters();
+ size_t filterIndex =
+ std::find(filters.begin(), filters.end(), gtkChooser->get_filter()) -
+ filters.begin();
+ if(filterIndex < extensions.size()) {
+ return extensions[filterIndex];
+ } else {
+ return extensions.front();
+ }
+ }
+
+ void SetExtension(std::string extension) {
+ auto filters = gtkChooser->list_filters();
+ size_t extensionIndex =
+ std::find(extensions.begin(), extensions.end(), extension) -
+ extensions.begin();
+ if(extensionIndex < filters.size()) {
+ gtkChooser->set_filter(filters[extensionIndex]);
+ } else {
+ gtkChooser->set_filter(filters.front());
+ }
+ }
+
+ void FilterChanged() {
+ std::string extension = GetExtension();
+ if(extension.empty())
+ return;
+
+ Platform::Path path = GetFilename();
+ SetCurrentName(path.WithExtension(extension).FileName());
+ }
+
+ void FreezeChoices(SettingsRef settings, const std::string &key) override {
+ settings->FreezeString("Dialog_" + key + "_Folder",
+ gtkChooser->get_current_folder());
+ settings->FreezeString("Dialog_" + key + "_Filter", GetExtension());
+ }
+
+ void ThawChoices(SettingsRef settings, const std::string &key) override {
+ gtkChooser->set_current_folder(settings->ThawString("Dialog_" + key + "_Folder"));
+ SetExtension(settings->ThawString("Dialog_" + key + "_Filter"));
+ }
+
+ void CheckForUntitledFile() {
+ if(gtkChooser->get_action() == Gtk::FILE_CHOOSER_ACTION_SAVE &&
+ Path::From(gtkChooser->get_current_name()).FileStem().empty()) {
+ gtkChooser->set_current_name(std::string(_("untitled")) + "." + GetExtension());
+ }
+ }
+};
+
+class FileDialogGtkImplGtk final : public FileDialogImplGtk {
+public:
+ Gtk::FileChooserDialog gtkDialog;
+
+ FileDialogGtkImplGtk(Gtk::Window >kParent, bool isSave)
+ : gtkDialog(gtkParent,
+ isSave ? C_("title", "Save File")
+ : C_("title", "Open File"),
+ isSave ? Gtk::FILE_CHOOSER_ACTION_SAVE
+ : Gtk::FILE_CHOOSER_ACTION_OPEN) {
+ gtkDialog.add_button(C_("button", "_Cancel"), Gtk::RESPONSE_CANCEL);
+ gtkDialog.add_button(isSave ? C_("button", "_Save")
+ : C_("button", "_Open"), Gtk::RESPONSE_OK);
+ gtkDialog.set_default_response(Gtk::RESPONSE_OK);
+ InitFileChooser(gtkDialog);
+ }
+
+ void SetTitle(std::string title) override {
+ gtkDialog.set_title(PrepareTitle(title));
+ }
+
+ bool RunModal() override {
+ CheckForUntitledFile();
+ if(gtkDialog.run() == Gtk::RESPONSE_OK) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+};
+
+#if defined(HAVE_GTK_FILECHOOSERNATIVE)
+
+class FileDialogNativeImplGtk final : public FileDialogImplGtk {
+public:
+ Glib::RefPtr<Gtk::FileChooserNative> gtkNative;
+
+ FileDialogNativeImplGtk(Gtk::Window >kParent, bool isSave) {
+ gtkNative = Gtk::FileChooserNative::create(
+ isSave ? C_("title", "Save File")
+ : C_("title", "Open File"),
+ gtkParent,
+ isSave ? Gtk::FILE_CHOOSER_ACTION_SAVE
+ : Gtk::FILE_CHOOSER_ACTION_OPEN,
+ isSave ? C_("button", "_Save")
+ : C_("button", "_Open"),
+ C_("button", "_Cancel"));
+ // Seriously, GTK?!
+ InitFileChooser(*gtkNative.operator->());
+ }
+
+ void SetTitle(std::string title) override {
+ gtkNative->set_title(PrepareTitle(title));
+ }
+
+ bool RunModal() override {
+ CheckForUntitledFile();
+ if(gtkNative->run() == Gtk::RESPONSE_ACCEPT) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+};
+
+#endif
+
+#if defined(HAVE_GTK_FILECHOOSERNATIVE)
+# define FILE_DIALOG_IMPL FileDialogNativeImplGtk
+#else
+# define FILE_DIALOG_IMPL FileDialogGtkImplGtk
+#endif
+
+FileDialogRef CreateOpenFileDialog(WindowRef parentWindow) {
+ Gtk::Window >kParent = std::static_pointer_cast<WindowImplGtk>(parentWindow)->gtkWindow;
+ return std::make_shared<FILE_DIALOG_IMPL>(gtkParent, /*isSave=*/false);
+}
+
+FileDialogRef CreateSaveFileDialog(WindowRef parentWindow) {
+ Gtk::Window >kParent = std::static_pointer_cast<WindowImplGtk>(parentWindow)->gtkWindow;
+ return std::make_shared<FILE_DIALOG_IMPL>(gtkParent, /*isSave=*/true);
+}
+
+//-----------------------------------------------------------------------------
+// Application-wide APIs
+//-----------------------------------------------------------------------------
+
+std::vector<Platform::Path> GetFontFiles() {
+ std::vector<Platform::Path> fonts;
+
+ // fontconfig is already initialized by GTK
+ FcPattern *pat = FcPatternCreate();
+ FcObjectSet *os = FcObjectSetBuild(FC_FILE, (char *)0);
+ FcFontSet *fs = FcFontList(0, pat, os);
+
+ for(int i = 0; i < fs->nfont; i++) {
+ FcChar8 *filenameFC = FcPatternFormat(fs->fonts[i], (const FcChar8*) "%{file}");
+ fonts.push_back(Platform::Path::From((const char *)filenameFC));
+ FcStrFree(filenameFC);
+ }
+
+ FcFontSetDestroy(fs);
+ FcObjectSetDestroy(os);
+ FcPatternDestroy(pat);
+
+ return fonts;
+}
+
+void OpenInBrowser(const std::string &url) {
+ gtk_show_uri(Gdk::Screen::get_default()->gobj(), url.c_str(), GDK_CURRENT_TIME, NULL);
+}
+
+Gtk::Main *gtkMain;
+
+std::vector<std::string> InitGui(int argc, char **argv) {
+ // It would in principle be possible to judiciously use Glib::filename_{from,to}_utf8,
+ // but it's not really worth the effort.
+ // The setlocale() call is necessary for Glib::get_charset() to detect the system
+ // character set; otherwise it thinks it is always ANSI_X3.4-1968.
+ // We set it back to C after all so that printf() and friends behave in a consistent way.
+ setlocale(LC_ALL, "");
+ if(!Glib::get_charset()) {
+ dbp("Sorry, only UTF-8 locales are supported.");
+ exit(1);
+ }
+ setlocale(LC_ALL, "C");
+
+ // Let GTK parse arguments and update argc/argv. (They're passed by reference.)
+ gtkMain = new Gtk::Main(argc, argv, /*set_locale=*/false);
+
+ // Now that GTK arguments are removed, grab arguments for ourselves.
+ std::vector<std::string> args = InitCli(argc, argv);
+
+ // Add our application-specific styles, to override GTK defaults.
+ Glib::RefPtr<Gtk::CssProvider> style_provider = Gtk::CssProvider::create();
+ style_provider->load_from_data(R"(
+ entry {
+ background: white;
+ color: black;
+ }
+ )");
+ Gtk::StyleContext::add_provider_for_screen(
+ Gdk::Screen::get_default(), style_provider,
+ 600 /*Gtk::STYLE_PROVIDER_PRIORITY_APPLICATION*/);
+
+ // Set locale from user preferences.
+ // This apparently only consults the LANGUAGE environment variable.
+ const char* const* langNames = g_get_language_names();
+ while(*langNames) {
+ if(SetLocale(*langNames++)) break;
+ }
+ if(!*langNames) {
+ SetLocale("en_US");
+ }
+
+ return args;
+}
+
+void RunGui() {
+ Gtk::Main::run();
+}
+
+void ExitGui() {
+ Gtk::Main::quit();
+}
+
+void ClearGui() {
+ delete gtkMain;
+}
+
+}
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// The Cocoa-based implementation of platform-dependent GUI functionality.
+//
+// Copyright 2018 whitequark
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+#import <AppKit/AppKit.h>
+
+using namespace SolveSpace;
+
+//-----------------------------------------------------------------------------
+// Internal AppKit classes
+//-----------------------------------------------------------------------------
+
+@interface NSToolTipManager : NSObject
++ (NSToolTipManager *)sharedToolTipManager;
+- (void)setInitialToolTipDelay:(double)delay;
+- (void)orderOutToolTip;
+- (void)abortToolTip;
+- (void)_displayTemporaryToolTipForView:(id)arg1 withString:(id)arg2;
+@end
+
+//-----------------------------------------------------------------------------
+// Objective-C bridging
+//-----------------------------------------------------------------------------
+
+static NSString* Wrap(const std::string &s) {
+ return [NSString stringWithUTF8String:s.c_str()];
+}
+
+@interface SSFunction : NSObject
+- (SSFunction *)initWithFunction:(std::function<void ()> *)aFunc;
+- (void)run;
+@end
+
+@implementation SSFunction
+{
+ std::function<void ()> *func;
+}
+
+- (SSFunction *)initWithFunction:(std::function<void ()> *)aFunc {
+ if(self = [super init]) {
+ func = aFunc;
+ }
+ return self;
+}
+
+- (void)run {
+ if(*func) (*func)();
+}
+@end
+
+namespace SolveSpace {
+namespace Platform {
+
+//-----------------------------------------------------------------------------
+// Utility functions
+//-----------------------------------------------------------------------------
+
+static std::string PrepareMnemonics(std::string label) {
+ // OS X does not support mnemonics
+ label.erase(std::remove(label.begin(), label.end(), '&'), label.end());
+ return label;
+}
+
+//-----------------------------------------------------------------------------
+// Fatal errors
+//-----------------------------------------------------------------------------
+
+// This gets put into the "Application Specific Information" field in crash
+// reporter dialog.
+typedef struct {
+ unsigned version __attribute__((aligned(8)));
+ const char *message __attribute__((aligned(8)));
+ const char *signature __attribute__((aligned(8)));
+ const char *backtrace __attribute__((aligned(8)));
+ const char *message2 __attribute__((aligned(8)));
+ void *reserved __attribute__((aligned(8)));
+ void *reserved2 __attribute__((aligned(8)));
+} crash_info_t;
+
+#define CRASH_VERSION 4
+
+crash_info_t crashAnnotation __attribute__((section("__DATA,__crash_info"))) = {
+ CRASH_VERSION, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+void FatalError(const std::string &message) {
+ crashAnnotation.message = message.c_str();
+ abort();
+}
+
+//-----------------------------------------------------------------------------
+// Settings
+//-----------------------------------------------------------------------------
+
+class SettingsImplCocoa final : public Settings {
+public:
+ NSUserDefaults *userDefaults;
+
+ SettingsImplCocoa() {
+ userDefaults = [NSUserDefaults standardUserDefaults];
+ }
+
+ void FreezeInt(const std::string &key, uint32_t value) override {
+ [userDefaults setInteger:value forKey:Wrap(key)];
+ }
+
+ uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) override {
+ NSString *nsKey = Wrap(key);
+ if([userDefaults objectForKey:nsKey]) {
+ return [userDefaults integerForKey:nsKey];
+ }
+ return defaultValue;
+ }
+
+ void FreezeBool(const std::string &key, bool value) override {
+ [userDefaults setBool:value forKey:Wrap(key)];
+ }
+
+ bool ThawBool(const std::string &key, bool defaultValue = false) override {
+ NSString *nsKey = Wrap(key);
+ if([userDefaults objectForKey:nsKey]) {
+ return [userDefaults boolForKey:nsKey];
+ }
+ return defaultValue;
+ }
+
+ void FreezeFloat(const std::string &key, double value) override {
+ [userDefaults setDouble:value forKey:Wrap(key)];
+ }
+
+ double ThawFloat(const std::string &key, double defaultValue = 0.0) override {
+ NSString *nsKey = Wrap(key);
+ if([userDefaults objectForKey:nsKey]) {
+ return [userDefaults doubleForKey:nsKey];
+ }
+ return defaultValue;
+ }
+
+ void FreezeString(const std::string &key, const std::string &value) override {
+ [userDefaults setObject:Wrap(value) forKey:Wrap(key)];
+ }
+
+ std::string ThawString(const std::string &key,
+ const std::string &defaultValue = "") override {
+ NSObject *nsValue = [userDefaults objectForKey:Wrap(key)];
+ if(nsValue && [nsValue isKindOfClass:[NSString class]]) {
+ return [(NSString *)nsValue UTF8String];
+ }
+ return defaultValue;
+ }
+};
+
+SettingsRef GetSettings() {
+ return std::make_shared<SettingsImplCocoa>();
+}
+
+//-----------------------------------------------------------------------------
+// Timers
+//-----------------------------------------------------------------------------
+
+class TimerImplCocoa final : public Timer {
+public:
+ NSTimer *timer;
+
+ TimerImplCocoa() : timer(NULL) {}
+
+ void RunAfter(unsigned milliseconds) override {
+ SSFunction *callback = [[SSFunction alloc] initWithFunction:&this->onTimeout];
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
+ [callback methodSignatureForSelector:@selector(run)]];
+ invocation.target = callback;
+ invocation.selector = @selector(run);
+
+ if(timer != NULL) {
+ [timer invalidate];
+ }
+ timer = [NSTimer scheduledTimerWithTimeInterval:(milliseconds / 1000.0)
+ invocation:invocation repeats:NO];
+ }
+
+ ~TimerImplCocoa() {
+ if(timer != NULL) {
+ [timer invalidate];
+ }
+ }
+};
+
+TimerRef CreateTimer() {
+ return std::make_shared<TimerImplCocoa>();
+}
+
+//-----------------------------------------------------------------------------
+// Menus
+//-----------------------------------------------------------------------------
+
+class MenuItemImplCocoa final : public MenuItem {
+public:
+ SSFunction *ssFunction;
+ NSMenuItem *nsMenuItem;
+
+ MenuItemImplCocoa() {
+ ssFunction = [[SSFunction alloc] initWithFunction:&onTrigger];
+ nsMenuItem = [[NSMenuItem alloc] initWithTitle:@""
+ action:@selector(run) keyEquivalent:@""];
+ nsMenuItem.target = ssFunction;
+ }
+
+ void SetAccelerator(KeyboardEvent accel) override {
+ unichar accelChar;
+ switch(accel.key) {
+ case KeyboardEvent::Key::CHARACTER:
+ if(accel.chr == NSDeleteCharacter) {
+ accelChar = NSBackspaceCharacter;
+ } else {
+ accelChar = accel.chr;
+ }
+ break;
+
+ case KeyboardEvent::Key::FUNCTION:
+ accelChar = NSF1FunctionKey + accel.num - 1;
+ break;
+ }
+ nsMenuItem.keyEquivalent = [[NSString alloc] initWithCharacters:&accelChar length:1];
+
+ NSUInteger modifierMask = 0;
+ if(accel.shiftDown)
+ modifierMask |= NSEventModifierFlagShift;
+ if(accel.controlDown)
+ modifierMask |= NSEventModifierFlagCommand;
+ nsMenuItem.keyEquivalentModifierMask = modifierMask;
+ }
+
+ void SetIndicator(Indicator state) override {
+ // macOS does not support radio menu items
+ }
+
+ void SetActive(bool active) override {
+ nsMenuItem.state = active ? NSControlStateValueOn : NSControlStateValueOff;
+ }
+
+ void SetEnabled(bool enabled) override {
+ nsMenuItem.enabled = enabled;
+ }
+};
+
+class MenuImplCocoa final : public Menu {
+public:
+ NSMenu *nsMenu;
+
+ std::vector<std::shared_ptr<MenuItemImplCocoa>> menuItems;
+ std::vector<std::shared_ptr<MenuImplCocoa>> subMenus;
+
+ MenuImplCocoa() {
+ nsMenu = [[NSMenu alloc] initWithTitle:@""];
+ [nsMenu setAutoenablesItems:NO];
+ }
+
+ MenuItemRef AddItem(const std::string &label,
+ std::function<void()> onTrigger = NULL,
+ bool mnemonics = true) override {
+ auto menuItem = std::make_shared<MenuItemImplCocoa>();
+ menuItems.push_back(menuItem);
+
+ menuItem->onTrigger = onTrigger;
+ [menuItem->nsMenuItem setTitle:Wrap(mnemonics ? PrepareMnemonics(label) : label)];
+ [nsMenu addItem:menuItem->nsMenuItem];
+
+ return menuItem;
+ }
+
+ MenuRef AddSubMenu(const std::string &label) override {
+ auto subMenu = std::make_shared<MenuImplCocoa>();
+ subMenus.push_back(subMenu);
+
+ NSMenuItem *nsMenuItem =
+ [nsMenu addItemWithTitle:Wrap(PrepareMnemonics(label)) action:nil keyEquivalent:@""];
+ [nsMenu setSubmenu:subMenu->nsMenu forItem:nsMenuItem];
+
+ return subMenu;
+ }
+
+ void AddSeparator() override {
+ [nsMenu addItem:[NSMenuItem separatorItem]];
+ }
+
+ void PopUp() override {
+ [NSMenu popUpContextMenu:nsMenu withEvent:[NSApp currentEvent] forView:nil];
+ }
+
+ void Clear() override {
+ [nsMenu removeAllItems];
+ menuItems.clear();
+ subMenus.clear();
+ }
+};
+
+MenuRef CreateMenu() {
+ return std::make_shared<MenuImplCocoa>();
+}
+
+class MenuBarImplCocoa final : public MenuBar {
+public:
+ NSMenu *nsMenuBar;
+
+ std::vector<std::shared_ptr<MenuImplCocoa>> subMenus;
+
+ MenuBarImplCocoa() {
+ nsMenuBar = [NSApp mainMenu];
+ }
+
+ MenuRef AddSubMenu(const std::string &label) override {
+ auto subMenu = std::make_shared<MenuImplCocoa>();
+ subMenus.push_back(subMenu);
+
+ NSMenuItem *nsMenuItem = [nsMenuBar addItemWithTitle:@"" action:nil keyEquivalent:@""];
+ [subMenu->nsMenu setTitle:Wrap(PrepareMnemonics(label))];
+ [nsMenuBar setSubmenu:subMenu->nsMenu forItem:nsMenuItem];
+
+ return subMenu;
+ }
+
+ void Clear() override {
+ while([nsMenuBar numberOfItems] != 1) {
+ [nsMenuBar removeItemAtIndex:1];
+ }
+ subMenus.clear();
+ }
+};
+
+MenuBarRef GetOrCreateMainMenu(bool *unique) {
+ static std::shared_ptr<MenuBarImplCocoa> mainMenu;
+ if(!mainMenu) {
+ mainMenu = std::make_shared<MenuBarImplCocoa>();
+ }
+ *unique = true;
+ return mainMenu;
+}
+
+}
+}
+
+//-----------------------------------------------------------------------------
+// Cocoa NSView and NSWindow extensions
+//-----------------------------------------------------------------------------
+
+@interface SSView : NSOpenGLView
+@property Platform::Window *receiver;
+
+@property BOOL acceptsFirstResponder;
+
+@property(readonly, getter=isEditing) BOOL editing;
+- (void)startEditing:(NSString *)text at:(NSPoint)origin
+ withHeight:(double)fontHeight minWidth:(double)minWidth
+ usingMonospace:(BOOL)isMonospace;
+- (void)stopEditing;
+- (void)didEdit:(NSString *)text;
+
+@property double scrollerMin;
+@property double scrollerMax;
+@end
+
+@implementation SSView
+{
+ NSTrackingArea *trackingArea;
+ NSTextField *editor;
+}
+
+@synthesize acceptsFirstResponder;
+
+- (id)initWithFrame:(NSRect)frameRect {
+ NSOpenGLPixelFormatAttribute attrs[] = {
+ NSOpenGLPFADoubleBuffer,
+ NSOpenGLPFAColorSize, 24,
+ NSOpenGLPFADepthSize, 24,
+ 0
+ };
+ NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+ if(self = [super initWithFrame:frameRect pixelFormat:pixelFormat]) {
+ self.wantsBestResolutionOpenGLSurface = YES;
+ self.wantsLayer = YES;
+ editor = [[NSTextField alloc] init];
+ editor.editable = YES;
+ [[editor cell] setWraps:NO];
+ [[editor cell] setScrollable:YES];
+ editor.bezeled = NO;
+ editor.target = self;
+ editor.action = @selector(didEdit:);
+ }
+ return self;
+}
+
+- (void)dealloc {
+}
+
+- (BOOL)isFlipped {
+ return YES;
+}
+
+@synthesize receiver;
+
+- (void)drawRect:(NSRect)aRect {
+ [[self openGLContext] makeCurrentContext];
+ if(receiver->onRender) {
+ receiver->onRender();
+ }
+ [[self openGLContext] flushBuffer];
+}
+
+- (BOOL)acceptsFirstMouse:(NSEvent *)event {
+ return YES;
+}
+
+- (void)updateTrackingAreas {
+ [self removeTrackingArea:trackingArea];
+ trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
+ options:(NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved |
+ ([self acceptsFirstResponder]
+ ? NSTrackingActiveInKeyWindow
+ : NSTrackingActiveAlways))
+ owner:self userInfo:nil];
+ [self addTrackingArea:trackingArea];
+ [super updateTrackingAreas];
+}
+
+- (Platform::MouseEvent)convertMouseEvent:(NSEvent *)nsEvent {
+ Platform::MouseEvent event = {};
+
+ NSPoint nsPoint = [self convertPoint:nsEvent.locationInWindow fromView:self];
+ event.x = nsPoint.x;
+ event.y = self.bounds.size.height - nsPoint.y;
+
+ NSUInteger nsFlags = [nsEvent modifierFlags];
+ if(nsFlags & NSEventModifierFlagShift) event.shiftDown = true;
+ if(nsFlags & NSEventModifierFlagCommand) event.controlDown = true;
+
+ return event;
+}
+
+- (void)mouseMotionEvent:(NSEvent *)nsEvent {
+ using Platform::MouseEvent;
+
+ MouseEvent event = [self convertMouseEvent:nsEvent];
+ event.type = MouseEvent::Type::MOTION;
+ event.button = MouseEvent::Button::NONE;
+
+ NSUInteger nsButtons = [NSEvent pressedMouseButtons];
+ if(nsButtons & (1 << 0)) {
+ event.button = MouseEvent::Button::LEFT;
+ } else if(nsButtons & (1 << 1)) {
+ event.button = MouseEvent::Button::RIGHT;
+ } else if(nsButtons & (1 << 2)) {
+ event.button = MouseEvent::Button::MIDDLE;
+ }
+
+ if(receiver->onMouseEvent) {
+ receiver->onMouseEvent(event);
+ }
+}
+
+- (void)mouseMotionEvent:(NSEvent *)nsEvent withButton:(Platform::MouseEvent::Button)button {
+ using Platform::MouseEvent;
+
+ MouseEvent event = [self convertMouseEvent:nsEvent];
+ event.type = MouseEvent::Type::MOTION;
+ event.button = button;
+
+ if(receiver->onMouseEvent) {
+ receiver->onMouseEvent(event);
+ }
+}
+
+- (void)mouseMoved:(NSEvent *)nsEvent {
+ [self mouseMotionEvent:nsEvent];
+ [super mouseMoved:nsEvent];
+}
+
+- (void)mouseDragged:(NSEvent *)nsEvent {
+ [self mouseMotionEvent:nsEvent withButton:Platform::MouseEvent::Button::LEFT];
+}
+
+- (void)otherMouseDragged:(NSEvent *)nsEvent {
+ [self mouseMotionEvent:nsEvent];
+}
+
+- (void)rightMouseDragged:(NSEvent *)nsEvent {
+ [self mouseMotionEvent:nsEvent];
+}
+
+- (void)mouseButtonEvent:(NSEvent *)nsEvent withType:(Platform::MouseEvent::Type)type {
+ using Platform::MouseEvent;
+
+ MouseEvent event = [self convertMouseEvent:nsEvent];
+ event.type = type;
+ if([nsEvent buttonNumber] == 0) {
+ event.button = MouseEvent::Button::LEFT;
+ } else if([nsEvent buttonNumber] == 1) {
+ event.button = MouseEvent::Button::RIGHT;
+ } else if([nsEvent buttonNumber] == 2) {
+ event.button = MouseEvent::Button::MIDDLE;
+ } else return;
+
+ if(receiver->onMouseEvent) {
+ receiver->onMouseEvent(event);
+ }
+}
+
+- (void)mouseDownEvent:(NSEvent *)nsEvent {
+ using Platform::MouseEvent;
+
+ MouseEvent::Type type;
+ if([nsEvent clickCount] == 1) {
+ type = MouseEvent::Type::PRESS;
+ } else {
+ type = MouseEvent::Type::DBL_PRESS;
+ }
+ [self mouseButtonEvent:nsEvent withType:type];
+}
+
+- (void)mouseUpEvent:(NSEvent *)nsEvent {
+ using Platform::MouseEvent;
+
+ [self mouseButtonEvent:nsEvent withType:(MouseEvent::Type::RELEASE)];
+}
+
+- (void)mouseDown:(NSEvent *)nsEvent {
+ [self mouseDownEvent:nsEvent];
+}
+
+- (void)otherMouseDown:(NSEvent *)nsEvent {
+ [self mouseDownEvent:nsEvent];
+}
+
+- (void)rightMouseDown:(NSEvent *)nsEvent {
+ [self mouseDownEvent:nsEvent];
+ [super rightMouseDown:nsEvent];
+}
+
+- (void)mouseUp:(NSEvent *)nsEvent {
+ [self mouseUpEvent:nsEvent];
+}
+
+- (void)otherMouseUp:(NSEvent *)nsEvent {
+ [self mouseUpEvent:nsEvent];
+}
+
+- (void)rightMouseUp:(NSEvent *)nsEvent {
+ [self mouseUpEvent:nsEvent];
+}
+
+- (void)scrollWheel:(NSEvent *)nsEvent {
+ using Platform::MouseEvent;
+
+ MouseEvent event = [self convertMouseEvent:nsEvent];
+ event.type = MouseEvent::Type::SCROLL_VERT;
+ event.scrollDelta = [nsEvent deltaY];
+
+ if(receiver->onMouseEvent) {
+ receiver->onMouseEvent(event);
+ }
+}
+
+- (void)mouseExited:(NSEvent *)nsEvent {
+ using Platform::MouseEvent;
+
+ MouseEvent event = [self convertMouseEvent:nsEvent];
+ event.type = MouseEvent::Type::LEAVE;
+
+ if(receiver->onMouseEvent) {
+ receiver->onMouseEvent(event);
+ }
+}
+
+- (Platform::KeyboardEvent)convertKeyboardEvent:(NSEvent *)nsEvent {
+ using Platform::KeyboardEvent;
+
+ KeyboardEvent event = {};
+
+ NSUInteger nsFlags = [nsEvent modifierFlags];
+ if(nsFlags & NSEventModifierFlagShift)
+ event.shiftDown = true;
+ if(nsFlags & NSEventModifierFlagCommand)
+ event.controlDown = true;
+
+ unichar chr = 0;
+ if(NSString *nsChr = [[nsEvent charactersIgnoringModifiers] lowercaseString]) {
+ chr = [nsChr characterAtIndex:0];
+ }
+ if(chr >= NSF1FunctionKey && chr <= NSF12FunctionKey) {
+ event.key = KeyboardEvent::Key::FUNCTION;
+ event.num = chr - NSF1FunctionKey + 1;
+ } else {
+ event.key = KeyboardEvent::Key::CHARACTER;
+ event.chr = chr;
+ }
+
+ return event;
+}
+
+- (void)keyDown:(NSEvent *)nsEvent {
+ using Platform::KeyboardEvent;
+
+ if([NSEvent modifierFlags] & ~(NSEventModifierFlagShift|NSEventModifierFlagCommand)) {
+ [super keyDown:nsEvent];
+ return;
+ }
+
+ KeyboardEvent event = [self convertKeyboardEvent:nsEvent];
+ event.type = KeyboardEvent::Type::PRESS;
+
+ if(receiver->onKeyboardEvent) {
+ receiver->onKeyboardEvent(event);
+ return;
+ }
+
+ [super keyDown:nsEvent];
+}
+
+- (void)keyUp:(NSEvent *)nsEvent {
+ using Platform::KeyboardEvent;
+
+ if([NSEvent modifierFlags] & ~(NSEventModifierFlagShift|NSEventModifierFlagCommand)) {
+ [super keyUp:nsEvent];
+ return;
+ }
+
+ KeyboardEvent event = [self convertKeyboardEvent:nsEvent];
+ event.type = KeyboardEvent::Type::RELEASE;
+
+ if(receiver->onKeyboardEvent) {
+ receiver->onKeyboardEvent(event);
+ return;
+ }
+
+ [super keyUp:nsEvent];
+}
+
+@synthesize editing;
+
+- (void)startEditing:(NSString *)text at:(NSPoint)origin withHeight:(double)fontHeight
+ minWidth:(double)minWidth usingMonospace:(BOOL)isMonospace {
+ if(!editing) {
+ [self addSubview:editor];
+ editing = YES;
+ }
+
+ if(isMonospace) {
+ editor.font = [NSFont fontWithName:@"Monaco" size:fontHeight];
+ } else {
+ editor.font = [NSFont controlContentFontOfSize:fontHeight];
+ }
+
+ origin.x -= 3; /* left padding; no way to get it from NSTextField */
+ origin.y -= [editor intrinsicContentSize].height;
+ origin.y += [editor baselineOffsetFromBottom];
+
+ [editor setFrameOrigin:origin];
+ [editor setStringValue:text];
+ [editor sizeToFit];
+
+ NSSize frameSize = [editor frame].size;
+ frameSize.width = std::max(frameSize.width, minWidth);
+ [editor setFrameSize:frameSize];
+
+ [[self window] makeFirstResponder:editor];
+ [[self window] makeKeyWindow];
+}
+
+- (void)stopEditing {
+ if(editing) {
+ [editor removeFromSuperview];
+ [[self window] makeFirstResponder:self];
+ editing = NO;
+ }
+}
+
+- (void)didEdit:(id)sender {
+ if(receiver->onEditingDone) {
+ receiver->onEditingDone([[editor stringValue] UTF8String]);
+ }
+}
+
+- (void)cancelOperation:(id)sender {
+ using Platform::KeyboardEvent;
+
+ if(receiver->onKeyboardEvent) {
+ KeyboardEvent event = {};
+ event.key = KeyboardEvent::Key::CHARACTER;
+ event.chr = '\e';
+ event.type = KeyboardEvent::Type::PRESS;
+ receiver->onKeyboardEvent(event);
+ event.type = KeyboardEvent::Type::RELEASE;
+ receiver->onKeyboardEvent(event);
+ }
+}
+
+@synthesize scrollerMin;
+@synthesize scrollerMax;
+
+- (void)didScroll:(NSScroller *)sender {
+ if(receiver->onScrollbarAdjusted) {
+ double pos = scrollerMin + [sender doubleValue] * (scrollerMax - scrollerMin);
+ receiver->onScrollbarAdjusted(pos);
+ }
+}
+@end
+
+@interface SSWindowDelegate : NSObject<NSWindowDelegate>
+@property Platform::Window *receiver;
+
+- (BOOL)windowShouldClose:(id)sender;
+
+@property(readonly, getter=isFullScreen) BOOL fullScreen;
+- (void)windowDidEnterFullScreen:(NSNotification *)notification;
+- (void)windowDidExitFullScreen:(NSNotification *)notification;
+@end
+
+@implementation SSWindowDelegate
+@synthesize receiver;
+
+- (BOOL)windowShouldClose:(id)sender {
+ if(receiver->onClose) {
+ receiver->onClose();
+ }
+ return NO;
+}
+
+@synthesize fullScreen;
+
+- (void)windowDidEnterFullScreen:(NSNotification *)notification {
+ fullScreen = true;
+ if(receiver->onFullScreen) {
+ receiver->onFullScreen(fullScreen);
+ }
+}
+
+- (void)windowDidExitFullScreen:(NSNotification *)notification {
+ fullScreen = false;
+ if(receiver->onFullScreen) {
+ receiver->onFullScreen(fullScreen);
+ }
+}
+@end
+
+namespace SolveSpace {
+namespace Platform {
+
+//-----------------------------------------------------------------------------
+// Windows
+//-----------------------------------------------------------------------------
+
+class WindowImplCocoa final : public Window {
+public:
+ NSWindow *nsWindow;
+ SSWindowDelegate *ssWindowDelegate;
+ SSView *ssView;
+ NSScroller *nsScroller;
+ NSView *nsContainer;
+
+ NSArray *nsConstraintsWithScrollbar;
+ NSArray *nsConstraintsWithoutScrollbar;
+
+ double minWidth = 100.0;
+ double minHeight = 100.0;
+
+ NSString *nsToolTip;
+
+ WindowImplCocoa(Window::Kind kind, std::shared_ptr<WindowImplCocoa> parentWindow) {
+ ssView = [[SSView alloc] init];
+ ssView.translatesAutoresizingMaskIntoConstraints = NO;
+ ssView.receiver = this;
+
+ nsScroller = [[NSScroller alloc] initWithFrame:NSMakeRect(0, 0, 0, 100)];
+ nsScroller.translatesAutoresizingMaskIntoConstraints = NO;
+ nsScroller.enabled = YES;
+ nsScroller.scrollerStyle = NSScrollerStyleOverlay;
+ nsScroller.knobStyle = NSScrollerKnobStyleLight;
+ nsScroller.action = @selector(didScroll:);
+ nsScroller.target = ssView;
+ nsScroller.continuous = YES;
+
+ nsContainer = [[NSView alloc] init];
+ [nsContainer addSubview:ssView];
+ [nsContainer addSubview:nsScroller];
+
+ NSDictionary *views = NSDictionaryOfVariableBindings(ssView, nsScroller);
+ nsConstraintsWithoutScrollbar = [NSLayoutConstraint
+ constraintsWithVisualFormat:@"H:|[ssView]|"
+ options:0 metrics:nil views:views];
+ [nsContainer addConstraints:nsConstraintsWithoutScrollbar];
+ nsConstraintsWithScrollbar = [NSLayoutConstraint
+ constraintsWithVisualFormat:@"H:|[ssView]-0-[nsScroller(11)]|"
+ options:0 metrics:nil views:views];
+ [nsContainer addConstraints:[NSLayoutConstraint
+ constraintsWithVisualFormat:@"V:|[ssView]|"
+ options:0 metrics:nil views:views]];
+ [nsContainer addConstraints:[NSLayoutConstraint
+ constraintsWithVisualFormat:@"V:|[nsScroller]|"
+ options:0 metrics:nil views:views]];
+
+ switch(kind) {
+ case Window::Kind::TOPLEVEL:
+ nsWindow = [[NSWindow alloc] init];
+ nsWindow.styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable |
+ NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
+ nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenPrimary;
+ ssView.acceptsFirstResponder = YES;
+ break;
+
+ case Window::Kind::TOOL:
+ NSPanel *nsPanel = [[NSPanel alloc] init];
+ nsPanel.styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable |
+ NSWindowStyleMaskClosable | NSWindowStyleMaskUtilityWindow;
+ [nsPanel standardWindowButton:NSWindowMiniaturizeButton].hidden = YES;
+ [nsPanel standardWindowButton:NSWindowZoomButton].hidden = YES;
+ nsPanel.floatingPanel = YES;
+ nsPanel.becomesKeyOnlyIfNeeded = YES;
+ nsWindow = nsPanel;
+ break;
+ }
+
+ ssWindowDelegate = [[SSWindowDelegate alloc] init];
+ ssWindowDelegate.receiver = this;
+ nsWindow.delegate = ssWindowDelegate;
+
+ nsWindow.backgroundColor = [NSColor blackColor];
+ nsWindow.contentView = nsContainer;
+ }
+
+ double GetPixelDensity() override {
+ NSDictionary *description = nsWindow.screen.deviceDescription;
+ NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
+ CGSize displayPhysicalSize = CGDisplayScreenSize(
+ [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
+ return (displayPixelSize.width / displayPhysicalSize.width) * 25.4f;
+ }
+
+ int GetDevicePixelRatio() override {
+ NSSize unitSize = { 1.0f, 0.0f };
+ unitSize = [ssView convertSizeToBacking:unitSize];
+ return (int)unitSize.width;
+ }
+
+ bool IsVisible() override {
+ return ![nsWindow isVisible];
+ }
+
+ void SetVisible(bool visible) override {
+ if(visible) {
+ [nsWindow orderFront:nil];
+ } else {
+ [nsWindow close];
+ }
+ }
+
+ void Focus() override {
+ [nsWindow makeKeyAndOrderFront:nil];
+ }
+
+ bool IsFullScreen() override {
+ return ssWindowDelegate.fullScreen;
+ }
+
+ void SetFullScreen(bool fullScreen) override {
+ if(fullScreen != IsFullScreen()) {
+ [nsWindow toggleFullScreen:nil];
+ }
+ }
+
+ void SetTitle(const std::string &title) override {
+ nsWindow.representedFilename = @"";
+ nsWindow.title = Wrap(title);
+ }
+
+ bool SetTitleForFilename(const Path &filename) override {
+ [nsWindow setTitleWithRepresentedFilename:Wrap(filename.raw)];
+ return true;
+ }
+
+ void SetMenuBar(MenuBarRef newMenuBar) override {
+ // Doesn't do anything, since we have an unique global menu bar.
+ }
+
+ void GetContentSize(double *width, double *height) override {
+ NSSize nsSize = ssView.frame.size;
+ *width = nsSize.width;
+ *height = nsSize.height;
+ }
+
+ void SetMinContentSize(double width, double height) override {
+ NSSize nsMinSize;
+ nsMinSize.width = width;
+ nsMinSize.height = height;
+ [nsWindow setContentMinSize:nsMinSize];
+ [nsWindow setContentSize:nsMinSize];
+ }
+
+ void FreezePosition(SettingsRef _settings, const std::string &key) override {
+ [nsWindow saveFrameUsingName:Wrap(key)];
+ }
+
+ void ThawPosition(SettingsRef _settings, const std::string &key) override {
+ [nsWindow setFrameUsingName:Wrap(key)];
+ }
+
+ void SetCursor(Cursor cursor) override {
+ NSCursor *nsNewCursor;
+ switch(cursor) {
+ case Cursor::POINTER: nsNewCursor = [NSCursor arrowCursor]; break;
+ case Cursor::HAND: nsNewCursor = [NSCursor pointingHandCursor]; break;
+ }
+
+ if([NSCursor currentCursor] != nsNewCursor) {
+ [nsNewCursor set];
+ }
+ }
+
+ void SetTooltip(const std::string &newText, double x, double y, double w, double h) override {
+ NSString *nsNewText = Wrap(newText);
+ if(![nsToolTip isEqualToString:nsNewText]) {
+ nsToolTip = nsNewText;
+
+ NSToolTipManager *nsToolTipManager = [NSToolTipManager sharedToolTipManager];
+ if(newText.empty()) {
+ if ([nsToolTipManager respondsToSelector:@selector(abortToolTip)]) {
+ [nsToolTipManager abortToolTip];
+ } else {
+ [nsToolTipManager orderOutToolTip];
+ }
+ } else {
+ [nsToolTipManager _displayTemporaryToolTipForView:ssView withString:Wrap(newText)];
+ }
+ }
+ }
+
+ bool IsEditorVisible() override {
+ return [ssView isEditing];
+ }
+
+ void ShowEditor(double x, double y, double fontHeight, double minWidth,
+ bool isMonospace, const std::string &text) override {
+ [ssView startEditing:Wrap(text) at:(NSPoint){(CGFloat)x, (CGFloat)y}
+ withHeight:fontHeight minWidth:minWidth usingMonospace:isMonospace];
+ }
+
+ void HideEditor() override {
+ [ssView stopEditing];
+ }
+
+ void SetScrollbarVisible(bool visible) override {
+ if(visible) {
+ [nsContainer removeConstraints:nsConstraintsWithoutScrollbar];
+ [nsContainer addConstraints:nsConstraintsWithScrollbar];
+ } else {
+ [nsContainer removeConstraints:nsConstraintsWithScrollbar];
+ [nsContainer addConstraints:nsConstraintsWithoutScrollbar];
+ }
+ }
+
+ void ConfigureScrollbar(double min, double max, double pageSize) override {
+ ssView.scrollerMin = min;
+ ssView.scrollerMax = max - pageSize;
+ [nsScroller setKnobProportion:(pageSize / (ssView.scrollerMax - ssView.scrollerMin))];
+ }
+
+ double GetScrollbarPosition() override {
+ return ssView.scrollerMin +
+ [nsScroller doubleValue] * (ssView.scrollerMax - ssView.scrollerMin);
+ }
+
+ void SetScrollbarPosition(double pos) override {
+ if(pos > ssView.scrollerMax)
+ pos = ssView.scrollerMax;
+ if(GetScrollbarPosition() == pos)
+ return;
+ [nsScroller setDoubleValue:(pos / (ssView.scrollerMax - ssView.scrollerMin))];
+ if(onScrollbarAdjusted) {
+ onScrollbarAdjusted(pos);
+ }
+ }
+
+ void Invalidate() override {
+ ssView.needsDisplay = YES;
+ }
+};
+
+WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) {
+ return std::make_shared<WindowImplCocoa>(kind,
+ std::static_pointer_cast<WindowImplCocoa>(parentWindow));
+}
+
+//-----------------------------------------------------------------------------
+// 3DConnexion support
+//-----------------------------------------------------------------------------
+
+// Normally we would just link to the 3DconnexionClient framework.
+//
+// We don't want to (are not allowed to) distribute the official framework, so we're trying
+// to use the one installed on the users' computer. There are some different versions of
+// the framework, the official one and re-implementations using an open source driver for
+// older devices (spacenav-plus). So weak-linking isn't an option, either. The only remaining
+// way is using CFBundle to dynamically load the library at runtime, and also detect its
+// availability.
+//
+// We're also defining everything needed from the 3DconnexionClientAPI, so we're not depending
+// on the API headers.
+
+#pragma pack(push,2)
+
+enum {
+ kConnexionClientModeTakeOver = 1,
+ kConnexionClientModePlugin = 2
+};
+
+#define kConnexionMsgDeviceState '3dSR'
+#define kConnexionMaskButtons 0x00FF
+#define kConnexionMaskAxis 0x3F00
+
+typedef struct {
+ uint16_t version;
+ uint16_t client;
+ uint16_t command;
+ int16_t param;
+ int32_t value;
+ UInt64 time;
+ uint8_t report[8];
+ uint16_t buttons8;
+ int16_t axis[6];
+ uint16_t address;
+ uint32_t buttons;
+} ConnexionDeviceState, *ConnexionDeviceStatePtr;
+
+#pragma pack(pop)
+
+typedef void (*ConnexionAddedHandlerProc)(io_connect_t);
+typedef void (*ConnexionRemovedHandlerProc)(io_connect_t);
+typedef void (*ConnexionMessageHandlerProc)(io_connect_t, natural_t, void *);
+
+typedef OSErr (*InstallConnexionHandlersProc)(ConnexionMessageHandlerProc, ConnexionAddedHandlerProc, ConnexionRemovedHandlerProc);
+typedef void (*CleanupConnexionHandlersProc)(void);
+typedef UInt16 (*RegisterConnexionClientProc)(UInt32, UInt8 *, UInt16, UInt32);
+typedef void (*UnregisterConnexionClientProc)(UInt16);
+
+static CFBundleRef spaceBundle = nil;
+static InstallConnexionHandlersProc installConnexionHandlers = NULL;
+static CleanupConnexionHandlersProc cleanupConnexionHandlers = NULL;
+static RegisterConnexionClientProc registerConnexionClient = NULL;
+static UnregisterConnexionClientProc unregisterConnexionClient = NULL;
+static UInt32 connexionSignature = 'SoSp';
+static UInt8 *connexionName = (UInt8 *)"\x10SolveSpace";
+static UInt16 connexionClient = 0;
+
+static std::vector<std::weak_ptr<Window>> connexionWindows;
+static bool connexionShiftIsDown = false;
+static bool connexionCommandIsDown = false;
+
+static void ConnexionAdded(io_connect_t con) {}
+static void ConnexionRemoved(io_connect_t con) {}
+static void ConnexionMessage(io_connect_t con, natural_t type, void *arg) {
+ if (type != kConnexionMsgDeviceState) {
+ return;
+ }
+
+ ConnexionDeviceState *device = (ConnexionDeviceState *)arg;
+
+ dispatch_async(dispatch_get_main_queue(), ^(void){
+ SixDofEvent event = {};
+ event.type = SixDofEvent::Type::MOTION;
+ event.translationX = (double)device->axis[0] * -0.25;
+ event.translationY = (double)device->axis[1] * -0.25;
+ event.translationZ = (double)device->axis[2] * 0.25;
+ event.rotationX = (double)device->axis[3] * -0.0005;
+ event.rotationY = (double)device->axis[4] * -0.0005;
+ event.rotationZ = (double)device->axis[5] * -0.0005;
+ event.shiftDown = connexionShiftIsDown;
+ event.controlDown = connexionCommandIsDown;
+
+ for(auto window : connexionWindows) {
+ if(auto windowStrong = window.lock()) {
+ if(windowStrong->onSixDofEvent) {
+ windowStrong->onSixDofEvent(event);
+ }
+ }
+ }
+ });
+}
+
+void Open3DConnexion() {
+ NSString *bundlePath = @"/Library/Frameworks/3DconnexionClient.framework";
+ NSURL *bundleURL = [NSURL fileURLWithPath:bundlePath];
+ spaceBundle = CFBundleCreate(kCFAllocatorDefault, (__bridge CFURLRef)bundleURL);
+
+ // Don't continue if no driver is installed on this machine
+ if(spaceBundle == nil) {
+ return;
+ }
+
+ installConnexionHandlers = (InstallConnexionHandlersProc)
+ CFBundleGetFunctionPointerForName(spaceBundle,
+ CFSTR("InstallConnexionHandlers"));
+
+ cleanupConnexionHandlers = (CleanupConnexionHandlersProc)
+ CFBundleGetFunctionPointerForName(spaceBundle,
+ CFSTR("CleanupConnexionHandlers"));
+
+ registerConnexionClient = (RegisterConnexionClientProc)
+ CFBundleGetFunctionPointerForName(spaceBundle,
+ CFSTR("RegisterConnexionClient"));
+
+ unregisterConnexionClient = (UnregisterConnexionClientProc)
+ CFBundleGetFunctionPointerForName(spaceBundle,
+ CFSTR("UnregisterConnexionClient"));
+
+ // Only continue if all required symbols have been loaded
+ if((installConnexionHandlers == NULL) || (cleanupConnexionHandlers == NULL)
+ || (registerConnexionClient == NULL) || (unregisterConnexionClient == NULL)) {
+ CFRelease(spaceBundle);
+ spaceBundle = nil;
+ return;
+ }
+
+ installConnexionHandlers(&ConnexionMessage, &ConnexionAdded, &ConnexionRemoved);
+ connexionClient = registerConnexionClient(connexionSignature, connexionName,
+ kConnexionClientModeTakeOver, kConnexionMaskButtons | kConnexionMaskAxis);
+
+ [NSEvent addLocalMonitorForEventsMatchingMask:(NSEventMaskKeyDown | NSEventMaskFlagsChanged)
+ handler:^(NSEvent *event) {
+ connexionShiftIsDown = (event.modifierFlags & NSEventModifierFlagShift);
+ connexionCommandIsDown = (event.modifierFlags & NSEventModifierFlagCommand);
+ return event;
+ }];
+
+ [NSEvent addLocalMonitorForEventsMatchingMask:(NSEventMaskKeyUp | NSEventMaskFlagsChanged)
+ handler:^(NSEvent *event) {
+ connexionShiftIsDown = (event.modifierFlags & NSEventModifierFlagShift);
+ connexionCommandIsDown = (event.modifierFlags & NSEventModifierFlagCommand);
+ return event;
+ }];
+}
+
+void Close3DConnexion() {
+ if(spaceBundle == nil) {
+ return;
+ }
+
+ unregisterConnexionClient(connexionClient);
+ cleanupConnexionHandlers();
+
+ CFRelease(spaceBundle);
+}
+
+void Request3DConnexionEventsForWindow(WindowRef window) {
+ connexionWindows.push_back(window);
+}
+
+//-----------------------------------------------------------------------------
+// Message dialogs
+//-----------------------------------------------------------------------------
+
+class MessageDialogImplCocoa final : public MessageDialog {
+public:
+ NSAlert *nsAlert = [[NSAlert alloc] init];
+ NSWindow *nsWindow;
+
+ std::vector<Response> responses;
+
+ void SetType(Type type) override {
+ switch(type) {
+ case Type::INFORMATION:
+ case Type::QUESTION:
+ nsAlert.alertStyle = NSAlertStyleInformational;
+ break;
+
+ case Type::WARNING:
+ case Type::ERROR:
+ nsAlert.alertStyle = NSAlertStyleWarning;
+ break;
+ }
+ }
+
+ void SetTitle(std::string title) override {
+ [nsAlert.window setTitle:Wrap(title)];
+ }
+
+ void SetMessage(std::string message) override {
+ nsAlert.messageText = Wrap(message);
+ }
+
+ void SetDescription(std::string description) override {
+ nsAlert.informativeText = Wrap(description);
+ }
+
+ void AddButton(std::string label, Response response, bool isDefault) override {
+ NSButton *nsButton = [nsAlert addButtonWithTitle:Wrap(PrepareMnemonics(label))];
+ if(!isDefault && [nsButton.keyEquivalent isEqualToString:@"\n"]) {
+ nsButton.keyEquivalent = @"";
+ } else if(response == Response::CANCEL) {
+ nsButton.keyEquivalent = @"\e";
+ }
+ responses.push_back(response);
+ }
+
+ Response RunModal() override {
+ // FIXME(platform/gui): figure out a way to run the alert as a sheet
+ NSModalResponse nsResponse = [nsAlert runModal];
+ ssassert(nsResponse >= NSAlertFirstButtonReturn &&
+ nsResponse <= NSAlertFirstButtonReturn + (long)responses.size(),
+ "Unexpected response");
+ return responses[nsResponse - NSAlertFirstButtonReturn];
+ }
+};
+
+MessageDialogRef CreateMessageDialog(WindowRef parentWindow) {
+ std::shared_ptr<MessageDialogImplCocoa> dialog = std::make_shared<MessageDialogImplCocoa>();
+ dialog->nsWindow = std::static_pointer_cast<WindowImplCocoa>(parentWindow)->nsWindow;
+ return dialog;
+}
+
+//-----------------------------------------------------------------------------
+// File dialogs
+//-----------------------------------------------------------------------------
+
+}
+}
+
+@interface SSSaveFormatAccessory : NSViewController
+@property NSSavePanel *panel;
+@property NSMutableArray *filters;
+
+@property(nonatomic) NSInteger index;
+@property(nonatomic) IBOutlet NSTextField *textField;
+@property(nonatomic) IBOutlet NSPopUpButton *button;
+@end
+
+@implementation SSSaveFormatAccessory
+@synthesize panel, filters, button;
+
+- (void)setIndex:(NSInteger)newIndex {
+ self->_index = newIndex;
+ NSMutableArray *filter = [filters objectAtIndex:newIndex];
+ NSString *extension = [filter objectAtIndex:0];
+ if(![extension isEqual:@"*"]) {
+ NSString *filename = panel.nameFieldStringValue;
+ NSString *basename = [[filename componentsSeparatedByString:@"."] objectAtIndex:0];
+ panel.nameFieldStringValue = [basename stringByAppendingPathExtension:extension];
+ }
+ [panel setAllowedFileTypes:filter];
+}
+@end
+
+namespace SolveSpace {
+namespace Platform {
+
+class FileDialogImplCocoa : public FileDialog {
+public:
+ NSSavePanel *nsPanel = nil;
+
+ void SetTitle(std::string title) override {
+ nsPanel.title = Wrap(title);
+ }
+
+ void SetCurrentName(std::string name) override {
+ nsPanel.nameFieldStringValue = Wrap(name);
+ }
+
+ Platform::Path GetFilename() override {
+ return Platform::Path::From(nsPanel.URL.fileSystemRepresentation);
+ }
+
+ void SetFilename(Platform::Path path) override {
+ nsPanel.directoryURL =
+ [NSURL fileURLWithPath:Wrap(path.Parent().raw) isDirectory:YES];
+ nsPanel.nameFieldStringValue = Wrap(path.FileStem());
+ }
+
+ void FreezeChoices(SettingsRef settings, const std::string &key) override {
+ settings->FreezeString("Dialog_" + key + "_Folder",
+ [nsPanel.directoryURL.absoluteString UTF8String]);
+ }
+
+ void ThawChoices(SettingsRef settings, const std::string &key) override {
+ nsPanel.directoryURL =
+ [NSURL URLWithString:Wrap(settings->ThawString("Dialog_" + key + "_Folder", ""))];
+ }
+
+ bool RunModal() override {
+ if([nsPanel runModal] == NSModalResponseOK) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+};
+
+class OpenFileDialogImplCocoa final : public FileDialogImplCocoa {
+public:
+ NSMutableArray *nsFilter = [[NSMutableArray alloc] init];
+
+ OpenFileDialogImplCocoa() {
+ SetTitle(C_("title", "Open File"));
+ }
+
+ void AddFilter(std::string name, std::vector<std::string> extensions) override {
+ for(auto extension : extensions) {
+ [nsFilter addObject:Wrap(extension)];
+ }
+ [nsPanel setAllowedFileTypes:nsFilter];
+ }
+};
+
+class SaveFileDialogImplCocoa final : public FileDialogImplCocoa {
+public:
+ NSMutableArray *nsFilters = [[NSMutableArray alloc] init];
+ SSSaveFormatAccessory *ssAccessory = nil;
+
+ SaveFileDialogImplCocoa() {
+ SetTitle(C_("title", "Save File"));
+ }
+
+ void AddFilter(std::string name, std::vector<std::string> extensions) override {
+ NSMutableArray *nsFilter = [[NSMutableArray alloc] init];
+ for(auto extension : extensions) {
+ [nsFilter addObject:Wrap(extension)];
+ }
+ if(nsFilters.count == 0) {
+ [nsPanel setAllowedFileTypes:nsFilter];
+ }
+ [nsFilters addObject:nsFilter];
+
+ std::string desc;
+ for(auto extension : extensions) {
+ if(!desc.empty()) desc += ", ";
+ desc += extension;
+ }
+ std::string title = name + " (" + desc + ")";
+ if(nsFilters.count == 1) {
+ [ssAccessory.button removeAllItems];
+ }
+ [ssAccessory.button addItemWithTitle:Wrap(title)];
+ [ssAccessory.button synchronizeTitleAndSelectedItem];
+ }
+
+ void FreezeChoices(SettingsRef settings, const std::string &key) override {
+ FileDialogImplCocoa::FreezeChoices(settings, key);
+ settings->FreezeInt("Dialog_" + key + "_Filter", ssAccessory.index);
+ }
+
+ void ThawChoices(SettingsRef settings, const std::string &key) override {
+ FileDialogImplCocoa::ThawChoices(settings, key);
+ ssAccessory.index = settings->ThawInt("Dialog_" + key + "_Filter", 0);
+ }
+
+ bool RunModal() override {
+ if(nsFilters.count == 1) {
+ nsPanel.accessoryView = nil;
+ }
+
+ if(nsPanel.nameFieldStringValue.length == 0) {
+ nsPanel.nameFieldStringValue = Wrap(_("untitled"));
+ }
+
+ return FileDialogImplCocoa::RunModal();
+ }
+};
+
+FileDialogRef CreateOpenFileDialog(WindowRef parentWindow) {
+ NSOpenPanel *nsPanel = [NSOpenPanel openPanel];
+ nsPanel.canSelectHiddenExtension = YES;
+
+ std::shared_ptr<OpenFileDialogImplCocoa> dialog = std::make_shared<OpenFileDialogImplCocoa>();
+ dialog->nsPanel = nsPanel;
+
+ return dialog;
+}
+
+FileDialogRef CreateSaveFileDialog(WindowRef parentWindow) {
+ NSSavePanel *nsPanel = [NSSavePanel savePanel];
+ nsPanel.canSelectHiddenExtension = YES;
+
+ SSSaveFormatAccessory *ssAccessory =
+ [[SSSaveFormatAccessory alloc] initWithNibName:@"SaveFormatAccessory" bundle:nil];
+ ssAccessory.panel = nsPanel;
+ nsPanel.accessoryView = [ssAccessory view];
+
+ std::shared_ptr<SaveFileDialogImplCocoa> dialog = std::make_shared<SaveFileDialogImplCocoa>();
+ dialog->nsPanel = nsPanel;
+ dialog->ssAccessory = ssAccessory;
+ ssAccessory.filters = dialog->nsFilters;
+
+ return dialog;
+}
+
+//-----------------------------------------------------------------------------
+// Application-wide APIs
+//-----------------------------------------------------------------------------
+
+std::vector<Platform::Path> GetFontFiles() {
+ std::vector<SolveSpace::Platform::Path> fonts;
+
+ NSArray *fontNames = [[NSFontManager sharedFontManager] availableFonts];
+ for(NSString *fontName in fontNames) {
+ CTFontDescriptorRef fontRef =
+ CTFontDescriptorCreateWithNameAndSize ((__bridge CFStringRef)fontName, 10.0);
+ CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(fontRef, kCTFontURLAttribute);
+ NSString *fontPath = [NSString stringWithString:[(NSURL *)CFBridgingRelease(url) path]];
+ fonts.push_back(
+ Platform::Path::From([[NSFileManager defaultManager]
+ fileSystemRepresentationWithPath:fontPath]));
+ }
+
+ return fonts;
+}
+
+void OpenInBrowser(const std::string &url) {
+ [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:Wrap(url)]];
+}
+
+}
+}
+
+@interface SSApplicationDelegate : NSObject<NSApplicationDelegate>
+- (IBAction)preferences:(id)sender;
+- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
+@end
+
+@implementation SSApplicationDelegate
+- (IBAction)preferences:(id)sender {
+ if (!SS.GW.showTextWindow) {
+ SolveSpace::SS.GW.MenuView(SolveSpace::Command::SHOW_TEXT_WND);
+ }
+ SolveSpace::SS.TW.GoToScreen(SolveSpace::TextWindow::Screen::CONFIGURATION);
+ SolveSpace::SS.ScheduleShowTW();
+}
+
+- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
+ SolveSpace::Platform::Path path = SolveSpace::Platform::Path::From([filename UTF8String]);
+ return SolveSpace::SS.Load(path.Expand(/*fromCurrentDirectory=*/true));
+}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
+ [[[NSApp mainWindow] delegate] windowShouldClose:[NSApp mainWindow]];
+ return NSTerminateCancel;
+}
+
+- (void)applicationTerminatePrompt {
+ SolveSpace::SS.MenuFile(SolveSpace::Command::EXIT);
+}
+@end
+
+namespace SolveSpace {
+namespace Platform {
+
+static SSApplicationDelegate *ssDelegate;
+
+std::vector<std::string> InitGui(int argc, char **argv) {
+ std::vector<std::string> args = InitCli(argc, argv);
+ if(args.size() >= 2 && args[1].find("-psn_") == 0) {
+ // For unknown reasons, Finder passes a Carbon PSN (Process Serial Number) argument
+ // when a freshly downloaded application is run for the first time. Remove it so
+ // that it isn't interpreted as a filename.
+ args.erase(args.begin() + 1);
+ }
+
+ ssDelegate = [[SSApplicationDelegate alloc] init];
+ NSApplication.sharedApplication.delegate = ssDelegate;
+
+ [NSBundle.mainBundle loadNibNamed:@"MainMenu" owner:nil topLevelObjects:nil];
+
+ NSArray *languages = NSLocale.preferredLanguages;
+ for(NSString *language in languages) {
+ if(SolveSpace::SetLocale([language UTF8String])) break;
+ }
+ if(languages.count == 0) {
+ SolveSpace::SetLocale("en_US");
+ }
+
+ return args;
+}
+
+void RunGui() {
+ [NSApp run];
+}
+
+void ExitGui() {
+ [NSApp setDelegate:nil];
+ [NSApp terminate:nil];
+}
+
+void ClearGui() {}
+
+}
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Our platform support functions for the headless (no OpenGL) test runner.
+//
+// Copyright 2016 whitequark
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+#include <cairo.h>
+
+namespace SolveSpace {
+
+//-----------------------------------------------------------------------------
+// Rendering
+//-----------------------------------------------------------------------------
+
+std::shared_ptr<ViewportCanvas> CreateRenderer() {
+ return std::make_shared<CairoPixmapRenderer>();
+}
+
+namespace Platform {
+
+//-----------------------------------------------------------------------------
+// Fatal errors
+//-----------------------------------------------------------------------------
+
+void FatalError(const std::string &message) {
+ fprintf(stderr, "%s", message.c_str());
+ abort();
+}
+
+//-----------------------------------------------------------------------------
+// Settings
+//-----------------------------------------------------------------------------
+
+class SettingsImplDummy final : public Settings {
+public:
+ void FreezeInt(const std::string &key, uint32_t value) override {
+ }
+
+ uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) override {
+ return defaultValue;
+ }
+
+ void FreezeFloat(const std::string &key, double value) override {
+ }
+
+ double ThawFloat(const std::string &key, double defaultValue = 0.0) override {
+ return defaultValue;
+ }
+
+ void FreezeString(const std::string &key, const std::string &value) override {
+ }
+
+ std::string ThawString(const std::string &key, const std::string &defaultValue = "") override {
+ return defaultValue;
+ }
+};
+
+SettingsRef GetSettings() {
+ static std::shared_ptr<SettingsImplDummy> settings =
+ std::make_shared<SettingsImplDummy>();
+ return settings;
+}
+
+//-----------------------------------------------------------------------------
+// Timers
+//-----------------------------------------------------------------------------
+
+class TimerImplDummy final : public Timer {
+public:
+ void RunAfter(unsigned milliseconds) override {}
+};
+
+TimerRef CreateTimer() {
+ return std::make_shared<TimerImplDummy>();
+}
+
+//-----------------------------------------------------------------------------
+// Menus
+//-----------------------------------------------------------------------------
+
+MenuRef CreateMenu() {
+ return std::shared_ptr<Menu>();
+}
+
+MenuBarRef GetOrCreateMainMenu(bool *unique) {
+ *unique = false;
+ return std::shared_ptr<MenuBar>();
+}
+
+//-----------------------------------------------------------------------------
+// Windows
+//-----------------------------------------------------------------------------
+
+WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) {
+ return std::shared_ptr<Window>();
+}
+
+void Request3DConnexionEventsForWindow(WindowRef window) {}
+
+//-----------------------------------------------------------------------------
+// Message dialogs
+//-----------------------------------------------------------------------------
+
+MessageDialogRef CreateMessageDialog(WindowRef parentWindow) {
+ return std::shared_ptr<MessageDialog>();
+}
+
+//-----------------------------------------------------------------------------
+// File dialogs
+//-----------------------------------------------------------------------------
+
+FileDialogRef CreateOpenFileDialog(WindowRef parentWindow) {
+ return std::shared_ptr<FileDialog>();
+}
+
+FileDialogRef CreateSaveFileDialog(WindowRef parentWindow) {
+ return std::shared_ptr<FileDialog>();
+}
+
+//-----------------------------------------------------------------------------
+// Application-wide APIs
+//-----------------------------------------------------------------------------
+
+std::vector<Platform::Path> fontFiles;
+std::vector<Platform::Path> GetFontFiles() {
+ return fontFiles;
+}
+
+void OpenInBrowser(const std::string &url) {}
+
+std::vector<std::string> InitGui(int argc, char **argv) {
+ return {};
+}
+
+void RunGui() {}
+
+void ExitGui() {
+ exit(0);
+}
+
+void ClearGui() {}
+
+}
+
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// The Win32-based implementation of platform-dependent GUI functionality.
+//
+// Copyright 2018 whitequark
+//-----------------------------------------------------------------------------
+#include "config.h"
+#include "solvespace.h"
+// Include after solvespace.h to avoid identifier clashes.
+#include <windows.h>
+#include <windowsx.h>
+#include <commctrl.h>
+#include <commdlg.h>
+#include <shellapi.h>
+
+// Macros to compile under XP
+#if !defined(LSTATUS)
+# define LSTATUS LONG
+#endif
+
+#if !defined(MAPVK_VK_TO_CHAR)
+# define MAPVK_VK_TO_CHAR 2
+#endif
+
+#if !defined(USER_DEFAULT_SCREEN_DPI)
+# define USER_DEFAULT_SCREEN_DPI 96
+#endif
+
+#if !defined(TTM_POPUP)
+# define TTM_POPUP (WM_USER + 34)
+#endif
+// End macros to compile under XP
+
+#if !defined(WM_DPICHANGED)
+# define WM_DPICHANGED 0x02E0
+#endif
+
+// These interfere with our identifiers.
+#undef CreateWindow
+#undef ERROR
+
+#if HAVE_OPENGL == 3
+# define EGLAPI /*static linkage*/
+# define EGL_EGLEXT_PROTOTYPES
+# include <EGL/egl.h>
+# include <EGL/eglext.h>
+#endif
+
+#if defined(HAVE_SPACEWARE)
+# include <si.h>
+# include <siapp.h>
+# undef uint32_t
+#endif
+
+#if defined(__GNUC__)
+// Disable bogus warning emitted by GCC on GetProcAddress, since there seems to be no way
+// of restructuring the code to easily disable it just at the call site.
+#pragma GCC diagnostic ignored "-Wcast-function-type"
+#endif
+
+namespace SolveSpace {
+namespace Platform {
+
+//-----------------------------------------------------------------------------
+// Windows API bridging
+//-----------------------------------------------------------------------------
+
+#define sscheck(expr) do { \
+ SetLastError(0); \
+ if(!(expr)) \
+ CheckLastError(__FILE__, __LINE__, __func__, #expr); \
+ } while(0)
+
+void CheckLastError(const char *file, int line, const char *function, const char *expr) {
+ if(GetLastError() != S_OK) {
+ LPWSTR messageW;
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPWSTR)&messageW, 0, NULL);
+
+ std::string message;
+ message += ssprintf("File %s, line %u, function %s:\n", file, line, function);
+ message += ssprintf("Win32 API call failed: %s.\n", expr);
+ message += ssprintf("Error: %s", Narrow(messageW).c_str());
+ FatalError(message);
+ }
+}
+
+typedef UINT (WINAPI *LPFNGETDPIFORWINDOW)(HWND);
+
+UINT ssGetDpiForWindow(HWND hwnd) {
+ static bool checked;
+ static LPFNGETDPIFORWINDOW lpfnGetDpiForWindow;
+ if(!checked) {
+ checked = true;
+ lpfnGetDpiForWindow = (LPFNGETDPIFORWINDOW)
+ GetProcAddress(GetModuleHandleW(L"user32.dll"), "GetDpiForWindow");
+ }
+ if(lpfnGetDpiForWindow) {
+ return lpfnGetDpiForWindow(hwnd);
+ } else {
+ HDC hDc;
+ sscheck(hDc = GetDC(HWND_DESKTOP));
+ UINT dpi;
+ sscheck(dpi = GetDeviceCaps(hDc, LOGPIXELSX));
+ sscheck(ReleaseDC(HWND_DESKTOP, hDc));
+ return dpi;
+ }
+}
+
+typedef BOOL (WINAPI *LPFNADJUSTWINDOWRECTEXFORDPI)(LPRECT, DWORD, BOOL, DWORD, UINT);
+
+BOOL ssAdjustWindowRectExForDpi(LPRECT lpRect, DWORD dwStyle, BOOL bMenu,
+ DWORD dwExStyle, UINT dpi) {
+ static bool checked;
+ static LPFNADJUSTWINDOWRECTEXFORDPI lpfnAdjustWindowRectExForDpi;
+ if(!checked) {
+ checked = true;
+ lpfnAdjustWindowRectExForDpi = (LPFNADJUSTWINDOWRECTEXFORDPI)
+ GetProcAddress(GetModuleHandleW(L"user32.dll"), "AdjustWindowRectExForDpi");
+ }
+ if(lpfnAdjustWindowRectExForDpi) {
+ return lpfnAdjustWindowRectExForDpi(lpRect, dwStyle, bMenu, dwExStyle, dpi);
+ } else {
+ return AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Utility functions
+//-----------------------------------------------------------------------------
+
+static std::wstring PrepareTitle(const std::string &s) {
+ return Widen("SolveSpace - " + s);
+}
+
+static std::string NegateMnemonics(const std::string &label) {
+ std::string newLabel;
+ for(char c : label) {
+ newLabel.push_back(c);
+ if(c == '&') newLabel.push_back(c);
+ }
+ return newLabel;
+}
+
+static int Clamp(int x, int a, int b) {
+ return max(a, min(x, b));
+}
+
+//-----------------------------------------------------------------------------
+// Fatal errors
+//-----------------------------------------------------------------------------
+
+bool handlingFatalError = false;
+
+void FatalError(const std::string &message) {
+ // Indicate that we're handling a fatal error, to avoid re-entering application code
+ // and potentially crashing even harder.
+ handlingFatalError = true;
+
+ switch(MessageBoxW(NULL, Platform::Widen(message + "\nGenerate debug report?").c_str(),
+ L"Fatal error — SolveSpace",
+ MB_ICONERROR|MB_TASKMODAL|MB_SETFOREGROUND|MB_TOPMOST|
+ MB_OKCANCEL|MB_DEFBUTTON2)) {
+ case IDOK:
+ abort();
+
+ case IDCANCEL:
+ default: {
+ WCHAR appPath[MAX_PATH] = {};
+ GetModuleFileNameW(NULL, appPath, sizeof(appPath));
+ ShellExecuteW(NULL, L"open", appPath, NULL, NULL, SW_SHOW);
+ _exit(1);
+ }
+ }
+}
+//-----------------------------------------------------------------------------
+// Settings
+//-----------------------------------------------------------------------------
+
+class SettingsImplWin32 final : public Settings {
+public:
+ HKEY hKey = NULL;
+
+ HKEY GetKey() {
+ if(hKey == NULL) {
+ sscheck(RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\SolveSpace", 0, NULL, 0,
+ KEY_ALL_ACCESS, NULL, &hKey, NULL));
+ }
+ return hKey;
+ }
+
+ ~SettingsImplWin32() {
+ if(hKey != NULL) {
+ sscheck(RegCloseKey(hKey));
+ }
+ }
+
+ void FreezeInt(const std::string &key, uint32_t value) {
+ sscheck(RegSetValueExW(GetKey(), &Widen(key)[0], 0,
+ REG_DWORD, (const BYTE *)&value, sizeof(value)));
+ }
+
+ uint32_t ThawInt(const std::string &key, uint32_t defaultValue) {
+ DWORD value;
+ DWORD type, length = sizeof(value);
+ LSTATUS result = RegQueryValueExW(GetKey(), &Widen(key)[0], 0,
+ &type, (BYTE *)&value, &length);
+ if(result == ERROR_SUCCESS && type == REG_DWORD) {
+ return value;
+ }
+ return defaultValue;
+ }
+
+ void FreezeFloat(const std::string &key, double value) {
+ sscheck(RegSetValueExW(GetKey(), &Widen(key)[0], 0,
+ REG_QWORD, (const BYTE *)&value, sizeof(value)));
+ }
+
+ double ThawFloat(const std::string &key, double defaultValue) {
+ double value;
+ DWORD type, length = sizeof(value);
+ LSTATUS result = RegQueryValueExW(GetKey(), &Widen(key)[0], 0,
+ &type, (BYTE *)&value, &length);
+ if(result == ERROR_SUCCESS && type == REG_QWORD) {
+ return value;
+ }
+ return defaultValue;
+ }
+
+ void FreezeString(const std::string &key, const std::string &value) {
+ ssassert(value.length() == strlen(value.c_str()),
+ "illegal null byte in middle of a string setting");
+ std::wstring valueW = Widen(value);
+ sscheck(RegSetValueExW(GetKey(), &Widen(key)[0], 0,
+ REG_SZ, (const BYTE *)&valueW[0], (valueW.length() + 1) * 2));
+ }
+
+ std::string ThawString(const std::string &key, const std::string &defaultValue) {
+ DWORD type, length = 0;
+ LSTATUS result = RegQueryValueExW(GetKey(), &Widen(key)[0], 0,
+ &type, NULL, &length);
+ if(result == ERROR_SUCCESS && type == REG_SZ) {
+ std::wstring valueW;
+ valueW.resize(length / 2 - 1);
+ sscheck(RegQueryValueExW(GetKey(), &Widen(key)[0], 0,
+ &type, (BYTE *)&valueW[0], &length));
+ return Narrow(valueW);
+ }
+ return defaultValue;
+ }
+};
+
+SettingsRef GetSettings() {
+ return std::make_shared<SettingsImplWin32>();
+}
+
+//-----------------------------------------------------------------------------
+// Timers
+//-----------------------------------------------------------------------------
+
+class TimerImplWin32 final : public Timer {
+public:
+ static HWND WindowHandle() {
+ static HWND hTimerWnd;
+ if(hTimerWnd == NULL) {
+ sscheck(hTimerWnd = CreateWindowExW(0, L"Message", NULL, 0, 0, 0, 0, 0,
+ HWND_MESSAGE, NULL, NULL, NULL));
+ }
+ return hTimerWnd;
+ }
+
+ static void CALLBACK TimerFunc(HWND hwnd, UINT msg, UINT_PTR event, DWORD time) {
+ sscheck(KillTimer(WindowHandle(), event));
+
+ TimerImplWin32 *timer = (TimerImplWin32*)event;
+ if(timer->onTimeout) {
+ timer->onTimeout();
+ }
+ }
+
+ void RunAfter(unsigned milliseconds) override {
+ // FIXME(platform/gui): use SetCoalescableTimer when it's available (8+)
+ sscheck(SetTimer(WindowHandle(), (UINT_PTR)this,
+ milliseconds, &TimerImplWin32::TimerFunc));
+ }
+
+ ~TimerImplWin32() {
+ // FIXME(platform/gui): there's a race condition here--WM_TIMER messages already
+ // posted to the queue are not removed, so this destructor is at most "best effort".
+ KillTimer(WindowHandle(), (UINT_PTR)this);
+ }
+};
+
+TimerRef CreateTimer() {
+ return std::make_shared<TimerImplWin32>();
+}
+
+//-----------------------------------------------------------------------------
+// Menus
+//-----------------------------------------------------------------------------
+
+class MenuImplWin32;
+
+class MenuItemImplWin32 final : public MenuItem {
+public:
+ std::shared_ptr<MenuImplWin32> menu;
+
+ HMENU Handle();
+
+ MENUITEMINFOW GetInfo(UINT mask) {
+ MENUITEMINFOW mii = {};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = mask;
+ sscheck(GetMenuItemInfoW(Handle(), (UINT_PTR)this, FALSE, &mii));
+ return mii;
+ }
+
+ void SetAccelerator(KeyboardEvent accel) override {
+ MENUITEMINFOW mii = GetInfo(MIIM_TYPE);
+
+ std::wstring nameW(mii.cch, L'\0');
+ mii.dwTypeData = &nameW[0];
+ mii.cch++;
+ sscheck(GetMenuItemInfoW(Handle(), (UINT_PTR)this, FALSE, &mii));
+
+ std::string name = Narrow(nameW);
+ if(name.find('\t') != std::string::npos) {
+ name = name.substr(0, name.find('\t'));
+ }
+ name += '\t';
+ name += AcceleratorDescription(accel);
+
+ nameW = Widen(name);
+ mii.fMask = MIIM_STRING;
+ mii.dwTypeData = &nameW[0];
+ sscheck(SetMenuItemInfoW(Handle(), (UINT_PTR)this, FALSE, &mii));
+ }
+
+ void SetIndicator(Indicator type) override {
+ MENUITEMINFOW mii = GetInfo(MIIM_FTYPE);
+ switch(type) {
+ case Indicator::NONE:
+ case Indicator::CHECK_MARK:
+ mii.fType &= ~MFT_RADIOCHECK;
+ break;
+
+ case Indicator::RADIO_MARK:
+ mii.fType |= MFT_RADIOCHECK;
+ break;
+ }
+ sscheck(SetMenuItemInfoW(Handle(), (UINT_PTR)this, FALSE, &mii));
+ }
+
+ void SetActive(bool active) override {
+ MENUITEMINFOW mii = GetInfo(MIIM_STATE);
+ if(active) {
+ mii.fState |= MFS_CHECKED;
+ } else {
+ mii.fState &= ~MFS_CHECKED;
+ }
+ sscheck(SetMenuItemInfoW(Handle(), (UINT_PTR)this, FALSE, &mii));
+ }
+
+ void SetEnabled(bool enabled) override {
+ MENUITEMINFOW mii = GetInfo(MIIM_STATE);
+ if(enabled) {
+ mii.fState &= ~(MFS_DISABLED|MFS_GRAYED);
+ } else {
+ mii.fState |= MFS_DISABLED|MFS_GRAYED;
+ }
+ sscheck(SetMenuItemInfoW(Handle(), (UINT_PTR)this, FALSE, &mii));
+ }
+};
+
+int64_t contextMenuPopTime = 0;
+
+class MenuImplWin32 final : public Menu {
+public:
+ HMENU hMenu;
+
+ std::weak_ptr<MenuImplWin32> weakThis;
+ std::vector<std::shared_ptr<MenuItemImplWin32>> menuItems;
+ std::vector<std::shared_ptr<MenuImplWin32>> subMenus;
+
+ MenuImplWin32() {
+ sscheck(hMenu = CreatePopupMenu());
+ }
+
+ MenuItemRef AddItem(const std::string &label,
+ std::function<void()> onTrigger = NULL,
+ bool mnemonics = true) override {
+ auto menuItem = std::make_shared<MenuItemImplWin32>();
+ menuItem->menu = weakThis.lock();
+ menuItem->onTrigger = onTrigger;
+ menuItems.push_back(menuItem);
+
+ sscheck(AppendMenuW(hMenu, MF_STRING, (UINT_PTR)menuItem.get(),
+ Widen(mnemonics ? label : NegateMnemonics(label)).c_str()));
+
+ // uID is just an UINT, which isn't large enough to hold a pointer on 64-bit Windows,
+ // so we use dwItemData, which is.
+ MENUITEMINFOW mii = {};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_DATA;
+ mii.dwItemData = (LONG_PTR)menuItem.get();
+ sscheck(SetMenuItemInfoW(hMenu, (UINT_PTR)menuItem.get(), FALSE, &mii));
+
+ return menuItem;
+ }
+
+ MenuRef AddSubMenu(const std::string &label) override {
+ auto subMenu = std::make_shared<MenuImplWin32>();
+ subMenu->weakThis = subMenu;
+ subMenus.push_back(subMenu);
+
+ sscheck(AppendMenuW(hMenu, MF_STRING|MF_POPUP,
+ (UINT_PTR)subMenu->hMenu, Widen(label).c_str()));
+
+ return subMenu;
+ }
+
+ void AddSeparator() override {
+ sscheck(AppendMenuW(hMenu, MF_SEPARATOR, 0, L""));
+ }
+
+ void PopUp() override {
+ MENUINFO mi = {};
+ mi.cbSize = sizeof(mi);
+ mi.fMask = MIM_APPLYTOSUBMENUS|MIM_STYLE;
+ mi.dwStyle = MNS_NOTIFYBYPOS;
+ sscheck(SetMenuInfo(hMenu, &mi));
+
+ POINT pt;
+ sscheck(GetCursorPos(&pt));
+
+ sscheck(TrackPopupMenu(hMenu, TPM_TOPALIGN, pt.x, pt.y, 0, GetActiveWindow(), NULL));
+ contextMenuPopTime = GetMilliseconds();
+ }
+
+ void Clear() override {
+ for(int n = GetMenuItemCount(hMenu) - 1; n >= 0; n--) {
+ sscheck(RemoveMenu(hMenu, n, MF_BYPOSITION));
+ }
+ menuItems.clear();
+ subMenus.clear();
+ }
+
+ ~MenuImplWin32() {
+ Clear();
+ sscheck(DestroyMenu(hMenu));
+ }
+};
+
+HMENU MenuItemImplWin32::Handle() {
+ return menu->hMenu;
+}
+
+MenuRef CreateMenu() {
+ auto menu = std::make_shared<MenuImplWin32>();
+ // std::enable_shared_from_this fails for some reason, not sure why
+ menu->weakThis = menu;
+ return menu;
+}
+
+class MenuBarImplWin32 final : public MenuBar {
+public:
+ HMENU hMenuBar;
+
+ std::vector<std::shared_ptr<MenuImplWin32>> subMenus;
+
+ MenuBarImplWin32() {
+ sscheck(hMenuBar = ::CreateMenu());
+ }
+
+ MenuRef AddSubMenu(const std::string &label) override {
+ auto subMenu = std::make_shared<MenuImplWin32>();
+ subMenu->weakThis = subMenu;
+ subMenus.push_back(subMenu);
+
+ sscheck(AppendMenuW(hMenuBar, MF_STRING|MF_POPUP,
+ (UINT_PTR)subMenu->hMenu, Widen(label).c_str()));
+
+ return subMenu;
+ }
+
+ void Clear() override {
+ for(int n = GetMenuItemCount(hMenuBar) - 1; n >= 0; n--) {
+ sscheck(RemoveMenu(hMenuBar, n, MF_BYPOSITION));
+ }
+ subMenus.clear();
+ }
+
+ ~MenuBarImplWin32() {
+ Clear();
+ sscheck(DestroyMenu(hMenuBar));
+ }
+};
+
+MenuBarRef GetOrCreateMainMenu(bool *unique) {
+ *unique = false;
+ return std::make_shared<MenuBarImplWin32>();
+}
+
+//-----------------------------------------------------------------------------
+// Windows
+//-----------------------------------------------------------------------------
+
+#define SCROLLBAR_UNIT 65536
+
+class WindowImplWin32 final : public Window {
+public:
+ HWND hWindow = NULL;
+ HWND hTooltip = NULL;
+ HWND hEditor = NULL;
+ WNDPROC editorWndProc = NULL;
+
+#if HAVE_OPENGL == 1
+ HGLRC hGlRc = NULL;
+#elif HAVE_OPENGL == 3
+ static EGLDisplay eglDisplay;
+ EGLSurface eglSurface = EGL_NO_SURFACE;
+ EGLContext eglContext = EGL_NO_CONTEXT;
+#endif
+
+ WINDOWPLACEMENT placement = {};
+ int minWidth = 0, minHeight = 0;
+
+#if defined(HAVE_SPACEWARE)
+ SiOpenData sod = {};
+ SiHdl hSpaceWare = SI_NO_HANDLE;
+#endif
+
+ std::shared_ptr<MenuBarImplWin32> menuBar;
+ std::string tooltipText;
+ bool scrollbarVisible = false;
+
+ static void RegisterWindowClass() {
+ static bool registered;
+ if(registered) return;
+
+ WNDCLASSEXW wc = {};
+ wc.cbSize = sizeof(wc);
+ wc.style = CS_BYTEALIGNCLIENT|CS_BYTEALIGNWINDOW|CS_OWNDC|CS_DBLCLKS;
+ wc.lpfnWndProc = WndProc;
+ wc.cbWndExtra = sizeof(WindowImplWin32 *);
+ wc.hIcon = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(4000),
+ IMAGE_ICON, 32, 32, 0);
+ wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(4000),
+ IMAGE_ICON, 16, 16, 0);
+ wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
+ wc.lpszClassName = L"SolveSpace";
+ sscheck(RegisterClassExW(&wc));
+ registered = true;
+ }
+
+ WindowImplWin32(Window::Kind kind, std::shared_ptr<WindowImplWin32> parentWindow) {
+ placement.length = sizeof(placement);
+
+ RegisterWindowClass();
+
+ HWND hParentWindow = NULL;
+ if(parentWindow) {
+ hParentWindow = parentWindow->hWindow;
+ }
+
+ DWORD style = WS_SIZEBOX|WS_CLIPCHILDREN;
+ switch(kind) {
+ case Window::Kind::TOPLEVEL:
+ style |= WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS;
+ break;
+
+ case Window::Kind::TOOL:
+ style |= WS_POPUPWINDOW|WS_CAPTION;
+ break;
+ }
+ sscheck(hWindow = CreateWindowExW(0, L"SolveSpace", L"", style,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ hParentWindow, NULL, NULL, NULL));
+ sscheck(SetWindowLongPtr(hWindow, 0, (LONG_PTR)this));
+
+ sscheck(hTooltip = CreateWindowExW(0, TOOLTIPS_CLASS, NULL,
+ WS_POPUP|TTS_NOPREFIX|TTS_ALWAYSTIP,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ hWindow, NULL, NULL, NULL));
+ sscheck(SetWindowPos(hTooltip, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE));
+
+ TOOLINFOW ti = {};
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_SUBCLASS;
+ ti.hwnd = hWindow;
+ ti.lpszText = (LPWSTR)L"";
+ sscheck(SendMessageW(hTooltip, TTM_ADDTOOLW, 0, (LPARAM)&ti));
+ sscheck(SendMessageW(hTooltip, TTM_ACTIVATE, FALSE, 0));
+
+ DWORD editorStyle = WS_CLIPSIBLINGS|WS_CHILD|WS_TABSTOP|ES_AUTOHSCROLL;
+ sscheck(hEditor = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDIT, L"", editorStyle,
+ 0, 0, 0, 0, hWindow, NULL, NULL, NULL));
+ sscheck(editorWndProc =
+ (WNDPROC)SetWindowLongPtr(hEditor, GWLP_WNDPROC, (LONG_PTR)EditorWndProc));
+
+ HDC hDc;
+ sscheck(hDc = GetDC(hWindow));
+
+#if HAVE_OPENGL == 1
+ PIXELFORMATDESCRIPTOR pfd = {};
+ pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
+ pfd.nVersion = 1;
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER;
+ pfd.dwLayerMask = PFD_MAIN_PLANE;
+ pfd.iPixelType = PFD_TYPE_RGBA;
+ pfd.cColorBits = 32;
+ pfd.cDepthBits = 24;
+ pfd.cAccumBits = 0;
+ pfd.cStencilBits = 0;
+ int pixelFormat;
+ sscheck(pixelFormat = ChoosePixelFormat(hDc, &pfd));
+ sscheck(SetPixelFormat(hDc, pixelFormat, &pfd));
+
+ sscheck(hGlRc = wglCreateContext(hDc));
+#elif HAVE_OPENGL == 3
+ if(eglDisplay == EGL_NO_DISPLAY) {
+ ssassert(eglBindAPI(EGL_OPENGL_ES_API), "Cannot bind EGL API");
+
+ EGLBoolean initialized = EGL_FALSE;
+ for(auto &platformType : {
+ // Try platform types from least to most amount of software translation required.
+ std::make_pair("OpenGL ES", EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE),
+ std::make_pair("OpenGL", EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE),
+ std::make_pair("Direct3D 11", EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE),
+ std::make_pair("Direct3D 9", EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE),
+ }) {
+ dbp("Initializing ANGLE with %s backend", platformType.first);
+ EGLint displayAttributes[] = {
+ EGL_PLATFORM_ANGLE_TYPE_ANGLE, platformType.second,
+ EGL_NONE
+ };
+ eglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, hDc,
+ displayAttributes);
+ if(eglDisplay != EGL_NO_DISPLAY) {
+ initialized = eglInitialize(eglDisplay, NULL, NULL);
+ if(initialized) break;
+ eglTerminate(eglDisplay);
+ }
+ }
+ ssassert(initialized, "Cannot find a suitable EGL display");
+ }
+
+ EGLint configAttributes[] = {
+ EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_DEPTH_SIZE, 24,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_NONE
+ };
+ EGLint numConfigs;
+ EGLConfig windowConfig;
+ ssassert(eglChooseConfig(eglDisplay, configAttributes, &windowConfig, 1, &numConfigs),
+ "Cannot choose EGL configuration");
+
+ EGLint surfaceAttributes[] = {
+ EGL_NONE
+ };
+ eglSurface = eglCreateWindowSurface(eglDisplay, windowConfig, hWindow, surfaceAttributes);
+ ssassert(eglSurface != EGL_NO_SURFACE, "Cannot create EGL window surface");
+
+ EGLint contextAttributes[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+ eglContext = eglCreateContext(eglDisplay, windowConfig, NULL, contextAttributes);
+ ssassert(eglContext != EGL_NO_CONTEXT, "Cannot create EGL context");
+#endif
+
+ sscheck(ReleaseDC(hWindow, hDc));
+ }
+
+ ~WindowImplWin32() {
+ // Make sure any of our child windows get destroyed before we call DestroyWindow, or their
+ // own destructors may fail.
+ menuBar.reset();
+
+ sscheck(DestroyWindow(hWindow));
+#if defined(HAVE_SPACEWARE)
+ if(hSpaceWare != SI_NO_HANDLE) {
+ SiClose(hSpaceWare);
+ }
+#endif
+ }
+
+ static LRESULT CALLBACK WndProc(HWND h, UINT msg, WPARAM wParam, LPARAM lParam) {
+ if(handlingFatalError) return TRUE;
+
+ WindowImplWin32 *window;
+ sscheck(window = (WindowImplWin32 *)GetWindowLongPtr(h, 0));
+
+ // The wndproc may be called from within CreateWindowEx, and before we've associated
+ // the window with the WindowImplWin32. In that case, just defer to the default wndproc.
+ if(window == NULL) {
+ return DefWindowProcW(h, msg, wParam, lParam);
+ }
+
+#if defined(HAVE_SPACEWARE)
+ if(window->hSpaceWare != SI_NO_HANDLE) {
+ SiGetEventData sged;
+ SiGetEventWinInit(&sged, msg, wParam, lParam);
+
+ SiSpwEvent sse;
+ if(SiGetEvent(window->hSpaceWare, 0, &sged, &sse) == SI_IS_EVENT) {
+ SixDofEvent event = {};
+ event.shiftDown = ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0);
+ event.controlDown = ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0);
+ if(sse.type == SI_MOTION_EVENT) {
+ // The Z axis translation and rotation are both
+ // backwards in the default mapping.
+ event.type = SixDofEvent::Type::MOTION;
+ event.translationX = sse.u.spwData.mData[SI_TX]*1.0,
+ event.translationY = sse.u.spwData.mData[SI_TY]*1.0,
+ event.translationZ = -sse.u.spwData.mData[SI_TZ]*1.0,
+ event.rotationX = sse.u.spwData.mData[SI_RX]*0.001,
+ event.rotationY = sse.u.spwData.mData[SI_RY]*0.001,
+ event.rotationZ = -sse.u.spwData.mData[SI_RZ]*0.001;
+ } else if(sse.type == SI_BUTTON_EVENT) {
+ if(SiButtonPressed(&sse) == SI_APP_FIT_BUTTON) {
+ event.type = SixDofEvent::Type::PRESS;
+ event.button = SixDofEvent::Button::FIT;
+ }
+ if(SiButtonReleased(&sse) == SI_APP_FIT_BUTTON) {
+ event.type = SixDofEvent::Type::RELEASE;
+ event.button = SixDofEvent::Button::FIT;
+ }
+ }
+ return 0;
+ }
+ }
+#endif
+
+ switch (msg) {
+ case WM_ERASEBKGND:
+ break;
+
+ case WM_PAINT: {
+ PAINTSTRUCT ps;
+ HDC hDc = BeginPaint(window->hWindow, &ps);
+ if(window->onRender) {
+#if HAVE_OPENGL == 1
+ wglMakeCurrent(hDc, window->hGlRc);
+#elif HAVE_OPENGL == 3
+ eglMakeCurrent(window->eglDisplay, window->eglSurface,
+ window->eglSurface, window->eglContext);
+#endif
+ window->onRender();
+#if HAVE_OPENGL == 1
+ SwapBuffers(hDc);
+#elif HAVE_OPENGL == 3
+ eglSwapBuffers(window->eglDisplay, window->eglSurface);
+ (void)hDc;
+#endif
+ }
+ EndPaint(window->hWindow, &ps);
+ break;
+ }
+
+ case WM_CLOSE:
+ if(window->onClose) {
+ window->onClose();
+ }
+ break;
+
+ case WM_SIZE:
+ window->Invalidate();
+ break;
+
+ case WM_SIZING: {
+ int pixelRatio = window->GetDevicePixelRatio();
+
+ RECT rcw, rcc;
+ sscheck(GetWindowRect(window->hWindow, &rcw));
+ sscheck(GetClientRect(window->hWindow, &rcc));
+ int nonClientWidth = (rcw.right - rcw.left) - (rcc.right - rcc.left);
+ int nonClientHeight = (rcw.bottom - rcw.top) - (rcc.bottom - rcc.top);
+
+ RECT *rc = (RECT *)lParam;
+ int adjWidth = rc->right - rc->left;
+ int adjHeight = rc->bottom - rc->top;
+
+ adjWidth -= nonClientWidth;
+ adjWidth = max(window->minWidth * pixelRatio, adjWidth);
+ adjWidth += nonClientWidth;
+ adjHeight -= nonClientHeight;
+ adjHeight = max(window->minHeight * pixelRatio, adjHeight);
+ adjHeight += nonClientHeight;
+ switch(wParam) {
+ case WMSZ_RIGHT:
+ case WMSZ_BOTTOMRIGHT:
+ case WMSZ_TOPRIGHT:
+ rc->right = rc->left + adjWidth;
+ break;
+
+ case WMSZ_LEFT:
+ case WMSZ_BOTTOMLEFT:
+ case WMSZ_TOPLEFT:
+ rc->left = rc->right - adjWidth;
+ break;
+ }
+ switch(wParam) {
+ case WMSZ_BOTTOM:
+ case WMSZ_BOTTOMLEFT:
+ case WMSZ_BOTTOMRIGHT:
+ rc->bottom = rc->top + adjHeight;
+ break;
+
+ case WMSZ_TOP:
+ case WMSZ_TOPLEFT:
+ case WMSZ_TOPRIGHT:
+ rc->top = rc->bottom - adjHeight;
+ break;
+ }
+ break;
+ }
+
+ case WM_DPICHANGED: {
+ RECT rc = *(RECT *)lParam;
+ sscheck(SendMessage(window->hWindow, WM_SIZING, WMSZ_BOTTOMRIGHT, (LPARAM)&rc));
+ sscheck(SetWindowPos(window->hWindow, NULL, rc.left, rc.top,
+ rc.right - rc.left, rc.bottom - rc.top,
+ SWP_NOZORDER|SWP_NOACTIVATE));
+ window->Invalidate();
+ break;
+ }
+
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ if(GetMilliseconds() - Platform::contextMenuPopTime < 100) {
+ // Ignore the mouse click that dismisses a context menu, to avoid
+ // (e.g.) clearing a selection.
+ return 0;
+ }
+ // fallthrough
+ case WM_MOUSEMOVE:
+ case WM_MOUSEWHEEL:
+ case WM_MOUSELEAVE: {
+ int pixelRatio = window->GetDevicePixelRatio();
+
+ MouseEvent event = {};
+ event.x = GET_X_LPARAM(lParam) / pixelRatio;
+ event.y = GET_Y_LPARAM(lParam) / pixelRatio;
+ event.button = MouseEvent::Button::NONE;
+
+ event.shiftDown = (wParam & MK_SHIFT) != 0;
+ event.controlDown = (wParam & MK_CONTROL) != 0;
+
+ bool consumed = false;
+ switch(msg) {
+ case WM_LBUTTONDOWN:
+ event.button = MouseEvent::Button::LEFT;
+ event.type = MouseEvent::Type::PRESS;
+ break;
+ case WM_MBUTTONDOWN:
+ event.button = MouseEvent::Button::MIDDLE;
+ event.type = MouseEvent::Type::PRESS;
+ break;
+ case WM_RBUTTONDOWN:
+ event.button = MouseEvent::Button::RIGHT;
+ event.type = MouseEvent::Type::PRESS;
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ event.button = MouseEvent::Button::LEFT;
+ event.type = MouseEvent::Type::DBL_PRESS;
+ break;
+ case WM_MBUTTONDBLCLK:
+ event.button = MouseEvent::Button::MIDDLE;
+ event.type = MouseEvent::Type::DBL_PRESS;
+ break;
+ case WM_RBUTTONDBLCLK:
+ event.button = MouseEvent::Button::RIGHT;
+ event.type = MouseEvent::Type::DBL_PRESS;
+ break;
+
+ case WM_LBUTTONUP:
+ event.button = MouseEvent::Button::LEFT;
+ event.type = MouseEvent::Type::RELEASE;
+ break;
+ case WM_MBUTTONUP:
+ event.button = MouseEvent::Button::MIDDLE;
+ event.type = MouseEvent::Type::RELEASE;
+ break;
+ case WM_RBUTTONUP:
+ event.button = MouseEvent::Button::RIGHT;
+ event.type = MouseEvent::Type::RELEASE;
+ break;
+
+ case WM_MOUSEWHEEL:
+ // Make the mousewheel work according to which window the mouse is
+ // over, not according to which window is active.
+ POINT pt;
+ pt.x = LOWORD(lParam);
+ pt.y = HIWORD(lParam);
+ HWND hWindowUnderMouse;
+ sscheck(hWindowUnderMouse = WindowFromPoint(pt));
+ if(hWindowUnderMouse && hWindowUnderMouse != h) {
+ SendMessageW(hWindowUnderMouse, msg, wParam, lParam);
+ consumed = true;
+ break;
+ }
+
+ event.type = MouseEvent::Type::SCROLL_VERT;
+ event.scrollDelta = GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? 1 : -1;
+ break;
+
+ case WM_MOUSELEAVE:
+ event.type = MouseEvent::Type::LEAVE;
+ break;
+ case WM_MOUSEMOVE: {
+ event.type = MouseEvent::Type::MOTION;
+
+ if(wParam & MK_LBUTTON) {
+ event.button = MouseEvent::Button::LEFT;
+ } else if(wParam & MK_MBUTTON) {
+ event.button = MouseEvent::Button::MIDDLE;
+ } else if(wParam & MK_RBUTTON) {
+ event.button = MouseEvent::Button::RIGHT;
+ }
+
+ // We need this in order to get the WM_MOUSELEAVE
+ TRACKMOUSEEVENT tme = {};
+ tme.cbSize = sizeof(tme);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = window->hWindow;
+ sscheck(TrackMouseEvent(&tme));
+ break;
+ }
+ }
+
+ if(!consumed && window->onMouseEvent) {
+ window->onMouseEvent(event);
+ }
+ break;
+ }
+
+ case WM_KEYDOWN:
+ case WM_KEYUP: {
+ Platform::KeyboardEvent event = {};
+ if(msg == WM_KEYDOWN) {
+ event.type = Platform::KeyboardEvent::Type::PRESS;
+ } else if(msg == WM_KEYUP) {
+ event.type = Platform::KeyboardEvent::Type::RELEASE;
+ }
+
+ if(GetKeyState(VK_SHIFT) & 0x8000)
+ event.shiftDown = true;
+ if(GetKeyState(VK_CONTROL) & 0x8000)
+ event.controlDown = true;
+
+ if(wParam >= VK_F1 && wParam <= VK_F12) {
+ event.key = Platform::KeyboardEvent::Key::FUNCTION;
+ event.num = wParam - VK_F1 + 1;
+ } else {
+ event.key = Platform::KeyboardEvent::Key::CHARACTER;
+ event.chr = tolower(MapVirtualKeyW(wParam, MAPVK_VK_TO_CHAR));
+ if(event.chr == 0) {
+ if(wParam == VK_DELETE) {
+ event.chr = '\x7f';
+ } else {
+ // Non-mappable key.
+ break;
+ }
+ } else if(event.chr == '.' && event.shiftDown) {
+ event.chr = '>';
+ event.shiftDown = false;;
+ }
+ }
+
+ if(window->onKeyboardEvent) {
+ window->onKeyboardEvent(event);
+ }
+ break;
+ }
+
+ case WM_SYSKEYDOWN: {
+ HWND hParent;
+ sscheck(hParent = GetParent(h));
+ if(hParent != NULL) {
+ // If the user presses the Alt key when a tool window has focus,
+ // then that should probably go to the main window instead.
+ sscheck(SetForegroundWindow(hParent));
+ break;
+ } else {
+ return DefWindowProcW(h, msg, wParam, lParam);
+ }
+ }
+
+ case WM_VSCROLL: {
+ SCROLLINFO si = {};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS|SIF_TRACKPOS|SIF_RANGE|SIF_PAGE;
+ sscheck(GetScrollInfo(window->hWindow, SB_VERT, &si));
+
+ switch(LOWORD(wParam)) {
+ case SB_LINEUP: si.nPos -= SCROLLBAR_UNIT; break;
+ case SB_PAGEUP: si.nPos -= si.nPage; break;
+ case SB_LINEDOWN: si.nPos += SCROLLBAR_UNIT; break;
+ case SB_PAGEDOWN: si.nPos += si.nPage; break;
+ case SB_TOP: si.nPos = si.nMin; break;
+ case SB_BOTTOM: si.nPos = si.nMax; break;
+ case SB_THUMBTRACK:
+ case SB_THUMBPOSITION: si.nPos = si.nTrackPos; break;
+ }
+
+ si.nPos = min((UINT)si.nPos, (UINT)(si.nMax - si.nPage));
+
+ if(window->onScrollbarAdjusted) {
+ window->onScrollbarAdjusted((double)si.nPos / SCROLLBAR_UNIT);
+ }
+ break;
+ }
+
+ case WM_MENUCOMMAND: {
+ MENUITEMINFOW mii = {};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_DATA;
+ sscheck(GetMenuItemInfoW((HMENU)lParam, wParam, TRUE, &mii));
+
+ MenuItemImplWin32 *menuItem = (MenuItemImplWin32 *)mii.dwItemData;
+ if(menuItem->onTrigger) {
+ menuItem->onTrigger();
+ }
+ break;
+ }
+
+ default:
+ return DefWindowProcW(h, msg, wParam, lParam);
+ }
+
+ return 0;
+ }
+
+ static LRESULT CALLBACK EditorWndProc(HWND h, UINT msg, WPARAM wParam, LPARAM lParam) {
+ if(handlingFatalError) return 0;
+
+ HWND hWindow;
+ sscheck(hWindow = GetParent(h));
+
+ WindowImplWin32 *window;
+ sscheck(window = (WindowImplWin32 *)GetWindowLongPtr(hWindow, 0));
+
+ switch(msg) {
+ case WM_CHAR:
+ if(wParam == VK_RETURN) {
+ if(window->onEditingDone) {
+ int length;
+ sscheck(length = GetWindowTextLength(h));
+
+ std::wstring resultW;
+ resultW.resize(length);
+ sscheck(GetWindowTextW(h, &resultW[0], resultW.length() + 1));
+
+ window->onEditingDone(Narrow(resultW));
+ return 0;
+ }
+ } else if(wParam == VK_ESCAPE) {
+ sscheck(SendMessageW(hWindow, msg, wParam, lParam));
+ return 0;
+ }
+ }
+
+ return CallWindowProc(window->editorWndProc, h, msg, wParam, lParam);
+ }
+
+ double GetPixelDensity() override {
+ UINT dpi;
+ sscheck(dpi = ssGetDpiForWindow(hWindow));
+ return (double)dpi;
+ }
+
+ int GetDevicePixelRatio() override {
+ UINT dpi;
+ sscheck(dpi = ssGetDpiForWindow(hWindow));
+ return dpi / USER_DEFAULT_SCREEN_DPI;
+ }
+
+ bool IsVisible() override {
+ BOOL isVisible;
+ sscheck(isVisible = IsWindowVisible(hWindow));
+ return isVisible == TRUE;
+ }
+
+ void SetVisible(bool visible) override {
+ sscheck(ShowWindow(hWindow, visible ? SW_SHOW : SW_HIDE));
+ }
+
+ void Focus() override {
+ sscheck(SetActiveWindow(hWindow));
+ }
+
+ bool IsFullScreen() override {
+ DWORD style;
+ sscheck(style = GetWindowLongPtr(hWindow, GWL_STYLE));
+ return !(style & WS_OVERLAPPEDWINDOW);
+ }
+
+ void SetFullScreen(bool fullScreen) override {
+ DWORD style;
+ sscheck(style = GetWindowLongPtr(hWindow, GWL_STYLE));
+ if(fullScreen) {
+ sscheck(GetWindowPlacement(hWindow, &placement));
+
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ sscheck(GetMonitorInfo(MonitorFromWindow(hWindow, MONITOR_DEFAULTTONEAREST), &mi));
+
+ sscheck(SetWindowLong(hWindow, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW));
+ sscheck(SetWindowPos(hWindow, HWND_TOP,
+ mi.rcMonitor.left, mi.rcMonitor.top,
+ mi.rcMonitor.right - mi.rcMonitor.left,
+ mi.rcMonitor.bottom - mi.rcMonitor.top,
+ SWP_NOOWNERZORDER|SWP_FRAMECHANGED));
+ } else {
+ sscheck(SetWindowLong(hWindow, GWL_STYLE, style | WS_OVERLAPPEDWINDOW));
+ sscheck(SetWindowPlacement(hWindow, &placement));
+ sscheck(SetWindowPos(hWindow, NULL, 0, 0, 0, 0,
+ SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|
+ SWP_NOOWNERZORDER|SWP_FRAMECHANGED));
+ }
+ }
+
+ void SetTitle(const std::string &title) override {
+ sscheck(SetWindowTextW(hWindow, PrepareTitle(title).c_str()));
+ }
+
+ void SetMenuBar(MenuBarRef newMenuBar) override {
+ menuBar = std::static_pointer_cast<MenuBarImplWin32>(newMenuBar);
+
+ MENUINFO mi = {};
+ mi.cbSize = sizeof(mi);
+ mi.fMask = MIM_APPLYTOSUBMENUS|MIM_STYLE;
+ mi.dwStyle = MNS_NOTIFYBYPOS;
+ sscheck(SetMenuInfo(menuBar->hMenuBar, &mi));
+
+ sscheck(SetMenu(hWindow, menuBar->hMenuBar));
+ }
+
+ void GetContentSize(double *width, double *height) override {
+ int pixelRatio = GetDevicePixelRatio();
+
+ RECT rc;
+ sscheck(GetClientRect(hWindow, &rc));
+ *width = (rc.right - rc.left) / pixelRatio;
+ *height = (rc.bottom - rc.top) / pixelRatio;
+ }
+
+ void SetMinContentSize(double width, double height) {
+ minWidth = (int)width;
+ minHeight = (int)height;
+
+ int pixelRatio = GetDevicePixelRatio();
+
+ RECT rc;
+ sscheck(GetClientRect(hWindow, &rc));
+ if(rc.right - rc.left < minWidth * pixelRatio) {
+ rc.right = rc.left + minWidth * pixelRatio;
+ }
+ if(rc.bottom - rc.top < minHeight * pixelRatio) {
+ rc.bottom = rc.top + minHeight * pixelRatio;
+ }
+ }
+
+ void FreezePosition(SettingsRef settings, const std::string &key) override {
+ sscheck(GetWindowPlacement(hWindow, &placement));
+
+ BOOL isMaximized;
+ sscheck(isMaximized = IsZoomed(hWindow));
+
+ RECT rc = placement.rcNormalPosition;
+ settings->FreezeInt(key + "_Left", rc.left);
+ settings->FreezeInt(key + "_Right", rc.right);
+ settings->FreezeInt(key + "_Top", rc.top);
+ settings->FreezeInt(key + "_Bottom", rc.bottom);
+ settings->FreezeBool(key + "_Maximized", isMaximized == TRUE);
+ }
+
+ void ThawPosition(SettingsRef settings, const std::string &key) override {
+ sscheck(GetWindowPlacement(hWindow, &placement));
+
+ RECT rc = placement.rcNormalPosition;
+ rc.left = settings->ThawInt(key + "_Left", rc.left);
+ rc.right = settings->ThawInt(key + "_Right", rc.right);
+ rc.top = settings->ThawInt(key + "_Top", rc.top);
+ rc.bottom = settings->ThawInt(key + "_Bottom", rc.bottom);
+
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ sscheck(GetMonitorInfo(MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST), &mi));
+
+ // If it somehow ended up off-screen, then put it back.
+ RECT mrc = mi.rcMonitor;
+ rc.left = Clamp(rc.left, mrc.left, mrc.right);
+ rc.right = Clamp(rc.right, mrc.left, mrc.right);
+ rc.top = Clamp(rc.top, mrc.top, mrc.bottom);
+ rc.bottom = Clamp(rc.bottom, mrc.top, mrc.bottom);
+
+ // And make sure the minimum size is respected. (We can freeze a size smaller
+ // than minimum size if the DPI changed between runs.)
+ sscheck(SendMessageW(hWindow, WM_SIZING, WMSZ_BOTTOMRIGHT, (LPARAM)&rc));
+
+ placement.flags = 0;
+ if(settings->ThawBool(key + "_Maximized", false)) {
+ placement.showCmd = SW_SHOWMAXIMIZED;
+ } else {
+ placement.showCmd = SW_SHOW;
+ }
+ placement.rcNormalPosition = rc;
+ sscheck(SetWindowPlacement(hWindow, &placement));
+ }
+
+ void SetCursor(Cursor cursor) override {
+ LPWSTR cursorName = IDC_ARROW;
+ switch(cursor) {
+ case Cursor::POINTER: cursorName = IDC_ARROW; break;
+ case Cursor::HAND: cursorName = IDC_HAND; break;
+ }
+
+ HCURSOR hCursor;
+ sscheck(hCursor = LoadCursorW(NULL, cursorName));
+ sscheck(::SetCursor(hCursor));
+ }
+
+ void SetTooltip(const std::string &newText, double x, double y,
+ double width, double height) override {
+ if(newText == tooltipText) return;
+ tooltipText = newText;
+
+ if(!newText.empty()) {
+ int pixelRatio = GetDevicePixelRatio();
+ RECT toolRect;
+ toolRect.left = (int)(x * pixelRatio);
+ toolRect.top = (int)(y * pixelRatio);
+ toolRect.right = toolRect.left + (int)(width * pixelRatio);
+ toolRect.bottom = toolRect.top + (int)(height * pixelRatio);
+
+ std::wstring newTextW = Widen(newText);
+ TOOLINFOW ti = {};
+ ti.cbSize = sizeof(ti);
+ ti.hwnd = hWindow;
+ ti.rect = toolRect;
+ ti.lpszText = &newTextW[0];
+ sscheck(SendMessageW(hTooltip, TTM_UPDATETIPTEXTW, 0, (LPARAM)&ti));
+ sscheck(SendMessageW(hTooltip, TTM_NEWTOOLRECTW, 0, (LPARAM)&ti));
+ }
+ // The following SendMessage call sometimes fails with ERROR_ACCESS_DENIED for
+ // no discernible reason, but only on wine.
+ SendMessageW(hTooltip, TTM_ACTIVATE, !newText.empty(), 0);
+ }
+
+ bool IsEditorVisible() override {
+ BOOL visible;
+ sscheck(visible = IsWindowVisible(hEditor));
+ return visible == TRUE;
+ }
+
+ void ShowEditor(double x, double y, double fontHeight, double minWidth,
+ bool isMonospace, const std::string &text) override {
+ if(IsEditorVisible()) return;
+
+ int pixelRatio = GetDevicePixelRatio();
+
+ HFONT hFont = CreateFontW(-(LONG)fontHeight * GetDevicePixelRatio(), 0, 0, 0,
+ FW_REGULAR, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
+ DEFAULT_QUALITY, FF_DONTCARE, isMonospace ? L"Lucida Console" : L"Arial");
+ if(hFont == NULL) {
+ sscheck(hFont = (HFONT)GetStockObject(SYSTEM_FONT));
+ }
+ sscheck(SendMessageW(hEditor, WM_SETFONT, (WPARAM)hFont, FALSE));
+ sscheck(SendMessageW(hEditor, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, 0));
+
+ std::wstring textW = Widen(text);
+
+ HDC hDc;
+ TEXTMETRICW tm;
+ SIZE ts;
+ sscheck(hDc = GetDC(hEditor));
+ sscheck(SelectObject(hDc, hFont));
+ sscheck(GetTextMetricsW(hDc, &tm));
+ sscheck(GetTextExtentPoint32W(hDc, textW.c_str(), textW.length(), &ts));
+ sscheck(ReleaseDC(hEditor, hDc));
+
+ RECT rc;
+ rc.left = (LONG)x * pixelRatio;
+ rc.top = (LONG)y * pixelRatio - tm.tmAscent;
+ // Add one extra char width to avoid scrolling.
+ rc.right = (LONG)x * pixelRatio +
+ std::max((LONG)minWidth * pixelRatio, ts.cx + tm.tmAveCharWidth);
+ rc.bottom = (LONG)y * pixelRatio + tm.tmDescent;
+ sscheck(ssAdjustWindowRectExForDpi(&rc, 0, /*bMenu=*/FALSE, WS_EX_CLIENTEDGE,
+ ssGetDpiForWindow(hWindow)));
+
+ sscheck(MoveWindow(hEditor, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
+ /*bRepaint=*/true));
+ sscheck(ShowWindow(hEditor, SW_SHOW));
+ if(!textW.empty()) {
+ sscheck(SendMessageW(hEditor, WM_SETTEXT, 0, (LPARAM)textW.c_str()));
+ sscheck(SendMessageW(hEditor, EM_SETSEL, 0, textW.length()));
+ sscheck(SetFocus(hEditor));
+ }
+ }
+
+ void HideEditor() override {
+ if(!IsEditorVisible()) return;
+
+ sscheck(ShowWindow(hEditor, SW_HIDE));
+ }
+
+ void SetScrollbarVisible(bool visible) override {
+ scrollbarVisible = visible;
+ sscheck(ShowScrollBar(hWindow, SB_VERT, visible));
+ }
+
+ void ConfigureScrollbar(double min, double max, double pageSize) override {
+ SCROLLINFO si = {};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_RANGE|SIF_PAGE;
+ si.nMin = (UINT)(min * SCROLLBAR_UNIT);
+ si.nMax = (UINT)(max * SCROLLBAR_UNIT);
+ si.nPage = (UINT)(pageSize * SCROLLBAR_UNIT);
+ sscheck(SetScrollInfo(hWindow, SB_VERT, &si, /*redraw=*/TRUE));
+ }
+
+ double GetScrollbarPosition() override {
+ if(!scrollbarVisible) return 0.0;
+
+ SCROLLINFO si = {};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS;
+ sscheck(GetScrollInfo(hWindow, SB_VERT, &si));
+ return (double)si.nPos / SCROLLBAR_UNIT;
+ }
+
+ void SetScrollbarPosition(double pos) override {
+ if(!scrollbarVisible) return;
+
+ SCROLLINFO si = {};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS;
+ sscheck(GetScrollInfo(hWindow, SB_VERT, &si));
+ if(si.nPos == (int)(pos * SCROLLBAR_UNIT))
+ return;
+
+ si.nPos = (int)(pos * SCROLLBAR_UNIT);
+ sscheck(SetScrollInfo(hWindow, SB_VERT, &si, /*redraw=*/TRUE));
+
+ // Windows won't synthesize a WM_VSCROLL for us here.
+ if(onScrollbarAdjusted) {
+ onScrollbarAdjusted((double)si.nPos / SCROLLBAR_UNIT);
+ }
+ }
+
+ void Invalidate() override {
+ sscheck(InvalidateRect(hWindow, NULL, /*bErase=*/FALSE));
+ }
+};
+
+#if HAVE_OPENGL == 3
+EGLDisplay WindowImplWin32::eglDisplay = EGL_NO_DISPLAY;
+#endif
+
+WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) {
+ return std::make_shared<WindowImplWin32>(kind,
+ std::static_pointer_cast<WindowImplWin32>(parentWindow));
+}
+
+//-----------------------------------------------------------------------------
+// 3DConnexion support
+//-----------------------------------------------------------------------------
+
+#if defined(HAVE_SPACEWARE)
+static HWND hSpaceWareDriverClass;
+
+void Open3DConnexion() {
+ hSpaceWareDriverClass = FindWindowW(L"SpaceWare Driver Class", NULL);
+ if(hSpaceWareDriverClass != NULL) {
+ SiInitialize();
+ }
+}
+
+void Close3DConnexion() {
+ if(hSpaceWareDriverClass != NULL) {
+ SiTerminate();
+ }
+}
+
+void Request3DConnexionEventsForWindow(WindowRef window) {
+ std::shared_ptr<WindowImplWin32> windowImpl =
+ std::static_pointer_cast<WindowImplWin32>(window);
+ if(hSpaceWareDriverClass != NULL) {
+ SiOpenWinInit(&windowImpl->sod, windowImpl->hWindow);
+ windowImpl->hSpaceWare = SiOpen("SolveSpace", SI_ANY_DEVICE, SI_NO_MASK, SI_EVENT,
+ &windowImpl->sod);
+ SiSetUiMode(windowImpl->hSpaceWare, SI_UI_NO_CONTROLS);
+ }
+}
+#else
+void Open3DConnexion() {}
+void Close3DConnexion() {}
+void Request3DConnexionEventsForWindow(WindowRef window) {}
+#endif
+
+//-----------------------------------------------------------------------------
+// Message dialogs
+//-----------------------------------------------------------------------------
+
+class MessageDialogImplWin32 final : public MessageDialog {
+public:
+ MSGBOXPARAMSW mbp = {};
+
+ int style;
+
+ std::wstring titleW;
+ std::wstring messageW;
+ std::wstring descriptionW;
+ std::wstring textW;
+
+ std::vector<int> buttons;
+ int defaultButton;
+
+ MessageDialogImplWin32() {
+ mbp.cbSize = sizeof(mbp);
+ SetTitle("Message");
+ }
+
+ void SetType(Type type) override {
+ switch(type) {
+ case Type::INFORMATION:
+ style = MB_ICONINFORMATION;
+ break;
+
+ case Type::QUESTION:
+ style = MB_ICONQUESTION;
+ break;
+
+ case Type::WARNING:
+ style = MB_ICONWARNING;
+ break;
+
+ case Type::ERROR:
+ style = MB_ICONERROR;
+ break;
+ }
+ }
+
+ void SetTitle(std::string title) override {
+ titleW = PrepareTitle(title);
+ mbp.lpszCaption = titleW.c_str();
+ }
+
+ void SetMessage(std::string message) override {
+ messageW = Widen(message);
+ UpdateText();
+ }
+
+ void SetDescription(std::string description) override {
+ descriptionW = Widen(description);
+ UpdateText();
+ }
+
+ void UpdateText() {
+ textW = messageW + L"\n\n" + descriptionW;
+ mbp.lpszText = textW.c_str();
+ }
+
+ void AddButton(std::string _label, Response response, bool isDefault) override {
+ int button;
+ switch(response) {
+ case Response::NONE: ssassert(false, "Invalid response");
+ case Response::OK: button = IDOK; break;
+ case Response::YES: button = IDYES; break;
+ case Response::NO: button = IDNO; break;
+ case Response::CANCEL: button = IDCANCEL; break;
+ }
+ buttons.push_back(button);
+ if(isDefault) {
+ defaultButton = button;
+ }
+ }
+
+ Response RunModal() override {
+ mbp.dwStyle = style;
+
+ std::sort(buttons.begin(), buttons.end());
+ if(buttons == std::vector<int>({ IDOK })) {
+ mbp.dwStyle |= MB_OK;
+ } else if(buttons == std::vector<int>({ IDOK, IDCANCEL })) {
+ mbp.dwStyle |= MB_OKCANCEL;
+ } else if(buttons == std::vector<int>({ IDYES, IDNO })) {
+ mbp.dwStyle |= MB_YESNO;
+ } else if(buttons == std::vector<int>({ IDCANCEL, IDYES, IDNO })) {
+ mbp.dwStyle |= MB_YESNOCANCEL;
+ } else {
+ ssassert(false, "Unexpected button set");
+ }
+
+ switch(MessageBoxIndirectW(&mbp)) {
+ case IDOK: return Response::OK; break;
+ case IDYES: return Response::YES; break;
+ case IDNO: return Response::NO; break;
+ case IDCANCEL: return Response::CANCEL; break;
+ default: ssassert(false, "Unexpected response");
+ }
+ }
+};
+
+MessageDialogRef CreateMessageDialog(WindowRef parentWindow) {
+ std::shared_ptr<MessageDialogImplWin32> dialog = std::make_shared<MessageDialogImplWin32>();
+ dialog->mbp.hwndOwner = std::static_pointer_cast<WindowImplWin32>(parentWindow)->hWindow;
+ return dialog;
+}
+
+//-----------------------------------------------------------------------------
+// File dialogs
+//-----------------------------------------------------------------------------
+
+class FileDialogImplWin32 final : public FileDialog {
+public:
+ OPENFILENAMEW ofn = {};
+ bool isSaveDialog;
+ std::wstring titleW;
+ std::wstring filtersW;
+ std::wstring defExtW;
+ std::wstring initialDirW;
+ // UNC paths may be as long as 32767 characters.
+ // Unfortunately, the Get*FileName API does not provide any way to use it
+ // except with a preallocated buffer of fixed size, so we use something
+ // reasonably large.
+ wchar_t filenameWC[32768] = {};
+
+ FileDialogImplWin32() {
+ ofn.lStructSize = sizeof(ofn);
+ ofn.lpstrFile = filenameWC;
+ ofn.nMaxFile = sizeof(filenameWC) / sizeof(wchar_t);
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY |
+ OFN_OVERWRITEPROMPT;
+ if(isSaveDialog) {
+ SetTitle(C_("title", "Save File"));
+ } else {
+ SetTitle(C_("title", "Open File"));
+ }
+ }
+
+ void SetTitle(std::string title) override {
+ titleW = PrepareTitle(title);
+ ofn.lpstrTitle = titleW.c_str();
+ }
+
+ void SetCurrentName(std::string name) override {
+ SetFilename(GetFilename().Parent().Join(name));
+ }
+
+ Platform::Path GetFilename() override {
+ return Path::From(Narrow(filenameWC));
+ }
+
+ void SetFilename(Platform::Path path) override {
+ wcsncpy(filenameWC, Widen(path.raw).c_str(), sizeof(filenameWC) / sizeof(wchar_t) - 1);
+ }
+
+ void AddFilter(std::string name, std::vector<std::string> extensions) override {
+ std::string desc, patterns;
+ for(auto extension : extensions) {
+ std::string pattern = "*." + extension;
+ if(!desc.empty()) desc += ", ";
+ desc += pattern;
+ if(!patterns.empty()) patterns += ";";
+ patterns += pattern;
+ }
+ filtersW += Widen(name + " (" + desc + ")" + '\0' + patterns + '\0');
+ ofn.lpstrFilter = filtersW.c_str();
+ if(ofn.lpstrDefExt == NULL) {
+ defExtW = Widen(extensions.front());
+ ofn.lpstrDefExt = defExtW.c_str();
+ }
+ }
+
+ void FreezeChoices(SettingsRef settings, const std::string &key) override {
+ settings->FreezeString("Dialog_" + key + "_Folder", GetFilename().Parent().raw);
+ settings->FreezeInt("Dialog_" + key + "_Filter", ofn.nFilterIndex);
+ }
+
+ void ThawChoices(SettingsRef settings, const std::string &key) override {
+ initialDirW = Widen(settings->ThawString("Dialog_" + key + "_Folder", ""));
+ ofn.lpstrInitialDir = initialDirW.c_str();
+ ofn.nFilterIndex = settings->ThawInt("Dialog_" + key + "_Filter", 0);
+ }
+
+ bool RunModal() override {
+ if(GetFilename().IsEmpty()) {
+ SetFilename(Path::From(_("untitled")));
+ }
+
+ if(isSaveDialog) {
+ return GetSaveFileNameW(&ofn) == TRUE;
+ } else {
+ return GetOpenFileNameW(&ofn) == TRUE;
+ }
+ }
+};
+
+FileDialogRef CreateOpenFileDialog(WindowRef parentWindow) {
+ std::shared_ptr<FileDialogImplWin32> dialog = std::make_shared<FileDialogImplWin32>();
+ dialog->ofn.hwndOwner = std::static_pointer_cast<WindowImplWin32>(parentWindow)->hWindow;
+ dialog->isSaveDialog = false;
+ return dialog;
+}
+
+FileDialogRef CreateSaveFileDialog(WindowRef parentWindow) {
+ std::shared_ptr<FileDialogImplWin32> dialog = std::make_shared<FileDialogImplWin32>();
+ dialog->ofn.hwndOwner = std::static_pointer_cast<WindowImplWin32>(parentWindow)->hWindow;
+ dialog->isSaveDialog = true;
+ return dialog;
+}
+
+//-----------------------------------------------------------------------------
+// Application-wide APIs
+//-----------------------------------------------------------------------------
+
+std::vector<Platform::Path> GetFontFiles() {
+ std::vector<Platform::Path> fonts;
+
+ std::wstring fontsDirW(MAX_PATH, '\0');
+ fontsDirW.resize(GetWindowsDirectoryW(&fontsDirW[0], fontsDirW.length()));
+ fontsDirW += L"\\fonts\\";
+ Platform::Path fontsDir = Platform::Path::From(Narrow(fontsDirW));
+
+ WIN32_FIND_DATAW wfd;
+ HANDLE h = FindFirstFileW((fontsDirW + L"*").c_str(), &wfd);
+ while(h != INVALID_HANDLE_VALUE) {
+ fonts.push_back(fontsDir.Join(Narrow(wfd.cFileName)));
+ if(!FindNextFileW(h, &wfd)) break;
+ }
+
+ return fonts;
+}
+
+void OpenInBrowser(const std::string &url) {
+ ShellExecuteW(NULL, L"open", Widen(url).c_str(), NULL, NULL, SW_SHOWNORMAL);
+}
+
+std::vector<std::string> InitGui(int argc, char **argv) {
+ std::vector<std::string> args = InitCli(argc, argv);
+
+ INITCOMMONCONTROLSEX icc;
+ icc.dwSize = sizeof(icc);
+ icc.dwICC = ICC_STANDARD_CLASSES|ICC_BAR_CLASSES;
+ InitCommonControlsEx(&icc);
+
+ if(!SetLocale((uint16_t)GetUserDefaultLCID())) {
+ SetLocale("en_US");
+ }
+
+ return args;
+}
+
+void RunGui() {
+ MSG msg;
+ // The return value of the following functions doesn't indicate success or failure.
+ while(GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
+void ExitGui() {
+ PostQuitMessage(0);
+}
+
+void ClearGui() {}
+
+}
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Platform-dependent functionality.
+//
+// Copyright 2017 whitequark
+//-----------------------------------------------------------------------------
+#if defined(__APPLE__)
+// Include Apple headers before solvespace.h to avoid identifier clashes.
+# include <CoreFoundation/CFString.h>
+# include <CoreFoundation/CFURL.h>
+# include <CoreFoundation/CFBundle.h>
+#endif
+#include "solvespace.h"
+#include "mimalloc.h"
+#include "config.h"
+#if defined(WIN32)
+// Conversely, include Microsoft headers after solvespace.h to avoid clashes.
+# include <windows.h>
+# include <shellapi.h>
+#else
+# include <unistd.h>
+# include <sys/stat.h>
+#endif
+
+namespace SolveSpace {
+namespace Platform {
+
+//-----------------------------------------------------------------------------
+// UTF-8 ⟷ UTF-16 conversion, on Windows.
+//-----------------------------------------------------------------------------
+
+#if defined(WIN32)
+
+std::string Narrow(const wchar_t *in)
+{
+ std::string out;
+ DWORD len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL);
+ out.resize(len - 1);
+ ssassert(WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], len, NULL, NULL),
+ "Invalid UTF-16");
+ return out;
+}
+
+std::string Narrow(const std::wstring &in)
+{
+ if(in == L"") return "";
+
+ std::string out;
+ out.resize(WideCharToMultiByte(CP_UTF8, 0, &in[0], (int)in.length(),
+ NULL, 0, NULL, NULL));
+ ssassert(WideCharToMultiByte(CP_UTF8, 0, &in[0], (int)in.length(),
+ &out[0], (int)out.length(), NULL, NULL),
+ "Invalid UTF-16");
+ return out;
+}
+
+std::wstring Widen(const char *in)
+{
+ std::wstring out;
+ DWORD len = MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0);
+ out.resize(len - 1);
+ ssassert(MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], len),
+ "Invalid UTF-8");
+ return out;
+}
+
+std::wstring Widen(const std::string &in)
+{
+ if(in == "") return L"";
+
+ std::wstring out;
+ out.resize(MultiByteToWideChar(CP_UTF8, 0, &in[0], (int)in.length(), NULL, 0));
+ ssassert(MultiByteToWideChar(CP_UTF8, 0, &in[0], (int)in.length(),
+ &out[0], (int)out.length()),
+ "Invalid UTF-8");
+ return out;
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Path utility functions.
+//-----------------------------------------------------------------------------
+
+static std::vector<std::string> Split(const std::string &joined, char separator) {
+ std::vector<std::string> parts;
+
+ size_t oldpos = 0, pos = 0;
+ while(true) {
+ oldpos = pos;
+ pos = joined.find(separator, pos);
+ if(pos == std::string::npos) break;
+ parts.push_back(joined.substr(oldpos, pos - oldpos));
+ pos += 1;
+ }
+
+ if(oldpos != joined.length() - 1) {
+ parts.push_back(joined.substr(oldpos));
+ }
+
+ return parts;
+}
+
+static std::string Concat(const std::vector<std::string> &parts, char separator) {
+ std::string joined;
+
+ bool first = true;
+ for(auto &part : parts) {
+ if(!first) joined += separator;
+ joined += part;
+ first = false;
+ }
+
+ return joined;
+}
+
+//-----------------------------------------------------------------------------
+// Path manipulation.
+//-----------------------------------------------------------------------------
+
+#if defined(WIN32)
+const char SEPARATOR = '\\';
+#else
+const char SEPARATOR = '/';
+#endif
+
+Path Path::From(std::string raw) {
+ Path path = { raw };
+ return path;
+}
+
+Path Path::CurrentDirectory() {
+#if defined(WIN32)
+ // On Windows, OpenFile needs an absolute UNC path proper, so get that.
+ std::wstring rawW;
+ rawW.resize(GetCurrentDirectoryW(0, NULL));
+ DWORD length = GetCurrentDirectoryW((int)rawW.length(), &rawW[0]);
+ ssassert(length > 0 && length == rawW.length() - 1, "Cannot get current directory");
+ rawW.resize(length);
+ return From(Narrow(rawW));
+#else
+ char *raw = getcwd(NULL, 0);
+ ssassert(raw != NULL, "Cannot get current directory");
+ Path path = From(raw);
+ free(raw);
+ return path;
+#endif
+}
+
+std::string Path::FileName() const {
+ std::string fileName = raw;
+ size_t slash = fileName.rfind(SEPARATOR);
+ if(slash != std::string::npos) {
+ fileName = fileName.substr(slash + 1);
+ }
+ return fileName;
+}
+
+std::string Path::FileStem() const {
+ std::string baseName = FileName();
+ size_t dot = baseName.rfind('.');
+ if(dot != std::string::npos) {
+ baseName = baseName.substr(0, dot);
+ }
+ return baseName;
+}
+
+std::string Path::Extension() const {
+ size_t dot = raw.rfind('.');
+ if(dot != std::string::npos) {
+ return raw.substr(dot + 1);
+ }
+ return "";
+}
+
+bool Path::HasExtension(std::string theirExt) const {
+ std::string ourExt = Extension();
+ std::transform(ourExt.begin(), ourExt.end(), ourExt.begin(), ::tolower);
+ std::transform(theirExt.begin(), theirExt.end(), theirExt.begin(), ::tolower);
+ return ourExt == theirExt;
+}
+
+Path Path::WithExtension(std::string ext) const {
+ Path withExt = *this;
+ size_t dot = withExt.raw.rfind('.');
+ if(dot != std::string::npos) {
+ withExt.raw.erase(dot);
+ }
+ withExt.raw += ".";
+ withExt.raw += ext;
+ return withExt;
+}
+
+static void FindPrefix(const std::string &raw, size_t *pos) {
+ *pos = std::string::npos;
+#if defined(WIN32)
+ if(raw.size() >= 7 && raw[2] == '?' && raw[3] == '\\' &&
+ isalpha(raw[4]) && raw[5] == ':' && raw[6] == '\\') {
+ *pos = 7;
+ } else if(raw.size() >= 3 && isalpha(raw[0]) && raw[1] == ':' && raw[2] == '\\') {
+ *pos = 3;
+ } else if(raw.size() >= 2 && raw[0] == '\\' && raw[1] == '\\') {
+ size_t slashAt = raw.find('\\', 2);
+ if(slashAt != std::string::npos) {
+ *pos = raw.find('\\', slashAt + 1);
+ }
+ }
+#else
+ if(!raw.empty() && raw[0] == '/') {
+ *pos = 1;
+ }
+#endif
+}
+
+bool Path::IsAbsolute() const {
+ size_t pos;
+ FindPrefix(raw, &pos);
+ return pos != std::string::npos;
+}
+
+// Removes one component from the end of the path.
+// Returns an empty path if the path consists only of a root.
+Path Path::Parent() const {
+ Path parent = { raw };
+ if(!parent.raw.empty() && parent.raw.back() == SEPARATOR) {
+ parent.raw.pop_back();
+ }
+ size_t slash = parent.raw.rfind(SEPARATOR);
+ if(slash != std::string::npos) {
+ parent.raw = parent.raw.substr(0, slash + 1);
+ } else {
+ parent.raw.clear();
+ }
+ if(IsAbsolute() && !parent.IsAbsolute()) {
+ return From("");
+ }
+ return parent;
+}
+
+// Concatenates a component to this path.
+// Returns an empty path if this path or the component is empty.
+Path Path::Join(const std::string &component) const {
+ ssassert(component.find(SEPARATOR) == std::string::npos,
+ "Use the Path::Join(const Path &) overload to append an entire path");
+ return Join(Path::From(component));
+}
+
+// Concatenates a relative path to this path.
+// Returns an empty path if either path is empty, or the other path is absolute.
+Path Path::Join(const Path &other) const {
+ if(IsEmpty() || other.IsEmpty() || other.IsAbsolute()) {
+ return From("");
+ }
+
+ Path joined = { raw };
+ if(joined.raw.back() != SEPARATOR) {
+ joined.raw += SEPARATOR;
+ }
+ joined.raw += other.raw;
+ return joined;
+}
+
+// Expands the "." and ".." components in this path.
+// On Windows, additionally prepends the UNC prefix to absolute paths without one.
+// Returns an empty path if a ".." component would escape from the root.
+Path Path::Expand(bool fromCurrentDirectory) const {
+ Path source;
+ Path expanded;
+
+ if(fromCurrentDirectory && !IsAbsolute()) {
+ source = CurrentDirectory().Join(*this);
+ } else {
+ source = *this;
+ }
+
+ size_t splitAt;
+ FindPrefix(source.raw, &splitAt);
+ if(splitAt != std::string::npos) {
+ expanded.raw = source.raw.substr(0, splitAt);
+ } else {
+ splitAt = 0;
+ }
+
+ std::vector<std::string> expandedComponents;
+ for(std::string component : Split(source.raw.substr(splitAt), SEPARATOR)) {
+ if(component == ".") {
+ // skip
+ } else if(component == "..") {
+ if(!expandedComponents.empty()) {
+ expandedComponents.pop_back();
+ } else {
+ return From("");
+ }
+ } else if(!component.empty()) {
+ expandedComponents.push_back(component);
+ }
+ }
+
+ if(expanded.IsEmpty()) {
+ if(expandedComponents.empty()) {
+ expandedComponents.emplace_back(".");
+ }
+ expanded = From(Concat(expandedComponents, SEPARATOR));
+ } else if(!expandedComponents.empty()) {
+ expanded = expanded.Join(From(Concat(expandedComponents, SEPARATOR)));
+ }
+
+#if defined(WIN32)
+ if(expanded.IsAbsolute() && expanded.raw.substr(0, 2) != "\\\\") {
+ expanded.raw = "\\\\?\\" + expanded.raw;
+ }
+#endif
+
+ return expanded;
+}
+
+static std::string FilesystemNormalize(const std::string &str) {
+#if defined(WIN32)
+ std::wstring strW = Widen(str);
+ std::transform(strW.begin(), strW.end(), strW.begin(), towlower);
+ return Narrow(strW);
+#elif defined(__APPLE__)
+ CFMutableStringRef cfStr =
+ CFStringCreateMutableCopy(NULL, 0,
+ CFStringCreateWithBytesNoCopy(NULL, (const UInt8*)str.data(), str.size(),
+ kCFStringEncodingUTF8, /*isExternalRepresentation=*/false, kCFAllocatorNull));
+ CFStringLowercase(cfStr, NULL);
+ std::string normalizedStr;
+ normalizedStr.resize(CFStringGetMaximumSizeOfFileSystemRepresentation(cfStr));
+ CFStringGetFileSystemRepresentation(cfStr, &normalizedStr[0], normalizedStr.size());
+ normalizedStr.erase(normalizedStr.find('\0'));
+ return normalizedStr;
+#else
+ return str;
+#endif
+}
+
+bool Path::Equals(const Path &other) const {
+ return FilesystemNormalize(raw) == FilesystemNormalize(other.raw);
+}
+
+// Returns a relative path from a given base path.
+// Returns an empty path if any of the paths is not absolute, or
+// if they belong to different roots, or
+// if they cannot be expanded.
+Path Path::RelativeTo(const Path &base) const {
+ Path expanded = Expand();
+ Path baseExpanded = base.Expand();
+ if(!(expanded.IsAbsolute() && baseExpanded.IsAbsolute())){
+ return From("");
+ }
+
+ size_t splitAt;
+ FindPrefix(expanded.raw, &splitAt);
+ size_t baseSplitAt;
+ FindPrefix(baseExpanded.raw, &baseSplitAt);
+ if(FilesystemNormalize(expanded.raw.substr(0, splitAt)) !=
+ FilesystemNormalize(baseExpanded.raw.substr(0, splitAt))) {
+ return From("");
+ }
+
+ std::vector<std::string> components =
+ Split(expanded.raw.substr(splitAt), SEPARATOR);
+ std::vector<std::string> baseComponents =
+ Split(baseExpanded.raw.substr(baseSplitAt), SEPARATOR);
+ size_t common;
+ for(common = 0; common < baseComponents.size() &&
+ common < components.size(); common++) {
+ if(FilesystemNormalize(baseComponents[common]) !=
+ FilesystemNormalize(components[common])) {
+ break;
+ }
+ }
+
+ std::vector<std::string> resultComponents;
+ for(size_t i = common; i < baseComponents.size(); i++) {
+ resultComponents.emplace_back("..");
+ }
+ resultComponents.insert(resultComponents.end(),
+ components.begin() + common, components.end());
+ if(resultComponents.empty()) {
+ resultComponents.emplace_back(".");
+ }
+ return From(Concat(resultComponents, SEPARATOR));
+}
+
+Path Path::FromPortable(const std::string &repr) {
+ return From(Concat(Split(repr, '/'), SEPARATOR));
+}
+
+std::string Path::ToPortable() const {
+ ssassert(!IsAbsolute(), "absolute paths cannot be made portable");
+
+ return Concat(Split(raw, SEPARATOR), '/');
+}
+
+//-----------------------------------------------------------------------------
+// File manipulation.
+//-----------------------------------------------------------------------------
+
+FILE *OpenFile(const Platform::Path &filename, const char *mode) {
+ ssassert(filename.raw.length() == strlen(filename.raw.c_str()),
+ "Unexpected null byte in middle of a path");
+#if defined(WIN32)
+ return _wfopen(Widen(filename.Expand(/*fromCurrentDirectory=*/true).raw).c_str(), Widen(mode).c_str());
+#else
+ return fopen(filename.raw.c_str(), mode);
+#endif
+}
+
+bool FileExists(const Platform::Path &filename) {
+ FILE *f = OpenFile(filename, "rb");
+ if(f == NULL) return false;
+ fclose(f);
+ return true;
+}
+
+void RemoveFile(const Platform::Path &filename) {
+ ssassert(filename.raw.length() == strlen(filename.raw.c_str()),
+ "Unexpected null byte in middle of a path");
+#if defined(WIN32)
+ _wremove(Widen(filename.Expand().raw).c_str());
+#else
+ remove(filename.raw.c_str());
+#endif
+}
+
+bool ReadFile(const Platform::Path &filename, std::string *data) {
+ FILE *f = OpenFile(filename, "rb");
+ if(f == NULL) return false;
+
+ if(fseek(f, 0, SEEK_END) != 0)
+ return false;
+ data->resize(ftell(f));
+ if(fseek(f, 0, SEEK_SET) != 0)
+ return false;
+ if(fread(&(*data)[0], 1, data->size(), f) != data->size())
+ return false;
+ if(fclose(f) != 0)
+ return false;
+
+ return true;
+}
+
+bool WriteFile(const Platform::Path &filename, const std::string &data) {
+ FILE *f = OpenFile(filename, "wb");
+ if(f == NULL) return false;
+
+ if(fwrite(&data[0], 1, data.size(), f) != data.size())
+ return false;
+ if(fclose(f) != 0)
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Loading resources, on Windows.
+//-----------------------------------------------------------------------------
+
+#if defined(WIN32)
+
+const void *LoadResource(const std::string &name, size_t *size) {
+ HRSRC hres = FindResourceW(NULL, Widen(name).c_str(), RT_RCDATA);
+ ssassert(hres != NULL, "Cannot find resource");
+ HGLOBAL res = ::LoadResource(NULL, hres);
+ ssassert(res != NULL, "Cannot load resource");
+
+ *size = SizeofResource(NULL, hres);
+ return LockResource(res);
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Loading resources, on *nix.
+//-----------------------------------------------------------------------------
+
+#if defined(__APPLE__)
+
+static Platform::Path PathFromCFURL(CFURLRef cfUrl) {
+ Path path;
+ CFStringRef cfPath = CFURLCopyFileSystemPath(cfUrl, kCFURLPOSIXPathStyle);
+ path.raw.resize(CFStringGetMaximumSizeOfFileSystemRepresentation(cfPath));
+ CFStringGetFileSystemRepresentation(cfPath, &path.raw[0], path.raw.size());
+ path.raw.erase(path.raw.find('\0'));
+ CFRelease(cfPath);
+ return path;
+}
+
+static Platform::Path ResourcePath(const std::string &name) {
+ Path path;
+
+ // First, try to get the URL from the bundle.
+ CFStringRef cfName = CFStringCreateWithCString(kCFAllocatorDefault, name.c_str(),
+ kCFStringEncodingUTF8);
+ CFURLRef cfUrl = CFBundleCopyResourceURL(CFBundleGetMainBundle(), cfName, NULL, NULL);
+ if(cfUrl != NULL) {
+ path = PathFromCFURL(cfUrl);
+ CFRelease(cfUrl);
+ }
+ CFRelease(cfName);
+
+ if(!path.IsEmpty()) return path;
+
+ // If that failed, it means we aren't running from the bundle.
+ // Reference off the executable path, then.
+ cfUrl = CFBundleCopyExecutableURL(CFBundleGetMainBundle());
+ if(cfUrl != NULL) {
+ path = PathFromCFURL(cfUrl).Parent().Parent().Join("res");
+ path = path.Join(Path::FromPortable(name));
+ CFRelease(cfUrl);
+ }
+
+ return path;
+}
+
+#elif !defined(WIN32)
+
+# if defined(__linux__)
+static const char *selfSymlink = "/proc/self/exe";
+# elif defined(__NetBSD__)
+static const char *selfSymlink = "/proc/curproc/exe";
+# elif defined(__OpenBSD__) || defined(__FreeBSD__)
+static const char *selfSymlink = "/proc/curproc/file";
+# else
+static const char *selfSymlink = "";
+# endif
+
+static Platform::Path FindLocalResourceDir() {
+ // Find out the path to the running binary.
+ Platform::Path selfPath;
+ char *expandedSelfPath = realpath(selfSymlink, NULL);
+ if(expandedSelfPath != NULL) {
+ selfPath = Path::From(expandedSelfPath);
+ }
+ free(expandedSelfPath);
+
+ Platform::Path resourceDir;
+ if(selfPath.IsEmpty()) {
+ // We don't know how to find the local resource directory on this platform,
+ // so use the global one (by returning an empty string).
+ return Path::From(UNIX_DATADIR);
+ } else {
+ resourceDir = selfPath.Parent().Parent().Join("res");
+ }
+
+ struct stat st;
+ if(stat(resourceDir.raw.c_str(), &st) != -1) {
+ // An executable-adjacent resource directory exists, good.
+ return resourceDir;
+ }
+
+ resourceDir = selfPath.Parent().Parent().Join("share").Join("solvespace");
+ if(stat(resourceDir.raw.c_str(), &st) != -1) {
+ // A resource directory exists at a relative path, good.
+ return resourceDir;
+ }
+
+ // No executable-adjacent resource directory; use the one from compile-time prefix.
+ return Path::From(UNIX_DATADIR);
+}
+
+static Platform::Path ResourcePath(const std::string &name) {
+ static Platform::Path resourceDir;
+ if(resourceDir.IsEmpty()) {
+ resourceDir = FindLocalResourceDir();
+ }
+
+ return resourceDir.Join(Path::FromPortable(name));
+}
+
+#endif
+
+#if !defined(WIN32)
+
+const void *LoadResource(const std::string &name, size_t *size) {
+ static std::map<std::string, std::string> cache;
+
+ auto it = cache.find(name);
+ if(it == cache.end()) {
+ ssassert(ReadFile(ResourcePath(name), &cache[name]), "Cannot read resource");
+ it = cache.find(name);
+ }
+
+ const std::string &content = (*it).second;
+ *size = content.size();
+ return (const void*)content.data();
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Startup and command-line argument handling, on Windows.
+//-----------------------------------------------------------------------------
+
+#if defined(WIN32)
+
+std::vector<std::string> InitCli(int argc, char **argv) {
+#if defined(_MSC_VER)
+ // We display our own message on abort; just call ReportFault.
+ _set_abort_behavior(_CALL_REPORTFAULT, _WRITE_ABORT_MSG|_CALL_REPORTFAULT);
+ int crtReportTypes[] = {_CRT_WARN, _CRT_ERROR, _CRT_ASSERT};
+ for(int crtReportType : crtReportTypes) {
+ _CrtSetReportMode(crtReportType, _CRTDBG_MODE_FILE|_CRTDBG_MODE_DEBUG);
+ _CrtSetReportFile(crtReportType, _CRTDBG_FILE_STDERR);
+ }
+#endif
+
+ // Extract the command-line arguments; the ones from main() are ignored,
+ // since they are in the OEM encoding.
+ int argcW;
+ LPWSTR *argvW = CommandLineToArgvW(GetCommandLineW(), &argcW);
+ std::vector<std::string> args;
+ for(int i = 0; i < argcW; i++)
+ args.push_back(Platform::Narrow(argvW[i]));
+ LocalFree(argvW);
+ return args;
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Startup and command-line argument handling, on *nix.
+//-----------------------------------------------------------------------------
+
+#if !defined(WIN32)
+
+std::vector<std::string> InitCli(int argc, char **argv) {
+ return {&argv[0], &argv[argc]};
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Debug output, on Windows.
+//-----------------------------------------------------------------------------
+
+#if defined(WIN32)
+
+void DebugPrint(const char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ int len = _vscprintf(fmt, va) + 1;
+ va_end(va);
+
+ va_start(va, fmt);
+ char *buf = (char *)_alloca(len);
+ _vsnprintf(buf, len, fmt, va);
+ va_end(va);
+
+ // The native version of OutputDebugString, unlike most others,
+ // is OutputDebugStringA.
+ OutputDebugStringA(buf);
+ OutputDebugStringA("\n");
+
+#ifndef NDEBUG
+ // Duplicate to stderr in debug builds, but not in release; this is slow.
+ fputs(buf, stderr);
+ fputc('\n', stderr);
+#endif
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Debug output, on *nix.
+//-----------------------------------------------------------------------------
+
+#if !defined(WIN32)
+
+void DebugPrint(const char *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ fputc('\n', stderr);
+ va_end(va);
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Temporary arena.
+//-----------------------------------------------------------------------------
+
+struct MimallocHeap {
+ mi_heap_t *heap = NULL;
+
+ ~MimallocHeap() {
+ if(heap != NULL)
+ mi_heap_destroy(heap);
+ }
+};
+
+static thread_local MimallocHeap TempArena;
+
+void *AllocTemporary(size_t size) {
+ if(TempArena.heap == NULL) {
+ TempArena.heap = mi_heap_new();
+ ssassert(TempArena.heap != NULL, "out of memory");
+ }
+ void *ptr = mi_heap_zalloc(TempArena.heap, size);
+ ssassert(ptr != NULL, "out of memory");
+ return ptr;
+}
+
+void FreeAllTemporary() {
+ MimallocHeap temp;
+ std::swap(TempArena.heap, temp.heap);
+}
+
+}
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Platform-dependent functionality.
+//
+// Copyright 2017 whitequark
+//-----------------------------------------------------------------------------
+
+#ifndef SOLVESPACE_PLATFORM_H
+#define SOLVESPACE_PLATFORM_H
+
+namespace Platform {
+
+// UTF-8 ⟷ UTF-16 conversion, for Windows.
+#if defined(WIN32)
+std::string Narrow(const wchar_t *s);
+std::wstring Widen(const char *s);
+std::string Narrow(const std::wstring &s);
+std::wstring Widen(const std::string &s);
+#endif
+
+// A filesystem path, respecting the conventions of the current platform.
+// Transformation functions return an empty path on error.
+class Path {
+public:
+ std::string raw;
+
+ static Path From(std::string raw);
+ static Path CurrentDirectory();
+
+ void Clear() { raw.clear(); }
+
+ bool Equals(const Path &other) const;
+ bool IsEmpty() const { return raw.empty(); }
+ bool IsAbsolute() const;
+ bool HasExtension(std::string ext) const;
+
+ std::string FileName() const;
+ std::string FileStem() const;
+ std::string Extension() const;
+
+ Path WithExtension(std::string ext) const;
+ Path Parent() const;
+ Path Join(const std::string &component) const;
+ Path Join(const Path &other) const;
+ Path Expand(bool fromCurrentDirectory = false) const;
+ Path RelativeTo(const Path &base) const;
+
+ // Converting to and from a platform-independent representation
+ // (conventionally, the Unix one).
+ static Path FromPortable(const std::string &repr);
+ std::string ToPortable() const;
+};
+
+struct PathLess {
+ bool operator()(const Path &a, const Path &b) const { return a.raw < b.raw; }
+};
+
+// File manipulation functions.
+bool FileExists(const Platform::Path &filename);
+FILE *OpenFile(const Platform::Path &filename, const char *mode);
+bool ReadFile(const Platform::Path &filename, std::string *data);
+bool WriteFile(const Platform::Path &filename, const std::string &data);
+void RemoveFile(const Platform::Path &filename);
+
+// Resource loading function.
+const void *LoadResource(const std::string &name, size_t *size);
+
+// Startup and command-line argument handling.
+std::vector<std::string> InitCli(int argc, char **argv);
+
+// Debug print function.
+void DebugPrint(const char *fmt, ...);
+
+// Temporary arena functions.
+void *AllocTemporary(size_t size);
+void FreeAllTemporary();
+
+}
+
+#endif
//-----------------------------------------------------------------------------
#include "solvespace.h"
-Vector STriangle::Normal(void) {
+Vector STriangle::Normal() const {
Vector ab = b.Minus(a), bc = c.Minus(b);
return ab.Cross(bc);
}
-double STriangle::MinAltitude(void) {
+double STriangle::MinAltitude() const {
double altA = a.DistanceToLine(b, c.Minus(b)),
altB = b.DistanceToLine(c, a.Minus(c)),
altC = c.DistanceToLine(a, b.Minus(a));
return min(altA, min(altB, altC));
}
-bool STriangle::ContainsPoint(Vector p) {
+bool STriangle::ContainsPoint(Vector p) const {
Vector n = Normal();
if(MinAltitude() < LENGTH_EPS) {
// shouldn't happen; zero-area triangle
return ContainsPointProjd(n.WithMagnitude(1), p);
}
-bool STriangle::ContainsPointProjd(Vector n, Vector p) {
+bool STriangle::ContainsPointProjd(Vector n, Vector p) const {
Vector ab = b.Minus(a), bc = c.Minus(b), ca = a.Minus(c);
Vector no_ab = n.Cross(ab);
return true;
}
-void STriangle::FlipNormal(void) {
+bool STriangle::Raytrace(const Vector &rayPoint, const Vector &rayDir,
+ double *t, Vector *inters) const {
+ // Algorithm from: "Fast, Minimum Storage Ray/Triangle Intersection" by
+ // Tomas Moeller and Ben Trumbore.
+
+ // Find vectors for two edges sharing vertex A.
+ Vector edge1 = b.Minus(a);
+ Vector edge2 = c.Minus(a);
+
+ // Begin calculating determinant - also used to calculate U parameter.
+ Vector pvec = rayDir.Cross(edge2);
+
+ // If determinant is near zero, ray lies in plane of triangle.
+ // Also, cull back facing triangles here.
+ double det = edge1.Dot(pvec);
+ if(-det < LENGTH_EPS) return false;
+ double inv_det = 1.0f / det;
+
+ // Calculate distance from vertex A to ray origin.
+ Vector tvec = rayPoint.Minus(a);
+
+ // Calculate U parameter and test bounds.
+ double u = tvec.Dot(pvec) * inv_det;
+ if (u < 0.0f || u > 1.0f) return false;
+
+ // Prepare to test V parameter.
+ Vector qvec = tvec.Cross(edge1);
+
+ // Calculate V parameter and test bounds.
+ double v = rayDir.Dot(qvec) * inv_det;
+ if (v < 0.0f || u + v > 1.0f) return false;
+
+ // Calculate t, ray intersects triangle.
+ *t = edge2.Dot(qvec) * inv_det;
+
+ // Calculate intersection point.
+ if(inters != NULL) *inters = rayPoint.Plus(rayDir.ScaledBy(*t));
+
+ return true;
+}
+
+double STriangle::SignedVolume() const {
+ return a.Dot(b.Cross(c)) / 6.0;
+}
+
+double STriangle::Area() const {
+ Vector ab = a.Minus(b);
+ Vector cb = c.Minus(b);
+ return ab.Cross(cb).Magnitude() / 2.0;
+}
+
+bool STriangle::IsDegenerate() const {
+ return a.OnLineSegment(b, c) ||
+ b.OnLineSegment(a, c) ||
+ c.OnLineSegment(a, b);
+}
+
+void STriangle::FlipNormal() {
swap(a, b);
swap(an, bn);
}
+STriangle STriangle::Transform(Vector u, Vector v, Vector n) const {
+ STriangle tr = *this;
+ tr.a = tr.a.ScaleOutOfCsys(u, v, n);
+ tr.an = tr.an.ScaleOutOfCsys(u, v, n);
+ tr.b = tr.b.ScaleOutOfCsys(u, v, n);
+ tr.bn = tr.bn.ScaleOutOfCsys(u, v, n);
+ tr.c = tr.c.ScaleOutOfCsys(u, v, n);
+ tr.cn = tr.cn.ScaleOutOfCsys(u, v, n);
+ return tr;
+}
+
STriangle STriangle::From(STriMeta meta, Vector a, Vector b, Vector c) {
STriangle tr = {};
tr.meta = meta;
return se;
}
-bool SEdge::EdgeCrosses(Vector ea, Vector eb, Vector *ppi, SPointList *spl) {
+bool SEdge::EdgeCrosses(Vector ea, Vector eb, Vector *ppi, SPointList *spl) const {
Vector d = eb.Minus(ea);
double t_eps = LENGTH_EPS/d.Magnitude();
if(sqrt(fabs(d.Dot(dthis))) > (m - LENGTH_EPS)) {
// The edges are parallel.
if(fabs(a.DistanceToLine(ea, d)) > LENGTH_EPS) {
- // and not coincident, so can't be interesecting
+ // and not coincident, so can't be intersecting
return false;
}
// The edges are coincident. Make sure that neither endpoint lies
// on the other
bool inters = false;
double t;
- t = a.Minus(ea).DivPivoting(d);
+ t = a.Minus(ea).DivProjected(d);
if(t > t_eps && t < (1 - t_eps)) inters = true;
- t = b.Minus(ea).DivPivoting(d);
+ t = b.Minus(ea).DivProjected(d);
if(t > t_eps && t < (1 - t_eps)) inters = true;
- t = ea.Minus(a).DivPivoting(dthis);
+ t = ea.Minus(a).DivProjected(dthis);
if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;
- t = eb.Minus(a).DivPivoting(dthis);
+ t = eb.Minus(a).DivProjected(dthis);
if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;
if(inters) {
return false;
}
-void SEdgeList::Clear(void) {
+void SEdgeList::Clear() {
l.Clear();
}
-void SEdgeList::AddEdge(Vector a, Vector b, int auxA, int auxB) {
+void SEdgeList::AddEdge(Vector a, Vector b, int auxA, int auxB, int tag) {
SEdge e = {};
+ e.tag = tag;
e.a = a;
e.b = b;
e.auxA = auxA;
}
bool SEdgeList::AssembleContour(Vector first, Vector last, SContour *dest,
- SEdge *errorAt, bool keepDir)
+ SEdge *errorAt, bool keepDir, int start) const
{
int i;
dest->AddPoint(last);
do {
- for(i = 0; i < l.n; i++) {
- SEdge *se = &(l.elem[i]);
+ for(i = start; i < l.n; i++) {
+ /// @todo fix const!
+ SEdge *se = const_cast<SEdge*>(&(l[i]));
if(se->tag) continue;
if(se->a.Equals(last)) {
return true;
}
-bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir) {
+bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir) const {
dest->Clear();
bool allClosed = true;
- for(;;) {
- Vector first = Vector::From(0, 0, 0);
- Vector last = Vector::From(0, 0, 0);
- int i;
- for(i = 0; i < l.n; i++) {
- if(!l.elem[i].tag) {
- first = l.elem[i].a;
- last = l.elem[i].b;
- l.elem[i].tag = 1;
- break;
+ Vector first = Vector::From(0, 0, 0);
+ Vector last = Vector::From(0, 0, 0);
+ int i;
+ for(i = 0; i < l.n; i++) {
+ if(!l[i].tag) {
+ first = l[i].a;
+ last = l[i].b;
+ /// @todo fix const!
+ const_cast<SEdge*>(&(l[i]))->tag = 1;
+ // Create a new empty contour in our polygon, and finish assembling
+ // into that contour.
+ dest->AddEmptyContour();
+ if(!AssembleContour(first, last, dest->l.Last(), errorAt, keepDir, i+1)) {
+ allClosed = false;
}
+ // But continue assembling, even if some of the contours are open
}
- if(i >= l.n) {
- return allClosed;
- }
-
- // Create a new empty contour in our polygon, and finish assembling
- // into that contour.
- dest->AddEmptyContour();
- if(!AssembleContour(first, last, &(dest->l.elem[dest->l.n-1]),
- errorAt, keepDir))
- {
- allClosed = false;
- }
- // But continue assembling, even if some of the contours are open
}
+ return allClosed;
}
//-----------------------------------------------------------------------------
// but they are considered to cross if they are coincident and overlapping.
// If pi is not NULL, then a crossing is returned in that.
//-----------------------------------------------------------------------------
-int SEdgeList::AnyEdgeCrossings(Vector a, Vector b,
- Vector *ppi, SPointList *spl)
-{
- int cnt = 0;
- SEdge *se;
- for(se = l.First(); se; se = l.NextAfter(se)) {
- if(se->EdgeCrosses(a, b, ppi, spl)) {
- cnt++;
- }
- }
- return cnt;
+int SEdgeList::AnyEdgeCrossings(Vector a, Vector b, Vector *ppi, SPointList *spl) const {
+ auto cnt = std::count_if(l.begin(), l.end(),
+ [&](SEdge const &se) { return se.EdgeCrosses(a, b, ppi, spl); });
+ return static_cast<int>(cnt);
}
//-----------------------------------------------------------------------------
// Returns true if the intersecting edge list contains an edge that shares
// an endpoint with one of our edges.
//-----------------------------------------------------------------------------
-bool SEdgeList::ContainsEdgeFrom(SEdgeList *sel) {
- SEdge *se;
- for(se = l.First(); se; se = l.NextAfter(se)) {
+bool SEdgeList::ContainsEdgeFrom(const SEdgeList *sel) const {
+ for(const SEdge *se = l.First(); se; se = l.NextAfter(se)) {
if(sel->ContainsEdge(se)) return true;
}
return false;
}
-bool SEdgeList::ContainsEdge(SEdge *set) {
- SEdge *se;
- for(se = l.First(); se; se = l.NextAfter(se)) {
+bool SEdgeList::ContainsEdge(const SEdge *set) const {
+ for(const SEdge *se = l.First(); se; se = l.NextAfter(se)) {
if((se->a).Equals(set->a) && (se->b).Equals(set->b)) return true;
if((se->b).Equals(set->a) && (se->a).Equals(set->b)) return true;
}
}
//-----------------------------------------------------------------------------
-// Remove unnecessary edges: if two are anti-parallel then remove both, and if
-// two are parallel then remove one.
+// Remove unnecessary edges:
+// - if two are anti-parallel then
+// if both=true, remove both
+// else remove only one.
+// - if two are parallel then remove one.
//-----------------------------------------------------------------------------
-void SEdgeList::CullExtraneousEdges(void) {
+void SEdgeList::CullExtraneousEdges(bool both) {
l.ClearTags();
- int i, j;
- for(i = 0; i < l.n; i++) {
- SEdge *se = &(l.elem[i]);
- for(j = i+1; j < l.n; j++) {
- SEdge *set = &(l.elem[j]);
+ for(int i = 0; i < l.n; i++) {
+ SEdge *se = &(l[i]);
+ for(int j = i + 1; j < l.n; j++) {
+ SEdge *set = &(l[j]);
if((set->a).Equals(se->a) && (set->b).Equals(se->b)) {
// Two parallel edges exist; so keep only the first one.
set->tag = 1;
}
if((set->a).Equals(se->b) && (set->b).Equals(se->a)) {
- // Two anti-parallel edges exist; so keep neither.
- se->tag = 1;
+ // Two anti-parallel edges exist; if both=true, keep neither,
+ // otherwise keep only one.
+ if (both) se->tag = 1;
set->tag = 1;
}
}
// Make a kd-tree of edges. This is used for O(log(n)) implementations of stuff
// that would naively be O(n).
//-----------------------------------------------------------------------------
-SKdNodeEdges *SKdNodeEdges::Alloc(void) {
+SKdNodeEdges *SKdNodeEdges::Alloc() {
SKdNodeEdges *ne = (SKdNodeEdges *)AllocTemporary(sizeof(SKdNodeEdges));
*ne = {};
return ne;
}
-SEdgeLl *SEdgeLl::Alloc(void) {
+SEdgeLl *SEdgeLl::Alloc() {
SEdgeLl *sell = (SEdgeLl *)AllocTemporary(sizeof(SEdgeLl));
*sell = {};
return sell;
}
int SKdNodeEdges::AnyEdgeCrossings(Vector a, Vector b, int cnt,
- Vector *pi, SPointList *spl)
+ Vector *pi, SPointList *spl) const
{
int inters = 0;
if(gt && lt) {
// We have an edge list that contains only collinear edges, maybe with more
// splits than necessary. Merge any collinear segments that join.
//-----------------------------------------------------------------------------
-static Vector LineStart, LineDirection;
-static int ByTAlongLine(const void *av, const void *bv)
-{
- SEdge *a = (SEdge *)av,
- *b = (SEdge *)bv;
-
- double ta = (a->a.Minus(LineStart)).DivPivoting(LineDirection),
- tb = (b->a.Minus(LineStart)).DivPivoting(LineDirection);
-
- return (ta > tb) ? 1 : -1;
-}
void SEdgeList::MergeCollinearSegments(Vector a, Vector b) {
- LineStart = a;
- LineDirection = b.Minus(a);
- qsort(l.elem, l.n, sizeof(l.elem[0]), ByTAlongLine);
+ const Vector lineStart = a;
+ const Vector lineDirection = b.Minus(a);
+ std::sort(l.begin(), l.end(), [&](const SEdge &a, const SEdge &b) {
+ double ta = (a.a.Minus(lineStart)).DivProjected(lineDirection);
+ double tb = (b.a.Minus(lineStart)).DivProjected(lineDirection);
+
+ return (ta < tb);
+ });
l.ClearTags();
- int i;
- for(i = 1; i < l.n; i++) {
- SEdge *prev = &(l.elem[i-1]),
- *now = &(l.elem[i]);
-
- if((prev->b).Equals(now->a) && prev->auxA == now->auxA) {
- // The previous segment joins up to us; so merge it into us.
- prev->tag = 1;
- now->a = prev->a;
+ SEdge *prev = nullptr;
+ for(auto &now : l) {
+ if(prev != nullptr) {
+ if((prev->b).Equals(now.a) && prev->auxA == now.auxA) {
+ // The previous segment joins up to us; so merge it into us.
+ prev->tag = 1;
+ now.a = prev->a;
+ }
}
+ prev = &now;
}
l.RemoveTagged();
}
-void SPointList::Clear(void) {
+void SPointList::Clear() {
l.Clear();
}
-bool SPointList::ContainsPoint(Vector pt) {
+bool SPointList::ContainsPoint(Vector pt) const {
return (IndexForPoint(pt) >= 0);
}
-int SPointList::IndexForPoint(Vector pt) {
+int SPointList::IndexForPoint(Vector pt) const {
int i;
for(i = 0; i < l.n; i++) {
- SPoint *p = &(l.elem[i]);
+ const SPoint *p = &(l[i]);
if(pt.Equals(p->p)) {
return i;
}
l.Add(&sp);
}
-void SContour::MakeEdgesInto(SEdgeList *el) {
+void SContour::MakeEdgesInto(SEdgeList *el) const {
int i;
for(i = 0; i < (l.n - 1); i++) {
- el->AddEdge(l.elem[i].p, l.elem[i+1].p);
+ el->AddEdge(l[i].p, l[i+1].p);
}
}
-void SContour::CopyInto(SContour *dest) {
- SPoint *sp;
- for(sp = l.First(); sp; sp = l.NextAfter(sp)) {
+void SContour::CopyInto(SContour *dest) const {
+ for(const SPoint *sp = l.First(); sp; sp = l.NextAfter(sp)) {
dest->AddPoint(sp->p);
}
}
-void SContour::FindPointWithMinX(void) {
- SPoint *sp;
+void SContour::FindPointWithMinX() {
xminPt = Vector::From(1e10, 1e10, 1e10);
- for(sp = l.First(); sp; sp = l.NextAfter(sp)) {
+ for(const SPoint *sp = l.First(); sp; sp = l.NextAfter(sp)) {
if(sp->p.x < xminPt.x) {
xminPt = sp->p;
}
}
}
-Vector SContour::ComputeNormal(void) {
+Vector SContour::ComputeNormal() const {
Vector n = Vector::From(0, 0, 0);
for(int i = 0; i < l.n - 2; i++) {
- Vector u = (l.elem[i+1].p).Minus(l.elem[i+0].p).WithMagnitude(1);
- Vector v = (l.elem[i+2].p).Minus(l.elem[i+1].p).WithMagnitude(1);
+ Vector u = (l[i+1].p).Minus(l[i+0].p).WithMagnitude(1);
+ Vector v = (l[i+2].p).Minus(l[i+1].p).WithMagnitude(1);
Vector nt = u.Cross(v);
if(nt.Magnitude() > n.Magnitude()) {
n = nt;
return n.WithMagnitude(1);
}
-Vector SContour::AnyEdgeMidpoint(void) {
- if(l.n < 2) oops();
- return ((l.elem[0].p).Plus(l.elem[1].p)).ScaledBy(0.5);
+Vector SContour::AnyEdgeMidpoint() const {
+ ssassert(l.n >= 2, "Need two points to find a midpoint");
+ return ((l[0].p).Plus(l[1].p)).ScaledBy(0.5);
}
-bool SContour::IsClockwiseProjdToNormal(Vector n) {
+bool SContour::IsClockwiseProjdToNormal(Vector n) const {
// Degenerate things might happen as we draw; doesn't really matter
// what we do then.
if(n.Magnitude() < 0.01) return true;
return (SignedAreaProjdToNormal(n) < 0);
}
-double SContour::SignedAreaProjdToNormal(Vector n) {
+double SContour::SignedAreaProjdToNormal(Vector n) const {
// An arbitrary 2d coordinate system that has n as its normal
Vector u = n.Normal(0);
Vector v = n.Normal(1);
double area = 0;
for(int i = 0; i < (l.n - 1); i++) {
- double u0 = (l.elem[i ].p).Dot(u);
- double v0 = (l.elem[i ].p).Dot(v);
- double u1 = (l.elem[i+1].p).Dot(u);
- double v1 = (l.elem[i+1].p).Dot(v);
+ double u0 = (l[i ].p).Dot(u);
+ double v0 = (l[i ].p).Dot(v);
+ double u1 = (l[i+1].p).Dot(u);
+ double v1 = (l[i+1].p).Dot(v);
area += ((v0 + v1)/2)*(u1 - u0);
}
return area;
}
-bool SContour::ContainsPointProjdToNormal(Vector n, Vector p) {
+bool SContour::ContainsPointProjdToNormal(Vector n, Vector p) const {
Vector u = n.Normal(0);
Vector v = n.Normal(1);
bool inside = false;
for(int i = 0; i < (l.n - 1); i++) {
- double ua = (l.elem[i ].p).Dot(u);
- double va = (l.elem[i ].p).Dot(v);
+ double ua = (l[i ].p).Dot(u);
+ double va = (l[i ].p).Dot(v);
// The curve needs to be exactly closed; approximation is death.
- double ub = (l.elem[(i+1)%(l.n-1)].p).Dot(u);
- double vb = (l.elem[(i+1)%(l.n-1)].p).Dot(v);
+ double ub = (l[(i+1)%(l.n-1)].p).Dot(u);
+ double vb = (l[(i+1)%(l.n-1)].p).Dot(v);
if ((((va <= vp) && (vp < vb)) ||
((vb <= vp) && (vp < va))) &&
return inside;
}
-void SContour::Reverse(void) {
+void SContour::Reverse() {
l.Reverse();
}
-void SPolygon::Clear(void) {
+void SPolygon::Clear() {
int i;
for(i = 0; i < l.n; i++) {
- (l.elem[i]).l.Clear();
+ (l[i]).l.Clear();
}
l.Clear();
}
-void SPolygon::AddEmptyContour(void) {
+void SPolygon::AddEmptyContour() {
SContour c = {};
l.Add(&c);
}
-void SPolygon::MakeEdgesInto(SEdgeList *el) {
+void SPolygon::MakeEdgesInto(SEdgeList *el) const {
int i;
for(i = 0; i < l.n; i++) {
- (l.elem[i]).MakeEdgesInto(el);
+ (l[i]).MakeEdgesInto(el);
}
}
-Vector SPolygon::ComputeNormal(void) {
- if(l.n < 1) return Vector::From(0, 0, 0);
- return (l.elem[0]).ComputeNormal();
+Vector SPolygon::ComputeNormal() const {
+ if(l.IsEmpty())
+ return Vector::From(0, 0, 0);
+ return (l[0]).ComputeNormal();
}
-double SPolygon::SignedArea(void) {
- SContour *sc;
+double SPolygon::SignedArea() const {
double area = 0;
// This returns the true area only if the contours are all oriented
// correctly, with the holes backwards from the outer contours.
- for(sc = l.First(); sc; sc = l.NextAfter(sc)) {
+ for(const SContour *sc = l.First(); sc; sc = l.NextAfter(sc)) {
area += sc->SignedAreaProjdToNormal(normal);
}
return area;
}
-bool SPolygon::ContainsPoint(Vector p) {
+bool SPolygon::ContainsPoint(Vector p) const {
return (WindingNumberForPoint(p) % 2) == 1;
}
-int SPolygon::WindingNumberForPoint(Vector p) {
- int winding = 0;
- int i;
- for(i = 0; i < l.n; i++) {
- SContour *sc = &(l.elem[i]);
- if(sc->ContainsPointProjdToNormal(normal, p)) {
- winding++;
- }
- }
+size_t SPolygon::WindingNumberForPoint(Vector p) const {
+ auto winding = std::count_if(l.begin(), l.end(), [&](const SContour &sc) {
+ return sc.ContainsPointProjdToNormal(normal, p);
+ });
return winding;
}
-void SPolygon::FixContourDirections(void) {
+void SPolygon::FixContourDirections() {
// At output, the contour's tag will be 1 if we reversed it, else 0.
l.ClearTags();
// Outside curve looks counterclockwise, projected against our normal.
int i, j;
for(i = 0; i < l.n; i++) {
- SContour *sc = &(l.elem[i]);
+ SContour *sc = &(l[i]);
if(sc->l.n < 2) continue;
// The contours may not intersect, but they may share vertices; so
// testing a vertex for point-in-polygon may fail, but the midpoint
// of an edge is okay.
- Vector pt = (((sc->l.elem[0]).p).Plus(sc->l.elem[1].p)).ScaledBy(0.5);
+ Vector pt = (((sc->l[0]).p).Plus(sc->l[1].p)).ScaledBy(0.5);
sc->timesEnclosed = 0;
bool outer = true;
for(j = 0; j < l.n; j++) {
if(i == j) continue;
- SContour *sct = &(l.elem[j]);
+ SContour *sct = &(l[j]);
if(sct->ContainsPointProjdToNormal(normal, pt)) {
outer = !outer;
(sc->timesEnclosed)++;
}
}
-bool SPolygon::IsEmpty(void) {
- if(l.n == 0 || l.elem[0].l.n == 0) return true;
+bool SPolygon::IsEmpty() const {
+ if(l.IsEmpty() || l[0].l.IsEmpty())
+ return true;
return false;
}
-Vector SPolygon::AnyPoint(void) {
- if(IsEmpty()) oops();
- return l.elem[0].l.elem[0].p;
+Vector SPolygon::AnyPoint() const {
+ ssassert(!IsEmpty(), "Need at least one point");
+ return l[0].l[0].p;
}
-bool SPolygon::SelfIntersecting(Vector *intersectsAt) {
+bool SPolygon::SelfIntersecting(Vector *intersectsAt) const {
SEdgeList el = {};
MakeEdgesInto(&el);
SKdNodeEdges *kdtree = SKdNodeEdges::From(&el);
return ret;
}
+void SPolygon::InverseTransformInto(SPolygon *sp, Vector u, Vector v, Vector n) const {
+ for(const SContour &sc : l) {
+ SContour tsc = {};
+ tsc.timesEnclosed = sc.timesEnclosed;
+ for(const SPoint &sp : sc.l) {
+ tsc.AddPoint(sp.p.DotInToCsys(u, v, n));
+ }
+ sp->l.Add(&tsc);
+ }
+}
+
//-----------------------------------------------------------------------------
// Low-quality routines to cutter radius compensate a polygon. Assumes the
// polygon is in the xy plane, and the contours all go in the right direction
// with respect to normal (0, 0, -1).
//-----------------------------------------------------------------------------
-void SPolygon::OffsetInto(SPolygon *dest, double r) {
+void SPolygon::OffsetInto(SPolygon *dest, double r) const {
int i;
dest->Clear();
for(i = 0; i < l.n; i++) {
- SContour *sc = &(l.elem[i]);
+ const SContour *sc = &(l[i]);
dest->AddEmptyContour();
- sc->OffsetInto(&(dest->l.elem[dest->l.n-1]), r);
+ sc->OffsetInto(&(dest->l[dest->l.n-1]), r);
}
}
//-----------------------------------------------------------------------------
return true;
}
-void SContour::OffsetInto(SContour *dest, double r) {
+void SContour::OffsetInto(SContour *dest, double r) const {
int i;
for(i = 0; i < l.n; i++) {
Vector dp, dn;
double thetan, thetap;
- a = l.elem[WRAP(i-1, (l.n-1))].p;
- b = l.elem[WRAP(i, (l.n-1))].p;
- c = l.elem[WRAP(i+1, (l.n-1))].p;
+ a = l[WRAP(i-1, (l.n-1))].p;
+ b = l[WRAP(i, (l.n-1))].p;
+ c = l[WRAP(i+1, (l.n-1))].p;
dp = a.Minus(b);
thetap = atan2(dp.y, dp.x);
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
-#ifndef __POLYGON_H
-#define __POLYGON_H
+#ifndef SOLVESPACE_POLYGON_H
+#define SOLVESPACE_POLYGON_H
class SPointList;
class SPolygon;
class SBsp3;
class SOutlineList;
+enum class EarType : uint32_t {
+ UNKNOWN = 0,
+ NOT_EAR = 1,
+ EAR = 2
+};
+
+enum class BspClass : uint32_t {
+ POS = 100,
+ NEG = 101,
+ COPLANAR = 200
+};
+
+enum class EdgeKind : uint32_t {
+ NAKED_OR_SELF_INTER = 100,
+ SELF_INTER = 200,
+ TURNING = 300,
+ EMPHASIZED = 400,
+ SHARP = 500,
+};
+
class SEdge {
public:
int tag;
Vector a, b;
static SEdge From(Vector a, Vector b);
- bool EdgeCrosses(Vector a, Vector b, Vector *pi=NULL, SPointList *spl=NULL);
+ bool EdgeCrosses(Vector a, Vector b, Vector *pi=NULL, SPointList *spl=NULL) const;
};
class SEdgeList {
public:
List<SEdge> l;
- void Clear(void);
- void AddEdge(Vector a, Vector b, int auxA=0, int auxB=0);
- bool AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir=false);
+ void Clear();
+ void AddEdge(Vector a, Vector b, int auxA=0, int auxB=0, int tag=0);
+ bool AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir=false) const;
bool AssembleContour(Vector first, Vector last, SContour *dest,
- SEdge *errorAt, bool keepDir);
+ SEdge *errorAt, bool keepDir, int start) const;
int AnyEdgeCrossings(Vector a, Vector b,
- Vector *pi=NULL, SPointList *spl=NULL);
- bool ContainsEdgeFrom(SEdgeList *sel);
- bool ContainsEdge(SEdge *se);
- void CullExtraneousEdges(void);
+ Vector *pi=NULL, SPointList *spl=NULL) const;
+ bool ContainsEdgeFrom(const SEdgeList *sel) const;
+ bool ContainsEdge(const SEdge *se) const;
+ void CullExtraneousEdges(bool both=true);
void MergeCollinearSegments(Vector a, Vector b);
};
SEdge *se;
SEdgeLl *next;
- static SEdgeLl *Alloc(void);
+ static SEdgeLl *Alloc();
};
class SKdNodeEdges {
static SKdNodeEdges *From(SEdgeList *sel);
static SKdNodeEdges *From(SEdgeLl *sell);
- static SKdNodeEdges *Alloc(void);
+ static SKdNodeEdges *Alloc();
int AnyEdgeCrossings(Vector a, Vector b, int cnt,
- Vector *pi=NULL, SPointList *spl=NULL);
+ Vector *pi=NULL, SPointList *spl=NULL) const;
};
class SPoint {
public:
int tag;
- enum {
- UNKNOWN = 0,
- NOT_EAR = 1,
- EAR = 2
- };
- int ear;
+ EarType ear;
Vector p;
Vector auxv;
public:
List<SPoint> l;
- void Clear(void);
- bool ContainsPoint(Vector pt);
- int IndexForPoint(Vector pt);
+ void Clear();
+ bool ContainsPoint(Vector pt) const;
+ int IndexForPoint(Vector pt) const;
void IncrementTagFor(Vector pt);
void Add(Vector pt);
};
List<SPoint> l;
void AddPoint(Vector p);
- void MakeEdgesInto(SEdgeList *el);
- void Reverse(void);
- Vector ComputeNormal(void);
- double SignedAreaProjdToNormal(Vector n);
- bool IsClockwiseProjdToNormal(Vector n);
- bool ContainsPointProjdToNormal(Vector n, Vector p);
- void OffsetInto(SContour *dest, double r);
- void CopyInto(SContour *dest);
- void FindPointWithMinX(void);
- Vector AnyEdgeMidpoint(void);
-
- bool IsEar(int bp, double scaledEps);
+ void MakeEdgesInto(SEdgeList *el) const;
+ void Reverse();
+ Vector ComputeNormal() const;
+ double SignedAreaProjdToNormal(Vector n) const;
+ bool IsClockwiseProjdToNormal(Vector n) const;
+ bool ContainsPointProjdToNormal(Vector n, Vector p) const;
+ void OffsetInto(SContour *dest, double r) const;
+ void CopyInto(SContour *dest) const;
+ void FindPointWithMinX();
+ Vector AnyEdgeMidpoint() const;
+
+ bool IsEmptyTriangle(int ap, int bp, int cp, double scaledEPS) const;
+ bool IsEar(int bp, double scaledEps) const;
bool BridgeToContour(SContour *sc, SEdgeList *el, List<Vector> *vl);
void ClipEarInto(SMesh *m, int bp, double scaledEps);
void UvTriangulateInto(SMesh *m, SSurface *srf);
List<SContour> l;
Vector normal;
- Vector ComputeNormal(void);
- void AddEmptyContour(void);
- int WindingNumberForPoint(Vector p);
- double SignedArea(void);
- bool ContainsPoint(Vector p);
- void MakeEdgesInto(SEdgeList *el);
- void FixContourDirections(void);
- void Clear(void);
- bool SelfIntersecting(Vector *intersectsAt);
- bool IsEmpty(void);
- Vector AnyPoint(void);
- void OffsetInto(SPolygon *dest, double r);
+ Vector ComputeNormal() const;
+ void AddEmptyContour();
+ size_t WindingNumberForPoint(Vector p) const;
+ double SignedArea() const;
+ bool ContainsPoint(Vector p) const;
+ void MakeEdgesInto(SEdgeList *el) const;
+ void FixContourDirections();
+ void Clear();
+ bool SelfIntersecting(Vector *intersectsAt) const;
+ bool IsEmpty() const;
+ Vector AnyPoint() const;
+ void OffsetInto(SPolygon *dest, double r) const;
void UvTriangulateInto(SMesh *m, SSurface *srf);
void UvGridTriangulateInto(SMesh *m, SSurface *srf);
+ void TriangulateInto(SMesh *m) const;
+ void InverseTransformInto(SPolygon *sp, Vector u, Vector v, Vector n) const;
};
class STriangle {
public:
int tag;
STriMeta meta;
- Vector a, b, c;
- Vector an, bn, cn;
+
+ union {
+ struct { Vector a, b, c; };
+ Vector vertices[3];
+ };
+
+ union {
+ struct { Vector an, bn, cn; };
+ Vector normals[3];
+ };
static STriangle From(STriMeta meta, Vector a, Vector b, Vector c);
- Vector Normal(void);
- void FlipNormal(void);
- double MinAltitude(void);
- int WindingNumberForPoint(Vector p);
- bool ContainsPoint(Vector p);
- bool ContainsPointProjd(Vector n, Vector p);
+ Vector Normal() const;
+ void FlipNormal();
+ double MinAltitude() const;
+ bool ContainsPoint(Vector p) const;
+ bool ContainsPointProjd(Vector n, Vector p) const;
+ STriangle Transform(Vector o, Vector u, Vector v) const;
+ bool Raytrace(const Vector &rayPoint, const Vector &rayDir,
+ double *t, Vector *inters) const;
+ double SignedVolume() const;
+ double Area() const;
+ bool IsDegenerate() const;
};
class SBsp2 {
SBsp2 *more;
- enum { POS = 100, NEG = 101, COPLANAR = 200 };
- void InsertTriangleHow(int how, STriangle *tr, SMesh *m, SBsp3 *bsp3);
+ void InsertTriangleHow(BspClass how, STriangle *tr, SMesh *m, SBsp3 *bsp3);
void InsertTriangle(STriangle *tr, SMesh *m, SBsp3 *bsp3);
- Vector IntersectionWith(Vector a, Vector b);
+ Vector IntersectionWith(Vector a, Vector b) const;
void InsertEdge(SEdge *nedge, Vector nnp, Vector out);
- static SBsp2 *InsertOrCreateEdge(SBsp2 *where, SEdge *nedge, Vector nnp, Vector out);
- static SBsp2 *Alloc(void);
-
- void DebugDraw(Vector n, double d);
+ static SBsp2 *InsertOrCreateEdge(SBsp2 *where, SEdge *nedge,
+ Vector nnp, Vector out);
+ static SBsp2 *Alloc();
};
class SBsp3 {
SBsp2 *edges;
- static SBsp3 *Alloc(void);
- static SBsp3 *FromMesh(SMesh *m);
+ static SBsp3 *Alloc();
+ static SBsp3 *FromMesh(const SMesh *m);
- Vector IntersectionWith(Vector a, Vector b);
+ Vector IntersectionWith(Vector a, Vector b) const;
- enum { POS = 100, NEG = 101, COPLANAR = 200 };
- void InsertHow(int how, STriangle *str, SMesh *instead);
+ void InsertHow(BspClass how, STriangle *str, SMesh *instead);
void Insert(STriangle *str, SMesh *instead);
static SBsp3 *InsertOrCreate(SBsp3 *where, STriangle *str, SMesh *instead);
- void InsertConvexHow(int how, STriMeta meta, Vector *vertex, int n,
+ void InsertConvexHow(BspClass how, STriMeta meta, Vector *vertex, size_t n,
SMesh *instead);
- SBsp3 *InsertConvex(STriMeta meta, Vector *vertex, int n, SMesh *instead);
+ SBsp3 *InsertConvex(STriMeta meta, Vector *vertex, size_t n, SMesh *instead);
void InsertInPlane(bool pos2, STriangle *tr, SMesh *m);
- void GenerateInPaintOrder(SMesh *m);
-
- void DebugDraw(void);
+ void GenerateInPaintOrder(SMesh *m) const;
};
class SMesh {
List<STriangle> l;
bool flipNormal;
+ bool keepInsideOtherShell;
bool keepCoplanar;
bool atLeastOneDiscarded;
bool isTransparent;
- void Clear(void);
- void AddTriangle(STriangle *st);
+ void Clear();
+ void AddTriangle(const STriangle *st);
void AddTriangle(STriMeta meta, Vector a, Vector b, Vector c);
- void AddTriangle(STriMeta meta, Vector n, Vector a, Vector b, Vector c);
- void DoBounding(Vector v, Vector *vmax, Vector *vmin);
- void GetBounding(Vector *vmax, Vector *vmin);
+ void AddTriangle(STriMeta meta, Vector n,
+ Vector a, Vector b, Vector c);
+ void DoBounding(Vector v, Vector *vmax, Vector *vmin) const;
+ void GetBounding(Vector *vmax, Vector *vmin) const;
void Simplify(int start);
void AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3);
void MakeFromUnionOf(SMesh *a, SMesh *b);
void MakeFromDifferenceOf(SMesh *a, SMesh *b);
+ void MakeFromIntersectionOf(SMesh *a, SMesh *b);
void MakeFromCopyOf(SMesh *a);
- void MakeFromTransformationOf(SMesh *a,
- Vector trans, Quaternion q, double scale);
+ void MakeFromTransformationOf(SMesh *a, Vector trans,
+ Quaternion q, double scale);
void MakeFromAssemblyOf(SMesh *a, SMesh *b);
void MakeEdgesInPlaneInto(SEdgeList *sel, Vector n, double d);
- void MakeCertainEdgesAndOutlinesInto(SEdgeList *sel, SOutlineList *sol, int type);
+ void MakeOutlinesInto(SOutlineList *sol, EdgeKind type);
+
+ void PrecomputeTransparency();
+ void RemoveDegenerateTriangles();
+ double CalculateVolume() const;
+ double CalculateSurfaceArea(const std::vector<uint32_t> &faces) const;
- bool IsEmpty(void);
+ bool IsEmpty() const;
void RemapFaces(Group *g, int remap);
- uint32_t FirstIntersectionWith(Point2d mp);
+ uint32_t FirstIntersectionWith(Point2d mp) const;
+
+ Vector GetCenterOfMass() const;
};
// A linked list of triangles
STriangleLl *next;
- static STriangleLl *Alloc(void);
+ static STriangleLl *Alloc();
};
class SOutline {
public:
int tag;
Vector a, b, nl, nr;
+
+ bool IsVisible(Vector projDir) const;
};
class SOutlineList {
List<SOutline> l;
void Clear();
- void AddEdge(Vector a, Vector b, Vector nl, Vector nr);
+ void AddEdge(Vector a, Vector b, Vector nl, Vector nr, int tag = 0);
+ void ListTaggedInto(SEdgeList *el, int auxA = 0, int auxB = 0);
void MakeFromCopyOf(SOutlineList *ol);
-
- void FillOutlineTags(Vector projDir);
};
class SKdNode {
STriangleLl *tris;
- static SKdNode *Alloc(void);
+ static SKdNode *Alloc();
static SKdNode *From(SMesh *m);
static SKdNode *From(STriangleLl *tll);
void AddTriangle(STriangle *tr);
- void MakeMeshInto(SMesh *m);
- void ListTrianglesInto(std::vector<STriangle *> *tl);
- void ClearTags(void);
-
- void FindEdgeOn(Vector a, Vector b, int cnt, bool coplanarIsInter, EdgeOnInfo *info);
- enum {
- NAKED_OR_SELF_INTER_EDGES = 100,
- SELF_INTER_EDGES = 200,
- TURNING_EDGES = 300,
- EMPHASIZED_EDGES = 400,
- SHARP_EDGES = 500,
- };
- void MakeCertainEdgesInto(SEdgeList *sel, int how, bool coplanarIsInter,
- bool *inter, bool *leaky, int auxA=0);
- void MakeOutlinesInto(SOutlineList *sel);
+ void MakeMeshInto(SMesh *m) const;
+ void ListTrianglesInto(std::vector<STriangle *> *tl) const;
+ void ClearTags() const;
+
+ void FindEdgeOn(Vector a, Vector b, int cnt, bool coplanarIsInter, EdgeOnInfo *info) const;
+ void MakeCertainEdgesInto(SEdgeList *sel, EdgeKind how, bool coplanarIsInter,
+ bool *inter, bool *leaky, int auxA = 0) const;
+ void MakeOutlinesInto(SOutlineList *sel, EdgeKind tagKind) const;
- void OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt, bool removeHidden);
- void SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr, bool removeHidden);
+ void OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) const;
+ void SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr) const;
void SnapToMesh(SMesh *m);
void SnapToVertex(Vector v, SMesh *extras);
};
+class PolylineBuilder {
+public:
+ struct Edge;
+
+ struct Vertex {
+ Vector pos;
+ std::vector<Edge *> edges;
+
+ bool GetNext(uint32_t kind, Vertex **next, Edge **nextEdge);
+ bool GetNext(uint32_t kind, Vector plane, double d, Vertex **next, Edge **nextEdge);
+ size_t CountEdgesWithTagAndKind(int tag, uint32_t kind) const;
+ };
+
+ struct VertexPairHash {
+ size_t operator()(const std::pair<Vertex *, Vertex *> &v) const;
+ };
+
+ struct Edge {
+ Vertex *a;
+ Vertex *b;
+ uint32_t kind;
+ int tag;
+
+ union {
+ uintptr_t data;
+ SOutline *outline;
+ SEdge *edge;
+ };
+
+ Vertex *GetOtherVertex(Vertex *v) const;
+ bool GetStartAndNext(Vertex **start, Vertex **next, bool loop) const;
+ };
+
+ std::unordered_map<Vector, Vertex *, VectorHash, VectorPred> vertices;
+ std::unordered_map<std::pair<Vertex *, Vertex *>, Edge *, VertexPairHash> edgeMap;
+ std::vector<Edge *> edges;
+
+ ~PolylineBuilder();
+ void Clear();
+
+ Vertex *AddVertex(const Vector &pos);
+ Edge *AddEdge(const Vector &p0, const Vector &p1, uint32_t kind, uintptr_t data = 0);
+ void Generate(std::function<void(Vertex *start, Vertex *next, Edge *edge)> const &startFunc,
+ std::function<void(Vertex *next, Edge *edge)> const &nextFunc,
+ std::function<void(Edge *)> const &aloneFunc,
+ std::function<void()> const &endFunc = []() {});
+
+ void MakeFromEdges(const SEdgeList &sel);
+ void MakeFromOutlines(const SOutlineList &sol);
+ void GenerateEdges(SEdgeList *sel);
+ void GenerateOutlines(SOutlineList *sol);
+};
+
#endif
--- /dev/null
+//-----------------------------------------------------------------------------
+// A helper class to assemble scattered edges into contiguous polylines,
+// as nicely as possible.
+//
+// Copyright 2016 M-Labs Ltd
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+
+bool PolylineBuilder::Vertex::GetNext(uint32_t kind, Vertex **next, Edge **nextEdge) {
+ auto it = std::find_if(edges.begin(), edges.end(), [&](const Edge *e) {
+ return e->tag == 0 && e->kind == kind;
+ });
+
+ if(it != edges.end()) {
+ (*it)->tag = 1;
+ *next = (*it)->GetOtherVertex(this);
+ *nextEdge = *it;
+ return true;
+ }
+
+ return false;
+}
+
+bool PolylineBuilder::Vertex::GetNext(uint32_t kind, Vector plane, double dist,
+ Vertex **next, Edge **nextEdge) {
+ Edge *best = NULL;
+ double minD = VERY_POSITIVE;
+ for(Edge *e : edges) {
+ if(e->tag != 0) continue;
+ if(e->kind != kind) continue;
+
+ // We choose the best next edge with minimal distance from the current plane
+ Vector nextPos = e->GetOtherVertex(this)->pos;
+ double curD = fabs(plane.Dot(nextPos) - dist);
+ if(best != NULL && curD > minD) continue;
+ best = e;
+ minD = curD;
+ }
+
+ if(best != NULL) {
+ best->tag = 1;
+ *next = best->GetOtherVertex(this);
+ *nextEdge = best;
+ return true;
+ }
+
+ return false;
+}
+
+
+size_t PolylineBuilder::Vertex::CountEdgesWithTagAndKind(int tag, uint32_t kind) const {
+ return std::count_if(edges.begin(), edges.end(), [&](const Edge *e) {
+ return e->tag == tag && e->kind == kind;
+ });
+}
+
+PolylineBuilder::Vertex *PolylineBuilder::Edge::GetOtherVertex(PolylineBuilder::Vertex *v) const {
+ if(a == v) return b;
+ if(b == v) return a;
+ return NULL;
+}
+
+size_t PolylineBuilder::VertexPairHash::operator()(const std::pair<Vertex *, Vertex *> &v) const {
+ return ((uintptr_t)v.first / sizeof(Vertex)) ^
+ ((uintptr_t)v.second / sizeof(Vertex));
+}
+
+bool PolylineBuilder::Edge::GetStartAndNext(PolylineBuilder::Vertex **start,
+ PolylineBuilder::Vertex **next, bool loop) const {
+ size_t numA = a->CountEdgesWithTagAndKind(0, kind);
+ size_t numB = b->CountEdgesWithTagAndKind(0, kind);
+
+ if((numA == 1 && numB > 1) || (loop && numA > 1 && numB > 1)) {
+ *start = a;
+ *next = b;
+ return true;
+ }
+
+ if(numA > 1 && numB == 1) {
+ *start = b;
+ *next = a;
+ return true;
+ }
+
+ return false;
+}
+
+PolylineBuilder::~PolylineBuilder() {
+ Clear();
+}
+
+void PolylineBuilder::Clear() {
+ for(Edge *e : edges) {
+ delete e;
+ }
+ edges.clear();
+
+ for(auto &v : vertices) {
+ delete v.second;
+ }
+ vertices.clear();
+}
+
+PolylineBuilder::Vertex *PolylineBuilder::AddVertex(const Vector &pos) {
+ auto it = vertices.find(pos);
+ if(it != vertices.end()) {
+ return it->second;
+ }
+
+ Vertex *result = new Vertex;
+ result->pos = pos;
+ vertices.emplace(pos, result);
+
+ return result;
+}
+
+PolylineBuilder::Edge *PolylineBuilder::AddEdge(const Vector &p0, const Vector &p1,
+ uint32_t kind, uintptr_t data) {
+ Vertex *v0 = AddVertex(p0);
+ Vertex *v1 = AddVertex(p1);
+ if(v0 == v1) return NULL;
+
+ auto it = edgeMap.find(std::make_pair(v0, v1));
+ if(it != edgeMap.end()) {
+ return it->second;
+ }
+
+ PolylineBuilder::Edge *edge = new PolylineBuilder::Edge {};
+ edge->a = v0;
+ edge->b = v1;
+ edge->kind = kind;
+ edge->tag = 0;
+ edge->data = data;
+ edges.push_back(edge);
+ edgeMap.emplace(std::make_pair(v0, v1), edge);
+
+ v0->edges.push_back(edge);
+ v1->edges.push_back(edge);
+
+ return edge;
+}
+
+void PolylineBuilder::Generate(
+ std::function<void(Vertex *start, Vertex *next, Edge *edge)> const &startFunc,
+ std::function<void(Vertex *next, Edge *edge)> const &nextFunc,
+ std::function<void(Edge *alone)> const &aloneFunc, std::function<void()> const &endFunc) {
+ bool found;
+ bool loop = false;
+ do {
+ found = false;
+ for(PolylineBuilder::Edge *e : edges) {
+ if(e->tag != 0) continue;
+
+ Vertex *start;
+ Vertex *next;
+ if(!e->GetStartAndNext(&start, &next, loop)) continue;
+
+ Vector startPos = start->pos;
+ Vector nextPos = next->pos;
+ found = true;
+ e->tag = 1;
+
+ startFunc(start, next, e);
+
+ Edge *nextEdge;
+ if(next->GetNext(e->kind, &next, &nextEdge)) {
+ Vector plane = nextPos.Minus(startPos).Cross(next->pos.Minus(startPos));
+ double dist = plane.Dot(startPos);
+ nextFunc(next, nextEdge);
+ while(next->GetNext(e->kind, plane, dist, &next, &nextEdge)) {
+ nextFunc(next, nextEdge);
+ }
+ }
+
+ endFunc();
+ }
+
+ if(!found && !loop) {
+ loop = true;
+ found = true;
+ }
+ } while(found);
+
+ for(PolylineBuilder::Edge *e : edges) {
+ if(e->tag != 0) continue;
+ aloneFunc(e);
+ }
+}
+
+void PolylineBuilder::MakeFromEdges(const SEdgeList &sel) {
+ for(const SEdge &se : sel.l) {
+ AddEdge(se.a, se.b, (uint32_t)se.auxA, reinterpret_cast<uintptr_t>(&se));
+ }
+}
+
+void PolylineBuilder::MakeFromOutlines(const SOutlineList &ol) {
+ for(const SOutline &so : ol.l) {
+ // Use outline tag as kind, so that emphasized and contour outlines
+ // would not be composed together.
+ AddEdge(so.a, so.b, (uint32_t)so.tag, reinterpret_cast<uintptr_t>(&so));
+ }
+}
+
+void PolylineBuilder::GenerateEdges(SEdgeList *sel) {
+ Vector prev;
+ auto startFunc = [&](Vertex *start, Vertex *next, Edge *e) {
+ sel->AddEdge(start->pos, next->pos, e->kind);
+ prev = next->pos;
+ };
+
+ auto nextFunc = [&](Vertex *next, Edge *e) {
+ sel->AddEdge(prev, next->pos, e->kind);
+ prev = next->pos;
+ };
+
+ auto aloneFunc = [&](Edge *e) {
+ sel->AddEdge(e->a->pos, e->b->pos, e->kind);
+ };
+
+ Generate(startFunc, nextFunc, aloneFunc);
+}
+
+void PolylineBuilder::GenerateOutlines(SOutlineList *sol) {
+ Vector prev;
+ auto startFunc = [&](Vertex *start, Vertex *next, Edge *e) {
+ SOutline *so = e->outline;
+ sol->AddEdge(start->pos, next->pos, so->nl, so->nr, so->tag);
+ prev = next->pos;
+ };
+
+ auto nextFunc = [&](Vertex *next, Edge *e) {
+ SOutline *so = e->outline;
+ sol->AddEdge(prev, next->pos, so->nl, so->nr, so->tag);
+ prev = next->pos;
+ };
+
+ auto aloneFunc = [&](Edge *e) {
+ SOutline *so = e->outline;
+ sol->AddEdge(so->a, so->b, so->nl, so->nr, so->tag);
+ };
+
+ Generate(startFunc, nextFunc, aloneFunc);
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// OpenGL ES 2.0 and OpenGL 3.0 shader interface.
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+#include "gl3shader.h"
+
+namespace SolveSpace {
+
+//-----------------------------------------------------------------------------
+// Floating point data structures
+//-----------------------------------------------------------------------------
+
+Vector2f Vector2f::From(float x, float y) {
+ return { x, y };
+}
+
+Vector2f Vector2f::From(double x, double y) {
+ return { (float)x, (float)y };
+}
+
+Vector2f Vector2f::FromInt(uint32_t x, uint32_t y) {
+ return { (float)x, (float)y };
+}
+
+Vector3f Vector3f::From(float x, float y, float z) {
+ return { x, y, z };
+}
+
+Vector3f Vector3f::From(const Vector &v) {
+ return { (float)v.x, (float)v.y, (float)v.z };
+}
+
+Vector3f Vector3f::From(const RgbaColor &c) {
+ return { c.redF(), c.greenF(), c.blueF() };
+}
+
+Vector4f Vector4f::From(float x, float y, float z, float w) {
+ return { x, y, z, w };
+}
+
+Vector4f Vector4f::From(const Vector &v, float w) {
+ return { (float)v.x, (float)v.y, (float)v.z, w };
+}
+
+Vector4f Vector4f::FromInt(uint32_t x, uint32_t y, uint32_t z, uint32_t w) {
+ return { (float)x, (float)y, (float)z, (float)w };
+}
+
+Vector4f Vector4f::From(const RgbaColor &c) {
+ return { c.redF(), c.greenF(), c.blueF(), c.alphaF() };
+}
+
+//-----------------------------------------------------------------------------
+// Shader manipulation
+//-----------------------------------------------------------------------------
+
+static GLuint CompileShader(const std::string &res, GLenum type) {
+ size_t size;
+ const char *resData = (const char *)Platform::LoadResource(res, &size);
+
+ // Sigh, here we go... We want to deploy to four platforms: Linux, Windows, OS X, mobile+web.
+ // These platforms are basically disjunctive in the OpenGL versions and profiles that they
+ // support: mobile+web support GLES2, Windows can only be guaranteed to support GL1 without
+ // vendor's drivers installed but supports D3D9+ natively, Linux supports GL3.2+ and/or
+ // GLES2+ depending on whether we run on X11 or Wayland, and OS X supports either a legacy
+ // profile or a GL3.2 core profile or (on 10.9+) a GL4.1 core profile.
+ // The platforms barely have a common subset of features:
+ // * mobile+web and Windows (D3D9 through ANGLE) are strictly GLES2/GLSL1.0;
+ // * OS X legacy compatibility profile has GLSL1.2 only shaders, and GL3.2 core profile
+ // that has GLSL1.0 shaders compatible with GLES2 makes mandatory the use of vertex array
+ // objects, which cannot be used in GLES2 at all; similarly GL3.2 core has GL_RED but not
+ // GL_ALPHA whereas GLES2 has GL_ALPHA but not GL_RED.
+ // * GTK does not work on anything prior to GL3.0/GLES2.0; it does not permit explicitly
+ // asking for a compatibility profile, i.e. you can only ask for 3.2+; and it does not
+ // permit asking for a GLES profile prior to GTK 3.22, which will get into Ubuntu
+ // no earlier than late 2017. This is despite the fact that if only GTK defaulted
+ // to the compatibility profile, everything would have just worked as Mesa is
+ // very permissive.
+ // While we're at it, let's remember that GLES2 has *only* glDepthRangef, GL3.2 has *only*
+ // glDepthRange, and GL4.1+ has both glDepthRangef and glDepthRange. Also, that GLSL1.0
+ // makes `precision highp float;` mandatory in fragment shaders, and GLSL1.2 removes
+ // the `precision` keyword entirely, because that's clearly how minor versions work.
+ // Christ, what a trash fire.
+
+ const char *prelude;
+#if defined(HAVE_GLES)
+ prelude = R"(
+#version 100
+#define TEX_ALPHA a
+precision highp float;
+)";
+#else
+ prelude = R"(
+#version 120
+#define TEX_ALPHA r
+)";
+#endif
+ std::string src(resData, size);
+ src = prelude + src;
+
+ GLuint shader = glCreateShader(type);
+ ssassert(shader != 0, "glCreateShader failed");
+
+ const GLint glSize[] = { (int)src.length() };
+ const GLchar* glSource[] = { src.c_str() };
+ glShaderSource(shader, 1, glSource, glSize);
+ glCompileShader(shader);
+
+ GLint infoLen;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+ if(infoLen > 1) {
+ std::string infoStr(infoLen, '\0');
+ glGetShaderInfoLog(shader, infoLen, NULL, &infoStr[0]);
+ dbp(infoStr.c_str());
+ }
+
+ GLint compiled;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if(!compiled) {
+ dbp("Failed to compile shader:\n"
+ "----8<----8<----8<----8<----8<----\n"
+ "%s\n"
+ "----8<----8<----8<----8<----8<----\n",
+ src.c_str());
+ }
+ ssassert(compiled, "Cannot compile shader");
+
+ return shader;
+}
+
+void Shader::Init(const std::string &vertexRes, const std::string &fragmentRes,
+ const std::vector<std::pair<GLuint, std::string> > &locations) {
+ GLuint vert = CompileShader(vertexRes, GL_VERTEX_SHADER);
+ GLuint frag = CompileShader(fragmentRes, GL_FRAGMENT_SHADER);
+
+ program = glCreateProgram();
+ ssassert(program != 0, "glCreateProgram failed");
+
+ glAttachShader(program, vert);
+ glAttachShader(program, frag);
+ for(const auto &l : locations) {
+ glBindAttribLocation(program, l.first, l.second.c_str());
+ }
+ glLinkProgram(program);
+
+ GLint infoLen;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
+ if(infoLen > 1) {
+ std::string infoStr(infoLen, '\0');
+ glGetProgramInfoLog(program, infoLen, NULL, &infoStr[0]);
+ dbp(infoStr.c_str());
+ }
+
+ GLint linked;
+ glGetProgramiv(program, GL_LINK_STATUS, &linked);
+ ssassert(linked, "Cannot link shader");
+}
+
+void Shader::Clear() {
+ glDeleteProgram(program);
+}
+
+void Shader::SetUniformMatrix(const char *name, const double *md) {
+ Enable();
+ float mf[16];
+ for(int i = 0; i < 16; i++) mf[i] = (float)md[i];
+ glUniformMatrix4fv(glGetUniformLocation(program, name), 1, false, mf);
+}
+
+void Shader::SetUniformVector(const char *name, const Vector &v) {
+ Enable();
+ glUniform3f(glGetUniformLocation(program, name), (float)v.x, (float)v.y, (float)v.z);
+}
+
+void Shader::SetUniformVector(const char *name, const Vector4f &v) {
+ Enable();
+ glUniform4f(glGetUniformLocation(program, name), v.x, v.y, v.z, v.w);
+}
+
+void Shader::SetUniformColor(const char *name, RgbaColor c) {
+ Enable();
+ glUniform4f(glGetUniformLocation(program, name), c.redF(), c.greenF(), c.blueF(), c.alphaF());
+}
+
+void Shader::SetUniformFloat(const char *name, float v) {
+ Enable();
+ glUniform1f(glGetUniformLocation(program, name), v);
+}
+
+void Shader::SetUniformInt(const char *name, GLint v) {
+ Enable();
+ glUniform1i(glGetUniformLocation(program, name), v);
+}
+
+void Shader::SetUniformTextureUnit(const char *name, GLint index) {
+ Enable();
+ glUniform1i(glGetUniformLocation(program, name), index);
+}
+
+void Shader::Enable() const {
+ glUseProgram(program);
+}
+
+void Shader::Disable() const {
+ glUseProgram(0);
+}
+
+//-----------------------------------------------------------------------------
+// Mesh rendering
+//-----------------------------------------------------------------------------
+
+void MeshRenderer::Init() {
+ lightShader.Init(
+ "shaders/mesh.vert", "shaders/mesh.frag",
+ {
+ { ATTRIB_POS, "pos" },
+ { ATTRIB_NOR, "nor" },
+ { ATTRIB_COL, "col" },
+ }
+ );
+
+ fillShader.Init(
+ "shaders/mesh_fill.vert", "shaders/mesh_fill.frag",
+ {
+ { ATTRIB_POS, "pos" },
+ }
+ );
+ fillShader.SetUniformTextureUnit("texture_", 0);
+
+ selectedShader = &lightShader;
+}
+
+void MeshRenderer::Clear() {
+ lightShader.Clear();
+ fillShader.Clear();
+}
+
+MeshRenderer::Handle MeshRenderer::Add(const SMesh &m, bool dynamic) {
+ Handle handle;
+ glGenBuffers(1, &handle.vertexBuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
+
+ MeshVertex *vertices = new MeshVertex[m.l.n * 3];
+ for(int i = 0; i < m.l.n; i++) {
+ const STriangle &t = m.l[i];
+ vertices[i * 3 + 0].pos = Vector3f::From(t.a);
+ vertices[i * 3 + 1].pos = Vector3f::From(t.b);
+ vertices[i * 3 + 2].pos = Vector3f::From(t.c);
+
+ if(t.an.EqualsExactly(Vector::From(0, 0, 0))) {
+ Vector3f normal = Vector3f::From(t.Normal());
+ vertices[i * 3 + 0].nor = normal;
+ vertices[i * 3 + 1].nor = normal;
+ vertices[i * 3 + 2].nor = normal;
+ } else {
+ vertices[i * 3 + 0].nor = Vector3f::From(t.an);
+ vertices[i * 3 + 1].nor = Vector3f::From(t.bn);
+ vertices[i * 3 + 2].nor = Vector3f::From(t.cn);
+ }
+
+ for(int j = 0; j < 3; j++) {
+ vertices[i * 3 + j].col = Vector4f::From(t.meta.color);
+ }
+
+ }
+ glBufferData(GL_ARRAY_BUFFER, m.l.n * 3 * sizeof(MeshVertex),
+ vertices, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
+ handle.size = m.l.n * 3;
+ delete []vertices;
+
+ return handle;
+}
+
+void MeshRenderer::Remove(const MeshRenderer::Handle &handle) {
+ glDeleteBuffers(1, &handle.vertexBuffer);
+}
+
+void MeshRenderer::Draw(const MeshRenderer::Handle &handle,
+ bool useColors, RgbaColor overrideColor) {
+ if(handle.size == 0) return;
+
+ selectedShader->Enable();
+
+ glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
+
+ glEnableVertexAttribArray(ATTRIB_POS);
+ glVertexAttribPointer(ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, sizeof(MeshVertex),
+ (void *)offsetof(MeshVertex, pos));
+
+ if(selectedShader == &lightShader) {
+ glEnableVertexAttribArray(ATTRIB_NOR);
+ glVertexAttribPointer(ATTRIB_NOR, 3, GL_FLOAT, GL_FALSE, sizeof(MeshVertex),
+ (void *)offsetof(MeshVertex, nor));
+ if(useColors) {
+ glEnableVertexAttribArray(ATTRIB_COL);
+ glVertexAttribPointer(ATTRIB_COL, 4, GL_FLOAT, GL_FALSE, sizeof(MeshVertex),
+ (void *)offsetof(MeshVertex, col));
+ } else {
+ glVertexAttrib4f(ATTRIB_COL, overrideColor.redF(), overrideColor.greenF(), overrideColor.blueF(), overrideColor.alphaF());
+ }
+ }
+
+ glDrawArrays(GL_TRIANGLES, 0, handle.size);
+
+ glDisableVertexAttribArray(ATTRIB_POS);
+ if(selectedShader == &lightShader) {
+ glDisableVertexAttribArray(ATTRIB_NOR);
+ if(useColors) glDisableVertexAttribArray(ATTRIB_COL);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ selectedShader->Disable();
+}
+
+void MeshRenderer::Draw(const SMesh &mesh, bool useColors, RgbaColor overrideColor) {
+ Handle handle = Add(mesh, /*dynamic=*/true);
+ Draw(handle, useColors, overrideColor);
+ Remove(handle);
+}
+
+void MeshRenderer::SetModelview(double *matrix) {
+ lightShader.SetUniformMatrix("modelview", matrix);
+ fillShader.SetUniformMatrix("modelview", matrix);
+}
+
+void MeshRenderer::SetProjection(double *matrix) {
+ lightShader.SetUniformMatrix("projection", matrix);
+ fillShader.SetUniformMatrix("projection", matrix);
+}
+
+void MeshRenderer::UseShaded(const Lighting &lighting) {
+ Vector dir0 = lighting.lightDirection[0];
+ Vector dir1 = lighting.lightDirection[1];
+ dir0.z = -dir0.z;
+ dir1.z = -dir1.z;
+
+ lightShader.SetUniformVector("lightDir0", dir0);
+ lightShader.SetUniformFloat("lightInt0", (float)lighting.lightIntensity[0]);
+ lightShader.SetUniformVector("lightDir1", dir1);
+ lightShader.SetUniformFloat("lightInt1", (float)lighting.lightIntensity[1]);
+ lightShader.SetUniformFloat("ambient", (float)lighting.ambientIntensity);
+ selectedShader = &lightShader;
+}
+
+void MeshRenderer::UseFilled(const Canvas::Fill &fill) {
+ fillShader.SetUniformColor("color", fill.color);
+ selectedShader = &fillShader;
+}
+
+//-----------------------------------------------------------------------------
+// Arrangement of stipple patterns into textures
+//-----------------------------------------------------------------------------
+
+static double Frac(double x) {
+ return x - floor(x);
+}
+
+static RgbaColor EncodeLengthAsFloat(double v) {
+ v = max(0.0, min(1.0, v));
+ double er = v;
+ double eg = Frac(255.0 * v);
+ double eb = Frac(65025.0 * v);
+ double ea = Frac(160581375.0 * v);
+
+ double r = er - eg / 255.0;
+ double g = eg - eb / 255.0;
+ double b = eb - ea / 255.0;
+ return RgbaColor::From((int)floor( r * 255.0 + 0.5),
+ (int)floor( g * 255.0 + 0.5),
+ (int)floor( b * 255.0 + 0.5),
+ (int)floor(ea * 255.0 + 0.5));
+}
+
+GLuint Generate(const std::vector<double> &pattern) {
+ double patternLen = 0.0;
+ for(double s : pattern) {
+ patternLen += s;
+ }
+
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+
+ GLint size;
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
+ size /= 2;
+
+ RgbaColor *textureData = new RgbaColor[size];
+ int mipCount = (int)log2(size) + 1;
+ for(int mip = 0; mip < mipCount; mip++) {
+ int dashI = 0;
+ double dashT = 0.0;
+ for(int i = 0; i < size; i++) {
+ if(pattern.empty()) {
+ textureData[i] = EncodeLengthAsFloat(0.0);
+ continue;
+ }
+
+ double t = (double)i / (double)(size - 1);
+ while(t - LENGTH_EPS > dashT + pattern[dashI] / patternLen) {
+ dashT += pattern[dashI] / patternLen;
+ dashI++;
+ }
+ double dashW = pattern[dashI] / patternLen;
+ if(dashI % 2 == 0) {
+ textureData[i] = EncodeLengthAsFloat(0.0);
+ } else {
+ double value;
+ if(t - dashT < pattern[dashI] / patternLen / 2.0) {
+ value = t - dashT;
+ } else {
+ value = dashT + dashW - t;
+ }
+ value = value * patternLen;
+ textureData[i] = EncodeLengthAsFloat(value);
+ }
+ }
+ glTexImage2D(GL_TEXTURE_2D, mip, GL_RGBA, size, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ textureData);
+ size /= 2;
+ }
+ delete []textureData;
+
+ return texture;
+}
+
+void StippleAtlas::Init() {
+ for(uint32_t i = 0; i <= (uint32_t)StipplePattern::LAST; i++) {
+ patterns.push_back(Generate(StipplePatternDashes((StipplePattern)i)));
+ }
+}
+
+void StippleAtlas::Clear() {
+ for(GLuint p : patterns) {
+ glDeleteTextures(1, &p);
+ }
+}
+
+GLint StippleAtlas::GetTexture(StipplePattern pattern) const {
+ return patterns[(uint32_t)pattern];
+}
+
+double StippleAtlas::GetLength(StipplePattern pattern) const {
+ if(pattern == StipplePattern::CONTINUOUS) {
+ return 1.0;
+ }
+ return StipplePatternLength(pattern);
+}
+
+//-----------------------------------------------------------------------------
+// Edge rendering
+//-----------------------------------------------------------------------------
+
+void EdgeRenderer::Init(const StippleAtlas *a) {
+ atlas = a;
+ shader.Init(
+ "shaders/edge.vert", "shaders/edge.frag",
+ {
+ { ATTRIB_POS, "pos" },
+ { ATTRIB_LOC, "loc" },
+ { ATTRIB_TAN, "tgt" }
+ }
+ );
+}
+
+void EdgeRenderer::Clear() {
+ shader.Clear();
+}
+
+EdgeRenderer::Handle EdgeRenderer::Add(const SEdgeList &edges, bool dynamic) {
+ Handle handle;
+ glGenBuffers(1, &handle.vertexBuffer);
+ glGenBuffers(1, &handle.indexBuffer);
+
+ glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle.indexBuffer);
+
+ EdgeVertex *vertices = new EdgeVertex[edges.l.n * 8];
+ uint32_t *indices = new uint32_t[edges.l.n * 6 * 3];
+ double phase = 0.0;
+ uint32_t curVertex = 0;
+ uint32_t curIndex = 0;
+ for(int i = 0; i < edges.l.n; i++) {
+ const SEdge &curr = edges.l[i];
+ const SEdge &next = edges.l[(i + 1) % edges.l.n];
+
+ // 3d positions
+ Vector3f a = Vector3f::From(curr.a);
+ Vector3f b = Vector3f::From(curr.b);
+
+ // tangent
+ Vector3f tan = Vector3f::From(curr.b.Minus(curr.a));
+
+ // length
+ double len = curr.b.Minus(curr.a).Magnitude();
+
+ // make line start cap
+ for(int j = 0; j < 2; j++) {
+ vertices[curVertex + j].pos = a;
+ vertices[curVertex + j].tan = tan;
+ }
+ vertices[curVertex + 0].loc = Vector3f::From(-1.0f, -1.0f, float(phase));
+ vertices[curVertex + 1].loc = Vector3f::From(-1.0f, +1.0f, float(phase));
+
+ indices[curIndex++] = curVertex + 0;
+ indices[curIndex++] = curVertex + 1;
+ indices[curIndex++] = curVertex + 2;
+ indices[curIndex++] = curVertex + 1;
+ indices[curIndex++] = curVertex + 2;
+ indices[curIndex++] = curVertex + 3;
+
+ curVertex += 2;
+
+ // make line body
+ vertices[curVertex + 0].pos = a;
+ vertices[curVertex + 1].pos = a;
+ vertices[curVertex + 2].pos = b;
+ vertices[curVertex + 3].pos = b;
+
+ for(int j = 0; j < 4; j++) {
+ vertices[curVertex + j].tan = tan;
+ }
+
+ vertices[curVertex + 0].loc = Vector3f::From( 0.0f, -1.0f, float(phase));
+ vertices[curVertex + 1].loc = Vector3f::From( 0.0f, +1.0f, float(phase));
+ vertices[curVertex + 2].loc = Vector3f::From( 0.0f, +1.0f, float(phase + len));
+ vertices[curVertex + 3].loc = Vector3f::From( 0.0f, -1.0f, float(phase + len));
+
+ indices[curIndex++] = curVertex + 0;
+ indices[curIndex++] = curVertex + 1;
+ indices[curIndex++] = curVertex + 2;
+ indices[curIndex++] = curVertex + 0;
+ indices[curIndex++] = curVertex + 2;
+ indices[curIndex++] = curVertex + 3;
+
+ curVertex += 4;
+
+ // make line end cap
+ for(int j = 0; j < 2; j++) {
+ vertices[curVertex + j].pos = b;
+ vertices[curVertex + j].tan = tan;
+ }
+
+ vertices[curVertex + 0].loc = Vector3f::From(+1.0, +1.0, float(phase + len));
+ vertices[curVertex + 1].loc = Vector3f::From(+1.0, -1.0, float(phase + len));
+
+ indices[curIndex++] = curVertex - 2;
+ indices[curIndex++] = curVertex - 1;
+ indices[curIndex++] = curVertex;
+ indices[curIndex++] = curVertex - 1;
+ indices[curIndex++] = curVertex;
+ indices[curIndex++] = curVertex + 1;
+
+ curVertex += 2;
+
+ // phase stitching
+ if(curr.a.EqualsExactly(next.a) ||
+ curr.a.EqualsExactly(next.b) ||
+ curr.b.EqualsExactly(next.a) ||
+ curr.b.EqualsExactly(next.b))
+ {
+ phase += len;
+ } else {
+ phase = 0.0;
+ }
+ }
+ handle.size = curIndex;
+ GLenum mode = dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW;
+ glBufferData(GL_ARRAY_BUFFER, curVertex * sizeof(EdgeVertex), vertices, mode);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, curIndex * sizeof(uint32_t), indices, mode);
+ delete []vertices;
+ delete []indices;
+
+ return handle;
+}
+
+void EdgeRenderer::Remove(const EdgeRenderer::Handle &handle) {
+ glDeleteBuffers(1, &handle.vertexBuffer);
+ glDeleteBuffers(1, &handle.indexBuffer);
+}
+
+void EdgeRenderer::Draw(const EdgeRenderer::Handle &handle) {
+ if(handle.size == 0) return;
+
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, atlas->GetTexture(pattern));
+ shader.SetUniformTextureUnit("pattern", 1);
+ shader.SetUniformFloat("patternLen", (float)atlas->GetLength(pattern));
+
+ shader.Enable();
+
+ glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle.indexBuffer);
+
+ glEnableVertexAttribArray(ATTRIB_POS);
+ glEnableVertexAttribArray(ATTRIB_LOC);
+ glEnableVertexAttribArray(ATTRIB_TAN);
+
+ glVertexAttribPointer(ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, sizeof(EdgeVertex), (void *)offsetof(EdgeVertex, pos));
+ glVertexAttribPointer(ATTRIB_LOC, 3, GL_FLOAT, GL_FALSE, sizeof(EdgeVertex), (void *)offsetof(EdgeVertex, loc));
+ glVertexAttribPointer(ATTRIB_TAN, 3, GL_FLOAT, GL_FALSE, sizeof(EdgeVertex), (void *)offsetof(EdgeVertex, tan));
+ glDrawElements(GL_TRIANGLES, handle.size, GL_UNSIGNED_INT, NULL);
+
+ glDisableVertexAttribArray(ATTRIB_POS);
+ glDisableVertexAttribArray(ATTRIB_LOC);
+ glDisableVertexAttribArray(ATTRIB_TAN);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ shader.Disable();
+}
+
+void EdgeRenderer::Draw(const SEdgeList &edges) {
+ Handle handle = Add(edges, /*dynamic=*/true);
+ Draw(handle);
+ Remove(handle);
+}
+
+void EdgeRenderer::SetModelview(const double *matrix) {
+ shader.SetUniformMatrix("modelview", matrix);
+}
+
+void EdgeRenderer::SetProjection(const double *matrix) {
+ shader.SetUniformMatrix("projection", matrix);
+}
+
+void EdgeRenderer::SetStroke(const Canvas::Stroke &stroke, double pixel) {
+ double unitScale = stroke.unit == Canvas::Unit::PX ? pixel : 1.0;
+ shader.SetUniformFloat("width", float(stroke.width * unitScale / 2.0));
+ shader.SetUniformColor("color", stroke.color);
+ shader.SetUniformFloat("patternScale", float(stroke.stippleScale * unitScale * 2.0));
+ shader.SetUniformFloat("pixel", (float)pixel);
+ pattern = stroke.stipplePattern;
+}
+
+//-----------------------------------------------------------------------------
+// Outline rendering
+//-----------------------------------------------------------------------------
+
+void OutlineRenderer::Init(const StippleAtlas *a) {
+ atlas = a;
+ shader.Init(
+ "shaders/outline.vert", "shaders/edge.frag",
+ {
+ { ATTRIB_POS, "pos" },
+ { ATTRIB_LOC, "loc" },
+ { ATTRIB_TAN, "tgt" },
+ { ATTRIB_NOL, "nol" },
+ { ATTRIB_NOR, "nor" }
+ }
+ );
+}
+
+void OutlineRenderer::Clear() {
+ shader.Clear();
+}
+
+OutlineRenderer::Handle OutlineRenderer::Add(const SOutlineList &outlines, bool dynamic) {
+ Handle handle;
+ glGenBuffers(1, &handle.vertexBuffer);
+ glGenBuffers(1, &handle.indexBuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle.indexBuffer);
+
+ OutlineVertex *vertices = new OutlineVertex[outlines.l.n * 8];
+ uint32_t *indices = new uint32_t[outlines.l.n * 6 * 3];
+ double phase = 0.0;
+ uint32_t curVertex = 0;
+ uint32_t curIndex = 0;
+
+ for(int i = 0; i < outlines.l.n; i++) {
+ const SOutline &curr = outlines.l[i];
+ const SOutline &next = outlines.l[(i + 1) % outlines.l.n];
+
+ // 3d positions
+ Vector3f a = Vector3f::From(curr.a);
+ Vector3f b = Vector3f::From(curr.b);
+ Vector3f nl = Vector3f::From(curr.nl);
+ Vector3f nr = Vector3f::From(curr.nr);
+
+ // tangent
+ Vector3f tan = Vector3f::From(curr.b.Minus(curr.a));
+
+ // length
+ double len = curr.b.Minus(curr.a).Magnitude();
+ float tag = (float)curr.tag;
+
+ // make line start cap
+ for(int j = 0; j < 2; j++) {
+ vertices[curVertex + j].pos = a;
+ vertices[curVertex + j].nol = nl;
+ vertices[curVertex + j].nor = nr;
+ vertices[curVertex + j].tan = tan;
+ }
+
+ vertices[curVertex + 0].loc = Vector4f::From(-1.0f, -1.0f, float(phase), (float)tag);
+ vertices[curVertex + 1].loc = Vector4f::From(-1.0f, +1.0f, float(phase), (float)tag);
+
+ indices[curIndex++] = curVertex;
+ indices[curIndex++] = curVertex + 1;
+ indices[curIndex++] = curVertex + 2;
+ indices[curIndex++] = curVertex + 1;
+ indices[curIndex++] = curVertex + 2;
+ indices[curIndex++] = curVertex + 3;
+
+ curVertex += 2;
+
+ // make line body
+ vertices[curVertex + 0].pos = a;
+ vertices[curVertex + 1].pos = a;
+ vertices[curVertex + 2].pos = b;
+ vertices[curVertex + 3].pos = b;
+
+ for(int j = 0; j < 4; j++) {
+ vertices[curVertex + j].nol = nl;
+ vertices[curVertex + j].nor = nr;
+ vertices[curVertex + j].tan = tan;
+ }
+
+ vertices[curVertex + 0].loc = Vector4f::From( 0.0f, -1.0f, float(phase), (float)tag);
+ vertices[curVertex + 1].loc = Vector4f::From( 0.0f, +1.0f, float(phase), (float)tag);
+ vertices[curVertex + 2].loc = Vector4f::From( 0.0f, +1.0f,
+ float(phase + len), (float)tag);
+ vertices[curVertex + 3].loc = Vector4f::From( 0.0f, -1.0f,
+ float(phase + len), (float)tag);
+
+ indices[curIndex++] = curVertex + 0;
+ indices[curIndex++] = curVertex + 1;
+ indices[curIndex++] = curVertex + 2;
+ indices[curIndex++] = curVertex + 0;
+ indices[curIndex++] = curVertex + 2;
+ indices[curIndex++] = curVertex + 3;
+
+ curVertex += 4;
+
+ // make line end cap
+ for(int j = 0; j < 2; j++) {
+ vertices[curVertex + j].pos = b;
+ vertices[curVertex + j].nol = nl;
+ vertices[curVertex + j].nor = nr;
+ vertices[curVertex + j].tan = tan;
+ }
+
+ vertices[curVertex + 0].loc = Vector4f::From(+1.0f, +1.0f, float(phase + len), (float)tag);
+ vertices[curVertex + 1].loc = Vector4f::From(+1.0f, -1.0f, float(phase + len), (float)tag);
+
+ indices[curIndex++] = curVertex - 2;
+ indices[curIndex++] = curVertex - 1;
+ indices[curIndex++] = curVertex;
+ indices[curIndex++] = curVertex - 1;
+ indices[curIndex++] = curVertex;
+ indices[curIndex++] = curVertex + 1;
+
+ curVertex += 2;
+
+ // phase stitching
+ if(curr.a.EqualsExactly(next.a) ||
+ curr.a.EqualsExactly(next.b) ||
+ curr.b.EqualsExactly(next.a) ||
+ curr.b.EqualsExactly(next.b))
+ {
+ phase += len;
+ } else {
+ phase = 0.0;
+ }
+ }
+ handle.size = curIndex;
+ GLenum mode = dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW;
+ glBufferData(GL_ARRAY_BUFFER, curVertex * sizeof(OutlineVertex), vertices, mode);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, curIndex * sizeof(uint32_t), indices, mode);
+
+ delete []vertices;
+ delete []indices;
+ return handle;
+}
+
+void OutlineRenderer::Remove(const OutlineRenderer::Handle &handle) {
+ glDeleteBuffers(1, &handle.vertexBuffer);
+ glDeleteBuffers(1, &handle.indexBuffer);
+}
+
+void OutlineRenderer::Draw(const OutlineRenderer::Handle &handle, Canvas::DrawOutlinesAs mode) {
+ if(handle.size == 0) return;
+
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, atlas->GetTexture(pattern));
+ shader.SetUniformTextureUnit("pattern", 1);
+ shader.SetUniformFloat("patternLen", (float)atlas->GetLength(pattern));
+ shader.SetUniformInt("mode", (GLint)mode);
+
+ shader.Enable();
+
+ glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle.indexBuffer);
+
+ glEnableVertexAttribArray(ATTRIB_POS);
+ glEnableVertexAttribArray(ATTRIB_LOC);
+ glEnableVertexAttribArray(ATTRIB_TAN);
+ glEnableVertexAttribArray(ATTRIB_NOL);
+ glEnableVertexAttribArray(ATTRIB_NOR);
+
+ glVertexAttribPointer(ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, sizeof(OutlineVertex),
+ (void *)offsetof(OutlineVertex, pos));
+ glVertexAttribPointer(ATTRIB_LOC, 4, GL_FLOAT, GL_FALSE, sizeof(OutlineVertex),
+ (void *)offsetof(OutlineVertex, loc));
+ glVertexAttribPointer(ATTRIB_TAN, 3, GL_FLOAT, GL_FALSE, sizeof(OutlineVertex),
+ (void *)offsetof(OutlineVertex, tan));
+ glVertexAttribPointer(ATTRIB_NOL, 3, GL_FLOAT, GL_FALSE, sizeof(OutlineVertex),
+ (void *)offsetof(OutlineVertex, nol));
+ glVertexAttribPointer(ATTRIB_NOR, 3, GL_FLOAT, GL_FALSE, sizeof(OutlineVertex),
+ (void *)offsetof(OutlineVertex, nor));
+ glDrawElements(GL_TRIANGLES, handle.size, GL_UNSIGNED_INT, NULL);
+
+ glDisableVertexAttribArray(ATTRIB_POS);
+ glDisableVertexAttribArray(ATTRIB_LOC);
+ glDisableVertexAttribArray(ATTRIB_TAN);
+ glDisableVertexAttribArray(ATTRIB_NOL);
+ glDisableVertexAttribArray(ATTRIB_NOR);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ shader.Disable();
+}
+
+void OutlineRenderer::Draw(const SOutlineList &outlines, Canvas::DrawOutlinesAs drawAs) {
+ Handle handle = Add(outlines, /*dynamic=*/true);
+ Draw(handle, drawAs);
+ Remove(handle);
+}
+
+void OutlineRenderer::SetModelview(const double *matrix) {
+ shader.SetUniformMatrix("modelview", matrix);
+}
+
+void OutlineRenderer::SetProjection(const double *matrix) {
+ shader.SetUniformMatrix("projection", matrix);
+}
+
+void OutlineRenderer::SetStroke(const Canvas::Stroke &stroke, double pixel) {
+ double unitScale = (stroke.unit == Canvas::Unit::PX) ? pixel : 1.0;
+ shader.SetUniformFloat("width", (float)(stroke.width * unitScale / 2.0));
+ shader.SetUniformColor("color", stroke.color);
+ shader.SetUniformFloat("patternScale", (float)(stroke.stippleScale * unitScale * 2.0));
+ shader.SetUniformFloat("pixel", (float)pixel);
+ pattern = stroke.stipplePattern;
+}
+
+//-----------------------------------------------------------------------------
+// Indexed mesh storage
+//-----------------------------------------------------------------------------
+
+void SIndexedMesh::AddPoint(const Vector &p) {
+ uint32_t vstart = vertices.size();
+ vertices.resize(vertices.size() + 4);
+
+ vertices[vstart + 0].pos = Vector3f::From(p);
+ vertices[vstart + 0].tex = Vector2f::From(-1.0f, -1.0f);
+ vertices[vstart + 1].pos = Vector3f::From(p);
+ vertices[vstart + 1].tex = Vector2f::From(+1.0f, -1.0f);
+ vertices[vstart + 2].pos = Vector3f::From(p);
+ vertices[vstart + 2].tex = Vector2f::From(+1.0f, +1.0f);
+ vertices[vstart + 3].pos = Vector3f::From(p);
+ vertices[vstart + 3].tex = Vector2f::From(-1.0f, +1.0f);
+
+ size_t istart = indices.size();
+ indices.resize(indices.size() + 6);
+
+ indices[istart + 0] = vstart + 0;
+ indices[istart + 1] = vstart + 1;
+ indices[istart + 2] = vstart + 2;
+ indices[istart + 3] = vstart + 0;
+ indices[istart + 4] = vstart + 2;
+ indices[istart + 5] = vstart + 3;
+}
+
+void SIndexedMesh::AddQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d) {
+ uint32_t vstart = vertices.size();
+ vertices.resize(vertices.size() + 4);
+
+ vertices[vstart + 0].pos = Vector3f::From(a);
+ vertices[vstart + 1].pos = Vector3f::From(b);
+ vertices[vstart + 2].pos = Vector3f::From(c);
+ vertices[vstart + 3].pos = Vector3f::From(d);
+
+ size_t istart = indices.size();
+ indices.resize(indices.size() + 6);
+
+ indices[istart + 0] = vstart + 0;
+ indices[istart + 1] = vstart + 1;
+ indices[istart + 2] = vstart + 2;
+ indices[istart + 3] = vstart + 0;
+ indices[istart + 4] = vstart + 2;
+ indices[istart + 5] = vstart + 3;
+}
+
+void SIndexedMesh::AddPixmap(const Vector &o, const Vector &u, const Vector &v,
+ const Point2d &ta, const Point2d &tb) {
+ uint32_t vstart = vertices.size();
+ vertices.resize(vertices.size() + 4);
+
+ vertices[vstart + 0].pos = Vector3f::From(o);
+ vertices[vstart + 0].tex = Vector2f::From(ta.x, ta.y);
+
+ vertices[vstart + 1].pos = Vector3f::From(o.Plus(v));
+ vertices[vstart + 1].tex = Vector2f::From(ta.x, tb.y);
+
+ vertices[vstart + 2].pos = Vector3f::From(o.Plus(u).Plus(v));
+ vertices[vstart + 2].tex = Vector2f::From(tb.x, tb.y);
+
+ vertices[vstart + 3].pos = Vector3f::From(o.Plus(u));
+ vertices[vstart + 3].tex = Vector2f::From(tb.x, ta.y);
+
+ size_t istart = indices.size();
+ indices.resize(indices.size() + 6);
+
+ indices[istart + 0] = vstart + 0;
+ indices[istart + 1] = vstart + 1;
+ indices[istart + 2] = vstart + 2;
+ indices[istart + 3] = vstart + 0;
+ indices[istart + 4] = vstart + 2;
+ indices[istart + 5] = vstart + 3;
+}
+
+void SIndexedMesh::Clear() {
+ vertices.clear();
+ indices.clear();
+}
+
+//-----------------------------------------------------------------------------
+// Indexed mesh rendering
+//-----------------------------------------------------------------------------
+
+void IndexedMeshRenderer::Init() {
+ colShader.Init(
+ "shaders/imesh.vert", "shaders/imesh.frag",
+ {
+ { ATTRIB_POS, "pos" }
+ }
+ );
+ texShader.Init(
+ "shaders/imesh_tex.vert", "shaders/imesh_tex.frag",
+ {
+ { ATTRIB_POS, "pos" },
+ { ATTRIB_TEX, "tex" }
+ }
+ );
+ texaShader.Init(
+ "shaders/imesh_tex.vert", "shaders/imesh_texa.frag",
+ {
+ { ATTRIB_POS, "pos" },
+ { ATTRIB_TEX, "tex" }
+ }
+ );
+ pointShader.Init(
+ "shaders/imesh_point.vert", "shaders/imesh_point.frag",
+ {
+ { ATTRIB_POS, "pos" },
+ { ATTRIB_TEX, "loc" }
+ }
+ );
+
+ texShader.SetUniformTextureUnit("texture_", 0);
+ texaShader.SetUniformTextureUnit("texture_", 0);
+ selectedShader = &colShader;
+}
+
+void IndexedMeshRenderer::Clear() {
+ texShader.Clear();
+ texaShader.Clear();
+ colShader.Clear();
+ pointShader.Clear();
+}
+
+IndexedMeshRenderer::Handle IndexedMeshRenderer::Add(const SIndexedMesh &m, bool dynamic) {
+ Handle handle;
+ glGenBuffers(1, &handle.vertexBuffer);
+ glGenBuffers(1, &handle.indexBuffer);
+
+ GLenum mode = dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW;
+ glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
+ glBufferData(GL_ARRAY_BUFFER, m.vertices.size() * sizeof(SIndexedMesh::Vertex),
+ m.vertices.data(), mode);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle.indexBuffer);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, m.indices.size() * sizeof(uint32_t),
+ m.indices.data(), mode);
+ handle.size = m.indices.size();
+ return handle;
+}
+
+void IndexedMeshRenderer::Remove(const IndexedMeshRenderer::Handle &handle) {
+ glDeleteBuffers(1, &handle.vertexBuffer);
+ glDeleteBuffers(1, &handle.indexBuffer);
+}
+
+void IndexedMeshRenderer::Draw(const IndexedMeshRenderer::Handle &handle) {
+ if(handle.size == 0) return;
+
+ selectedShader->Enable();
+
+ glBindBuffer(GL_ARRAY_BUFFER, handle.vertexBuffer);
+ glEnableVertexAttribArray(ATTRIB_POS);
+ glVertexAttribPointer(ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, sizeof(SIndexedMesh::Vertex),
+ (void *)offsetof(SIndexedMesh::Vertex, pos));
+ if(NeedsTexture()) {
+ glEnableVertexAttribArray(ATTRIB_TEX);
+ glVertexAttribPointer(ATTRIB_TEX, 2, GL_FLOAT, GL_FALSE, sizeof(SIndexedMesh::Vertex),
+ (void *)offsetof(SIndexedMesh::Vertex, tex));
+ }
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle.indexBuffer);
+ glDrawElements(GL_TRIANGLES, handle.size, GL_UNSIGNED_INT, NULL);
+
+ glDisableVertexAttribArray(ATTRIB_POS);
+ if(NeedsTexture()) glDisableVertexAttribArray(ATTRIB_TEX);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ selectedShader->Disable();
+}
+
+void IndexedMeshRenderer::Draw(const SIndexedMesh &mesh) {
+ Handle handle = Add(mesh, /*dynamic=*/true) ;
+ Draw(handle);
+ Remove(handle);
+}
+
+bool IndexedMeshRenderer::NeedsTexture() const {
+ return selectedShader == &texShader ||
+ selectedShader == &texaShader ||
+ selectedShader == &pointShader;
+}
+
+void IndexedMeshRenderer::SetModelview(const double *matrix) {
+ colShader.SetUniformMatrix("modelview", matrix);
+ texShader.SetUniformMatrix("modelview", matrix);
+ texaShader.SetUniformMatrix("modelview", matrix);
+ pointShader.SetUniformMatrix("modelview", matrix);
+}
+
+void IndexedMeshRenderer::SetProjection(const double *matrix) {
+ colShader.SetUniformMatrix("projection", matrix);
+ texShader.SetUniformMatrix("projection", matrix);
+ texaShader.SetUniformMatrix("projection", matrix);
+ pointShader.SetUniformMatrix("projection", matrix);
+}
+
+void IndexedMeshRenderer::UseFilled(const Canvas::Fill &fill) {
+ if(fill.texture) {
+ selectedShader = (fill.texture->format == Pixmap::Format::A) ? &texaShader : &texShader;
+ } else {
+ selectedShader = &colShader;
+ }
+ selectedShader->SetUniformColor("color", fill.color);
+}
+
+void IndexedMeshRenderer::UsePoint(const Canvas::Stroke &stroke, double pixel) {
+ pointShader.SetUniformColor("color", stroke.color);
+ pointShader.SetUniformFloat("width", (float)(stroke.width * pixel / 2.0));
+ pointShader.SetUniformFloat("pixel", (float)pixel);
+ selectedShader = &pointShader;
+}
+
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// OpenGL ES 2.0 and OpenGL 3.0 shader interface.
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+#ifndef SOLVESPACE_GL3SHADER_H
+#define SOLVESPACE_GL3SHADER_H
+
+#if defined(WIN32)
+# define GL_APICALL /*static linkage*/
+# define GL_GLEXT_PROTOTYPES
+# include <GLES2/gl2.h>
+# include <GLES2/gl2ext.h>
+# define HAVE_GLES
+#elif defined(__APPLE__)
+# include <OpenGL/gl.h>
+#else
+# define GL_GLEXT_PROTOTYPES
+# include <GL/gl.h>
+# include <GL/glext.h>
+#endif
+
+#if !defined(HAVE_GLES)
+// glDepthRange is in GL1+ but not GLES2, glDepthRangef is in GL4.1+ and GLES2.
+// Consistency!
+# define glClearDepthf glClearDepth
+# define glDepthRangef glDepthRange
+#endif
+
+namespace SolveSpace {
+
+//-----------------------------------------------------------------------------
+// Floating-point data structures; the layout of these must match shaders
+//-----------------------------------------------------------------------------
+
+class Vector2f {
+public:
+ float x, y;
+
+ static Vector2f From(float x, float y);
+ static Vector2f From(double x, double y);
+ static Vector2f FromInt(uint32_t x, uint32_t y);
+};
+
+class Vector3f {
+public:
+ float x, y, z;
+
+ static Vector3f From(float x, float y, float z);
+ static Vector3f From(const Vector &v);
+ static Vector3f From(const RgbaColor &c);
+};
+
+class Vector4f {
+public:
+ float x, y, z, w;
+
+ static Vector4f From(float x, float y, float z, float w);
+ static Vector4f From(const Vector &v, float w);
+ static Vector4f FromInt(uint32_t x, uint32_t y, uint32_t z, uint32_t w);
+ static Vector4f From(const RgbaColor &c);
+};
+
+//-----------------------------------------------------------------------------
+// Wrappers for our shaders
+//-----------------------------------------------------------------------------
+
+class Shader {
+public:
+ GLuint program = 0;
+
+ void Init(const std::string &vertexRes,
+ const std::string &fragmentRes,
+ const std::vector<std::pair<GLuint, std::string>> &locations = {});
+ void Clear();
+
+ void SetUniformMatrix(const char *name, const double *md);
+ void SetUniformVector(const char *name, const Vector &v);
+ void SetUniformVector(const char *name, const Vector4f &v);
+ void SetUniformColor(const char *name, RgbaColor c);
+ void SetUniformFloat(const char *name, float v);
+ void SetUniformInt(const char *name, GLint v);
+ void SetUniformTextureUnit(const char *name, GLint index);
+ void Enable() const;
+ void Disable() const;
+};
+
+class MeshRenderer {
+public:
+ const GLint ATTRIB_POS = 0;
+ const GLint ATTRIB_NOR = 1;
+ const GLint ATTRIB_COL = 2;
+
+ struct MeshVertex {
+ Vector3f pos;
+ Vector3f nor;
+ Vector4f col;
+ };
+
+ struct Handle {
+ GLuint vertexBuffer;
+ GLsizei size;
+ };
+
+ Shader lightShader;
+ Shader fillShader;
+ Shader *selectedShader = NULL;
+
+ void Init();
+ void Clear();
+
+ Handle Add(const SMesh &m, bool dynamic = false);
+ void Remove(const Handle &handle);
+ void Draw(const Handle &handle, bool useColors = true, RgbaColor overrideColor = {});
+ void Draw(const SMesh &mesh, bool useColors = true, RgbaColor overrideColor = {});
+
+ void SetModelview(double *matrix);
+ void SetProjection(double *matrix);
+
+ void UseShaded(const Lighting &lighting);
+ void UseFilled(const Canvas::Fill &fill);
+};
+
+class StippleAtlas {
+public:
+ std::vector<GLint> patterns;
+
+ void Init();
+ void Clear();
+
+ GLint GetTexture(StipplePattern pattern) const;
+ double GetLength(StipplePattern pattern) const;
+};
+
+class EdgeRenderer {
+public:
+ const GLint ATTRIB_POS = 0;
+ const GLint ATTRIB_LOC = 1;
+ const GLint ATTRIB_TAN = 2;
+
+ struct EdgeVertex {
+ Vector3f pos;
+ Vector3f loc;
+ Vector3f tan;
+ };
+
+ struct Handle {
+ GLuint vertexBuffer;
+ GLuint indexBuffer;
+ GLsizei size;
+ };
+
+ Shader shader;
+
+ const StippleAtlas *atlas = NULL;
+ StipplePattern pattern = StipplePattern::CONTINUOUS;
+
+ void Init(const StippleAtlas *atlas);
+ void Clear();
+
+ Handle Add(const SEdgeList &edges, bool dynamic = false);
+ void Remove(const Handle &handle);
+ void Draw(const Handle &handle);
+ void Draw(const SEdgeList &edges);
+
+ void SetModelview(const double *matrix);
+ void SetProjection(const double *matrix);
+ void SetStroke(const Canvas::Stroke &stroke, double pixel);
+};
+
+class OutlineRenderer {
+public:
+ const GLint ATTRIB_POS = 0;
+ const GLint ATTRIB_LOC = 1;
+ const GLint ATTRIB_TAN = 2;
+ const GLint ATTRIB_NOL = 3;
+ const GLint ATTRIB_NOR = 4;
+
+ struct OutlineVertex {
+ Vector3f pos;
+ Vector4f loc;
+ Vector3f tan;
+ Vector3f nol;
+ Vector3f nor;
+ };
+
+ struct Handle {
+ GLuint vertexBuffer;
+ GLuint indexBuffer;
+ GLsizei size;
+ };
+
+ Shader shader;
+
+ const StippleAtlas *atlas = NULL;
+ StipplePattern pattern = StipplePattern::CONTINUOUS;
+
+ void Init(const StippleAtlas *atlas);
+ void Clear();
+
+ Handle Add(const SOutlineList &outlines, bool dynamic = false);
+ void Remove(const Handle &handle);
+ void Draw(const Handle &handle, Canvas::DrawOutlinesAs mode);
+ void Draw(const SOutlineList &outlines, Canvas::DrawOutlinesAs mode);
+
+ void SetModelview(const double *matrix);
+ void SetProjection(const double *matrix);
+ void SetStroke(const Canvas::Stroke &stroke, double pixel);
+};
+
+class SIndexedMesh {
+public:
+ struct Vertex {
+ Vector3f pos;
+ Vector2f tex;
+ };
+
+ std::vector<Vertex> vertices;
+ std::vector<uint32_t> indices;
+
+ void AddPoint(const Vector &p);
+ void AddQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d);
+ void AddPixmap(const Vector &o, const Vector &u, const Vector &v,
+ const Point2d &ta, const Point2d &tb);
+
+ void Clear();
+};
+
+class IndexedMeshRenderer {
+public:
+ const GLint ATTRIB_POS = 0;
+ const GLint ATTRIB_TEX = 1;
+
+ struct Handle {
+ GLuint vertexBuffer;
+ GLuint indexBuffer;
+ GLsizei size;
+ };
+
+ Shader texShader;
+ Shader texaShader;
+ Shader colShader;
+ Shader pointShader;
+
+ Shader *selectedShader = NULL;
+
+ void Init();
+ void Clear();
+
+ Handle Add(const SIndexedMesh &m, bool dynamic = false);
+ void Remove(const Handle &handle);
+ void Draw(const Handle &handle);
+ void Draw(const SIndexedMesh &mesh);
+
+ void SetModelview(const double *matrix);
+ void SetProjection(const double *matrix);
+
+ bool NeedsTexture() const;
+
+ void UseFilled(const Canvas::Fill &fill);
+ void UsePoint(const Canvas::Stroke &stroke, double pixel);
+};
+
+}
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------
+// Backend-agnostic rendering interface, and various backends we use.
+//
+// Copyright 2016 whitequark
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+
+namespace SolveSpace {
+
+//-----------------------------------------------------------------------------
+// Camera transformations.
+//-----------------------------------------------------------------------------
+
+Point2d Camera::ProjectPoint(Vector p) const {
+ Vector p3 = ProjectPoint3(p);
+ Point2d p2 = { p3.x, p3.y };
+ return p2;
+}
+
+Vector Camera::ProjectPoint3(Vector p) const {
+ double w;
+ Vector r = ProjectPoint4(p, &w);
+ return r.ScaledBy(scale/w);
+}
+
+Vector Camera::ProjectPoint4(Vector p, double *w) const {
+ p = p.Plus(offset);
+
+ Vector r;
+ r.x = p.Dot(projRight);
+ r.y = p.Dot(projUp);
+ r.z = p.Dot(projUp.Cross(projRight));
+
+ *w = 1 + r.z*tangent*scale;
+ return r;
+}
+
+Vector Camera::UnProjectPoint(Point2d p) const {
+ Vector orig = offset.ScaledBy(-1);
+
+ // Note that we're ignoring the effects of perspective. Since our returned
+ // point has the same component normal to the screen as the offset, it
+ // will have z = 0 after the rotation is applied, thus w = 1. So this is
+ // correct.
+ orig = orig.Plus(projRight.ScaledBy(p.x / scale)).Plus(
+ projUp. ScaledBy(p.y / scale));
+ return orig;
+}
+
+Vector Camera::UnProjectPoint3(Vector p) const {
+ p.z = p.z / (scale - p.z * tangent * scale);
+ double w = 1 + p.z * tangent * scale;
+ p.x *= w / scale;
+ p.y *= w / scale;
+
+ Vector orig = offset.ScaledBy(-1);
+ orig = orig.Plus(projRight.ScaledBy(p.x)).Plus(
+ projUp. ScaledBy(p.y).Plus(
+ projUp.Cross(projRight). ScaledBy(p.z)));
+ return orig;
+}
+
+Vector Camera::VectorFromProjs(Vector rightUpForward) const {
+ Vector n = projRight.Cross(projUp);
+
+ Vector r = (projRight.ScaledBy(rightUpForward.x));
+ r = r.Plus(projUp.ScaledBy(rightUpForward.y));
+ r = r.Plus(n.ScaledBy(rightUpForward.z));
+ return r;
+}
+
+Vector Camera::AlignToPixelGrid(Vector v) const {
+ if(!gridFit) return v;
+
+ v = ProjectPoint3(v);
+ v.x = floor(v.x) + 0.5;
+ v.y = floor(v.y) + 0.5;
+ return UnProjectPoint3(v);
+}
+
+SBezier Camera::ProjectBezier(SBezier b) const {
+ Quaternion q = Quaternion::From(projRight, projUp);
+ q = q.Inverse();
+ // we want Q*(p - o) = Q*p - Q*o
+ b = b.TransformedBy(q.Rotate(offset).ScaledBy(scale), q, scale);
+ for(int i = 0; i <= b.deg; i++) {
+ Vector4 ct = Vector4::From(b.weight[i], b.ctrl[i]);
+ // so the desired curve, before perspective, is
+ // (x/w, y/w, z/w)
+ // and after perspective is
+ // ((x/w)/(1 - (z/w)*tangent, ...
+ // = (x/(w - z*tangent), ...
+ // so we want to let w' = w - z*tangent
+ ct.w = ct.w - ct.z*tangent;
+
+ b.ctrl[i] = ct.PerspectiveProject();
+ b.weight[i] = ct.w;
+ }
+ return b;
+}
+
+void Camera::LoadIdentity() {
+ offset = { 0.0, 0.0, 0.0 };
+ projRight = { 1.0, 0.0, 0.0 };
+ projUp = { 0.0, 1.0, 0.0 };
+ scale = 1.0;
+ tangent = 0.0;
+}
+
+void Camera::NormalizeProjectionVectors() {
+ if(projRight.Magnitude() < LENGTH_EPS) {
+ projRight = Vector::From(1, 0, 0);
+ }
+
+ Vector norm = projRight.Cross(projUp);
+ // If projRight and projUp somehow ended up parallel, then pick an
+ // arbitrary projUp normal to projRight.
+ if(norm.Magnitude() < LENGTH_EPS) {
+ norm = projRight.Normal(0);
+ }
+ projUp = norm.Cross(projRight);
+
+ projUp = projUp.WithMagnitude(1);
+ projRight = projRight.WithMagnitude(1);
+}
+
+//-----------------------------------------------------------------------------
+// Stroke and fill caching.
+//-----------------------------------------------------------------------------
+
+bool Canvas::Stroke::Equals(const Stroke &other) const {
+ return (layer == other.layer &&
+ zIndex == other.zIndex &&
+ color.Equals(other.color) &&
+ width == other.width &&
+ unit == other.unit &&
+ stipplePattern == other.stipplePattern &&
+ stippleScale == other.stippleScale);
+}
+
+double Canvas::Stroke::WidthMm(const Camera &camera) const {
+ switch(unit) {
+ case Canvas::Unit::MM:
+ return width;
+ case Canvas::Unit::PX:
+ return width / camera.scale;
+ default:
+ ssassert(false, "Unexpected unit");
+ }
+}
+
+double Canvas::Stroke::WidthPx(const Camera &camera) const {
+ switch(unit) {
+ case Canvas::Unit::MM:
+ return width * camera.scale;
+ case Canvas::Unit::PX:
+ return width;
+ default:
+ ssassert(false, "Unexpected unit");
+ }
+}
+
+double Canvas::Stroke::StippleScaleMm(const Camera &camera) const {
+ switch(unit) {
+ case Canvas::Unit::MM:
+ return stippleScale;
+ case Canvas::Unit::PX:
+ return stippleScale / camera.scale;
+ default:
+ ssassert(false, "Unexpected unit");
+ }
+}
+
+double Canvas::Stroke::StippleScalePx(const Camera &camera) const {
+ switch(unit) {
+ case Canvas::Unit::MM:
+ return stippleScale * camera.scale;
+ case Canvas::Unit::PX:
+ return stippleScale;
+ default:
+ ssassert(false, "Unexpected unit");
+ }
+}
+
+bool Canvas::Fill::Equals(const Fill &other) const {
+ return (layer == other.layer &&
+ zIndex == other.zIndex &&
+ color.Equals(other.color) &&
+ pattern == other.pattern &&
+ texture == other.texture);
+}
+
+void Canvas::Clear() {
+ strokes.Clear();
+ fills.Clear();
+}
+
+Canvas::hStroke Canvas::GetStroke(const Stroke &stroke) {
+ for(const Stroke &s : strokes) {
+ if(s.Equals(stroke)) return s.h;
+ }
+ Stroke strokeCopy = stroke;
+ return strokes.AddAndAssignId(&strokeCopy);
+}
+
+Canvas::hFill Canvas::GetFill(const Fill &fill) {
+ for(const Fill &f : fills) {
+ if(f.Equals(fill)) return f.h;
+ }
+ Fill fillCopy = fill;
+ return fills.AddAndAssignId(&fillCopy);
+}
+
+BitmapFont *Canvas::GetBitmapFont() {
+ if(bitmapFont.IsEmpty()) {
+ bitmapFont = BitmapFont::Create();
+ }
+ return &bitmapFont;
+}
+
+std::shared_ptr<BatchCanvas> Canvas::CreateBatch() {
+ return std::shared_ptr<BatchCanvas>();
+}
+
+//-----------------------------------------------------------------------------
+// An interface for view-independent visualization
+//-----------------------------------------------------------------------------
+
+const Camera &BatchCanvas::GetCamera() const {
+ ssassert(false, "Geometry drawn on BatchCanvas must be independent from camera");
+}
+
+//-----------------------------------------------------------------------------
+// A wrapper around Canvas that simplifies drawing UI in screen coordinates
+//-----------------------------------------------------------------------------
+
+void UiCanvas::DrawLine(int x1, int y1, int x2, int y2, RgbaColor color, int width, int zIndex) {
+ Vector va = { (double)x1 + 0.5, (double)Flip(y1) + 0.5, 0.0 },
+ vb = { (double)x2 + 0.5, (double)Flip(y2) + 0.5, 0.0 };
+
+ Canvas::Stroke stroke = {};
+ stroke.layer = Canvas::Layer::NORMAL;
+ stroke.zIndex = zIndex;
+ stroke.width = (double)width;
+ stroke.color = color;
+ stroke.unit = Canvas::Unit::PX;
+ Canvas::hStroke hcs = canvas->GetStroke(stroke);
+
+ canvas->DrawLine(va, vb, hcs);
+}
+
+void UiCanvas::DrawRect(int l, int r, int t, int b, RgbaColor fillColor, RgbaColor outlineColor,
+ int zIndex) {
+ Vector va = { (double)l + 0.5, (double)Flip(b) + 0.5, 0.0 },
+ vb = { (double)l + 0.5, (double)Flip(t) + 0.5, 0.0 },
+ vc = { (double)r + 0.5, (double)Flip(t) + 0.5, 0.0 },
+ vd = { (double)r + 0.5, (double)Flip(b) + 0.5, 0.0 };
+
+ if(!fillColor.IsEmpty()) {
+ Canvas::Fill fill = {};
+ fill.layer = Canvas::Layer::NORMAL;
+ fill.zIndex = zIndex;
+ fill.color = fillColor;
+ Canvas::hFill hcf = canvas->GetFill(fill);
+
+ canvas->DrawQuad(va, vb, vc, vd, hcf);
+ }
+
+ if(!outlineColor.IsEmpty()) {
+ Canvas::Stroke stroke = {};
+ stroke.layer = Canvas::Layer::NORMAL;
+ stroke.zIndex = zIndex;
+ stroke.width = 1.0;
+ stroke.color = outlineColor;
+ stroke.unit = Canvas::Unit::PX;
+ Canvas::hStroke hcs = canvas->GetStroke(stroke);
+
+ canvas->DrawLine(va, vb, hcs);
+ canvas->DrawLine(vb, vc, hcs);
+ canvas->DrawLine(vc, vd, hcs);
+ canvas->DrawLine(vd, va, hcs);
+ }
+}
+
+void UiCanvas::DrawPixmap(std::shared_ptr<const Pixmap> pm, int x, int y, int zIndex) {
+ Canvas::Fill fill = {};
+ fill.layer = Canvas::Layer::NORMAL;
+ fill.zIndex = zIndex;
+ fill.color = { 255, 255, 255, 255 };
+ Canvas::hFill hcf = canvas->GetFill(fill);
+
+ canvas->DrawPixmap(pm,
+ { (double)x, (double)(flip ? Flip(y) - pm->height : y), 0.0 },
+ { (double)pm->width, 0.0, 0.0 },
+ { 0.0, (double)pm->height, 0.0 },
+ { 0.0, 1.0 },
+ { 1.0, 0.0 },
+ hcf);
+}
+
+void UiCanvas::DrawBitmapChar(char32_t codepoint, int x, int y, RgbaColor color, int zIndex) {
+ BitmapFont *font = canvas->GetBitmapFont();
+
+ Canvas::Fill fill = {};
+ fill.layer = Canvas::Layer::NORMAL;
+ fill.zIndex = zIndex;
+ fill.color = color;
+ Canvas::hFill hcf = canvas->GetFill(fill);
+
+ if(codepoint >= 0xe000 && codepoint <= 0xefff) {
+ // Special character, like a checkbox or a radio button
+ x -= 3;
+ }
+
+ double s0, t0, s1, t1;
+ size_t w, h;
+ font->LocateGlyph(codepoint, &s0, &t0, &s1, &t1, &w, &h);
+ if(font->textureUpdated) {
+ // LocateGlyph modified the texture, reload it.
+ canvas->InvalidatePixmap(font->texture);
+ font->textureUpdated = false;
+ }
+
+ canvas->DrawPixmap(font->texture,
+ { (double)x, (double)Flip(y), 0.0 },
+ { (double)w, 0.0, 0.0 },
+ { 0.0, (double) h, 0.0 },
+ { s0, t1 },
+ { s1, t0 },
+ hcf);
+}
+
+void UiCanvas::DrawBitmapText(const std::string &str, int x, int y, RgbaColor color, int zIndex) {
+ BitmapFont *font = canvas->GetBitmapFont();
+
+ for(char32_t codepoint : ReadUTF8(str)) {
+ DrawBitmapChar(codepoint, x, y, color, zIndex);
+ x += (int)font->GetWidth(codepoint) * 8;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// A canvas that performs picking against drawn geometry.
+//-----------------------------------------------------------------------------
+
+void ObjectPicker::DoCompare(double depth, double distance, int zIndex, int comparePosition) {
+ if(distance > selRadius) return;
+ if((zIndex == maxZIndex && distance < minDistance) || (zIndex > maxZIndex)) {
+ minDepth = depth;
+ minDistance = distance;
+ maxZIndex = zIndex;
+ position = comparePosition;
+ }
+}
+
+void ObjectPicker::DoQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
+ int zIndex, int comparePosition) {
+ Point2d corners[4] = {
+ camera.ProjectPoint(a),
+ camera.ProjectPoint(b),
+ camera.ProjectPoint(c),
+ camera.ProjectPoint(d)
+ };
+ double minNegative = VERY_NEGATIVE,
+ maxPositive = VERY_POSITIVE;
+ for(int i = 0; i < 4; i++) {
+ Point2d ap = corners[i],
+ bp = corners[(i + 1) % 4];
+ double distance = point.DistanceToLineSigned(ap, bp.Minus(ap), /*asSegment=*/true);
+ if(distance < 0) minNegative = std::max(minNegative, distance);
+ if(distance > 0) maxPositive = std::min(maxPositive, distance);
+ }
+
+ bool insideQuad = (minNegative == VERY_NEGATIVE || maxPositive == VERY_POSITIVE);
+ if(insideQuad) {
+ DoCompare(0, 0.0, zIndex, comparePosition);
+ } else {
+ double distance = std::min(fabs(minNegative), fabs(maxPositive));
+ DoCompare(0, distance, zIndex, comparePosition);
+ }
+}
+
+void ObjectPicker::DrawLine(const Vector &a, const Vector &b, hStroke hcs) {
+ Stroke *stroke = strokes.FindById(hcs);
+ Point2d ap = camera.ProjectPoint(a);
+ Point2d bp = camera.ProjectPoint(b);
+ double distance = point.DistanceToLine(ap, bp.Minus(ap), /*asSegment=*/true);
+ double depth = 0.5 * (camera.ProjectPoint3(a).z + camera.ProjectPoint3(b).z) ;
+ DoCompare(depth, distance - stroke->width / 2.0, stroke->zIndex);
+}
+
+void ObjectPicker::DrawEdges(const SEdgeList &el, hStroke hcs) {
+ Stroke *stroke = strokes.FindById(hcs);
+ for(const SEdge &e : el.l) {
+ Point2d ap = camera.ProjectPoint(e.a);
+ Point2d bp = camera.ProjectPoint(e.b);
+ double distance = point.DistanceToLine(ap, bp.Minus(ap), /*asSegment=*/true);
+ double depth = 0.5 * (camera.ProjectPoint3(e.a).z + camera.ProjectPoint3(e.b).z) ;
+ DoCompare(depth, distance - stroke->width / 2.0, stroke->zIndex, e.auxB);
+ }
+}
+
+void ObjectPicker::DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) {
+ ssassert(false, "Not implemented");
+}
+
+void ObjectPicker::DrawVectorText(const std::string &text, double height,
+ const Vector &o, const Vector &u, const Vector &v,
+ hStroke hcs) {
+ Stroke *stroke = strokes.FindById(hcs);
+ double w = VectorFont::Builtin()-> GetWidth(height, text),
+ h = VectorFont::Builtin()->GetHeight(height);
+ DoQuad(o,
+ o.Plus(v.ScaledBy(h)),
+ o.Plus(u.ScaledBy(w)).Plus(v.ScaledBy(h)),
+ o.Plus(u.ScaledBy(w)),
+ stroke->zIndex);
+}
+
+void ObjectPicker::DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
+ hFill hcf) {
+ Fill *fill = fills.FindById(hcf);
+ DoQuad(a, b, c, d, fill->zIndex);
+}
+
+void ObjectPicker::DrawPoint(const Vector &o, Canvas::hStroke hcs) {
+ Stroke *stroke = strokes.FindById(hcs);
+ double distance = point.DistanceTo(camera.ProjectPoint(o)) - stroke->width / 2;
+ double depth = camera.ProjectPoint3(o).z;
+ DoCompare(depth, distance, stroke->zIndex);
+}
+
+void ObjectPicker::DrawPolygon(const SPolygon &p, hFill hcf) {
+ ssassert(false, "Not implemented");
+}
+
+void ObjectPicker::DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack) {
+ ssassert(false, "Not implemented");
+}
+
+void ObjectPicker::DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) {
+ ssassert(false, "Not implemented");
+}
+
+void ObjectPicker::DrawPixmap(std::shared_ptr<const Pixmap> pm,
+ const Vector &o, const Vector &u, const Vector &v,
+ const Point2d &ta, const Point2d &tb, Canvas::hFill hcf) {
+ DrawQuad(o, o.Plus(u), o.Plus(u).Plus(v), o.Plus(v), hcf);
+}
+
+bool ObjectPicker::Pick(const std::function<void()> &drawFn) {
+ minDepth = VERY_POSITIVE;
+ minDistance = VERY_POSITIVE;
+ maxZIndex = INT_MIN;
+
+ drawFn();
+ return minDistance < selRadius;
+}
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Backend-agnostic rendering interface, and various backends we use.
+//
+// Copyright 2016 whitequark
+//-----------------------------------------------------------------------------
+
+#ifndef SOLVESPACE_RENDER_H
+#define SOLVESPACE_RENDER_H
+
+//-----------------------------------------------------------------------------
+// Interfaces common for all renderers
+//-----------------------------------------------------------------------------
+
+enum class StipplePattern : uint32_t;
+
+// A mapping from 3d sketch coordinates to 2d screen coordinates, using
+// an axonometric projection.
+class Camera {
+public:
+ double width;
+ double height;
+ double pixelRatio;
+ bool gridFit;
+ Vector offset;
+ Vector projRight;
+ Vector projUp;
+ double scale;
+ double tangent;
+
+ bool IsPerspective() const { return tangent != 0.0; }
+
+ Point2d ProjectPoint(Vector p) const;
+ Vector ProjectPoint3(Vector p) const;
+ Vector ProjectPoint4(Vector p, double *w) const;
+ Vector UnProjectPoint(Point2d p) const;
+ Vector UnProjectPoint3(Vector p) const;
+ Vector VectorFromProjs(Vector rightUpForward) const;
+ Vector AlignToPixelGrid(Vector v) const;
+
+ SBezier ProjectBezier(SBezier b) const;
+
+ void LoadIdentity();
+ void NormalizeProjectionVectors();
+};
+
+// A description of scene lighting.
+class Lighting {
+public:
+ RgbaColor backgroundColor;
+ double ambientIntensity;
+ double lightIntensity[2];
+ Vector lightDirection[2];
+};
+
+class BatchCanvas;
+
+// An interface for populating a drawing area with geometry.
+class Canvas {
+public:
+ // Stroke and fill styles are addressed with handles to be able to quickly
+ // group geometry into indexed draw calls.
+ class hStroke {
+ public:
+ uint32_t v;
+ };
+
+ class hFill {
+ public:
+ uint32_t v;
+ };
+
+ // The layer of a geometry describes how it occludes other geometry.
+ // Within a layer, geometry with higher z-index occludes geometry with lower z-index,
+ // or geometry drawn earlier if z-indexes match.
+ enum class Layer {
+ NORMAL, // Occluded by geometry with lower Z coordinate
+ OCCLUDED, // Only drawn over geometry with lower Z coordinate
+ DEPTH_ONLY, // Like NORMAL, but only affects future occlusion, not color
+ BACK, // Always drawn below all other geometry
+ FRONT, // Always drawn above all other geometry
+ LAST = FRONT
+ };
+
+ // The outlines are the collection of all edges that may be drawn.
+ // Outlines can be classified as emphasized or not; emphasized outlines indicate an abrupt
+ // change in the surface curvature. These are indicated by the SOutline tag.
+ // Outlines can also be classified as contour or not; contour outlines indicate the boundary
+ // of the filled mesh. Whether an outline is a part of contour or not depends on point of view.
+ enum class DrawOutlinesAs {
+ EMPHASIZED_AND_CONTOUR = 0, // Both emphasized and contour outlines
+ EMPHASIZED_WITHOUT_CONTOUR = 1, // Emphasized outlines except those also belonging to contour
+ CONTOUR_ONLY = 2 // Contour outlines only
+ };
+
+ // Stroke widths, etc, can be scale-invariant (in pixels) or scale-dependent (in millimeters).
+ enum class Unit {
+ MM,
+ PX
+ };
+
+ class Stroke {
+ public:
+ hStroke h;
+
+ Layer layer;
+ int zIndex;
+ RgbaColor color;
+ double width;
+ Unit unit;
+ StipplePattern stipplePattern;
+ double stippleScale;
+
+ void Clear() { *this = {}; }
+ bool Equals(const Stroke &other) const;
+
+ double WidthMm(const Camera &camera) const;
+ double WidthPx(const Camera &camera) const;
+ double StippleScaleMm(const Camera &camera) const;
+ double StippleScalePx(const Camera &camera) const;
+ };
+
+ enum class FillPattern {
+ SOLID, CHECKERED_A, CHECKERED_B
+ };
+
+ class Fill {
+ public:
+ hFill h;
+
+ Layer layer;
+ int zIndex;
+ RgbaColor color;
+ FillPattern pattern;
+ std::shared_ptr<const Pixmap> texture;
+
+ void Clear() { *this = {}; }
+ bool Equals(const Fill &other) const;
+ };
+
+ IdList<Stroke, hStroke> strokes = {};
+ IdList<Fill, hFill> fills = {};
+ BitmapFont bitmapFont = {};
+
+ virtual void Clear();
+ virtual ~Canvas() = default;
+
+ hStroke GetStroke(const Stroke &stroke);
+ hFill GetFill(const Fill &fill);
+ BitmapFont *GetBitmapFont();
+
+ virtual const Camera &GetCamera() const = 0;
+
+ virtual void DrawLine(const Vector &a, const Vector &b, hStroke hcs) = 0;
+ virtual void DrawEdges(const SEdgeList &el, hStroke hcs) = 0;
+ virtual bool DrawBeziers(const SBezierList &bl, hStroke hcs) = 0;
+ virtual void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) = 0;
+ virtual void DrawVectorText(const std::string &text, double height,
+ const Vector &o, const Vector &u, const Vector &v,
+ hStroke hcs) = 0;
+
+ virtual void DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
+ hFill hcf) = 0;
+ virtual void DrawPoint(const Vector &o, hStroke hcs) = 0;
+ virtual void DrawPolygon(const SPolygon &p, hFill hcf) = 0;
+ virtual void DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack = {}) = 0;
+ virtual void DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) = 0;
+
+ virtual void DrawPixmap(std::shared_ptr<const Pixmap> pm,
+ const Vector &o, const Vector &u, const Vector &v,
+ const Point2d &ta, const Point2d &tb, hFill hcf) = 0;
+ virtual void InvalidatePixmap(std::shared_ptr<const Pixmap> pm) = 0;
+
+ virtual std::shared_ptr<BatchCanvas> CreateBatch();
+};
+
+template<>
+struct IsHandleOracle<Canvas::hStroke> : std::true_type {};
+
+template<>
+struct IsHandleOracle<Canvas::hFill> : std::true_type {};
+
+
+// An interface for view-dependent visualization.
+class ViewportCanvas : public Canvas {
+public:
+ virtual void SetCamera(const Camera &camera) = 0;
+ virtual void SetLighting(const Lighting &lighting) = 0;
+
+ virtual void StartFrame() = 0;
+ virtual void FlushFrame() = 0;
+ virtual void FinishFrame() = 0;
+ virtual std::shared_ptr<Pixmap> ReadFrame() = 0;
+
+ virtual void GetIdent(const char **vendor, const char **renderer, const char **version) = 0;
+};
+
+// An interface for view-independent visualization.
+class BatchCanvas : public Canvas {
+public:
+ const Camera &GetCamera() const override;
+
+ virtual void Finalize() = 0;
+ virtual void Draw() = 0;
+};
+
+// A wrapper around Canvas that simplifies drawing UI in screen coordinates.
+class UiCanvas {
+public:
+ std::shared_ptr<Canvas> canvas;
+ bool flip = false;
+
+ void DrawLine(int x1, int y1, int x2, int y2, RgbaColor color, int width = 1,
+ int zIndex = 0);
+ void DrawRect(int l, int r, int t, int b, RgbaColor fillColor, RgbaColor outlineColor,
+ int zIndex = 0);
+ void DrawPixmap(std::shared_ptr<const Pixmap> pm, int x, int y,
+ int zIndex = 0);
+ void DrawBitmapChar(char32_t codepoint, int x, int y, RgbaColor color,
+ int zIndex = 0);
+ void DrawBitmapText(const std::string &str, int x, int y, RgbaColor color,
+ int zIndex = 0);
+
+ int Flip(int y) const { return flip ? (int)canvas->GetCamera().height - y : y; }
+};
+
+// A canvas that performs picking against drawn geometry.
+class ObjectPicker : public Canvas {
+public:
+ Camera camera = {};
+ // Configuration.
+ Point2d point = {};
+ double selRadius = 0.0;
+ // Picking state.
+ double minDistance = 0.0;
+ double minDepth = 1e10;
+ int maxZIndex = 0;
+ uint32_t position = 0;
+
+ const Camera &GetCamera() const override { return camera; }
+
+ void DrawLine(const Vector &a, const Vector &b, hStroke hcs) override;
+ void DrawEdges(const SEdgeList &el, hStroke hcs) override;
+ bool DrawBeziers(const SBezierList &bl, hStroke hcs) override { return false; }
+ void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) override;
+ void DrawVectorText(const std::string &text, double height,
+ const Vector &o, const Vector &u, const Vector &v,
+ hStroke hcs) override;
+
+ void DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
+ hFill hcf) override;
+ void DrawPoint(const Vector &o, hStroke hcs) override;
+ void DrawPolygon(const SPolygon &p, hFill hcf) override;
+ void DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack) override;
+ void DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) override;
+
+ void DrawPixmap(std::shared_ptr<const Pixmap> pm,
+ const Vector &o, const Vector &u, const Vector &v,
+ const Point2d &ta, const Point2d &tb, hFill hcf) override;
+ void InvalidatePixmap(std::shared_ptr<const Pixmap> pm) override {}
+
+ void DoCompare(double depth, double distance, int zIndex, int comparePosition = 0);
+ void DoQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
+ int zIndex, int comparePosition = 0);
+
+ bool Pick(const std::function<void()> &drawFn);
+};
+
+// A canvas that renders onto a 2d surface, performing z-index sorting, occlusion testing, etc,
+// on the CPU.
+class SurfaceRenderer : public ViewportCanvas {
+public:
+ Camera camera = {};
+ Lighting lighting = {};
+ // Chord tolerance, for converting beziers to pwl.
+ double chordTolerance = 0.0;
+ // Render lists.
+ handle_map<hStroke, SEdgeList> edges;
+ handle_map<hStroke, SBezierList> beziers;
+ SMesh mesh = {};
+ // State.
+ BBox bbox = {};
+
+ void Clear() override;
+
+ // Canvas interface.
+ const Camera &GetCamera() const override { return camera; }
+
+ // ViewportCanvas interface.
+ void SetCamera(const Camera &cam) override { this->camera = cam; }
+ void SetLighting(const Lighting &light) override { this->lighting = light; }
+
+ void DrawLine(const Vector &a, const Vector &b, hStroke hcs) override;
+ void DrawEdges(const SEdgeList &el, hStroke hcs) override;
+ bool DrawBeziers(const SBezierList &bl, hStroke hcs) override;
+ void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) override;
+ void DrawVectorText(const std::string &text, double height,
+ const Vector &o, const Vector &u, const Vector &v,
+ hStroke hcs) override;
+
+ void DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
+ hFill hcf) override;
+ void DrawPoint(const Vector &o, hStroke hcs) override;
+ void DrawPolygon(const SPolygon &p, hFill hcf) override;
+ void DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack) override;
+ void DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) override;
+
+ void DrawPixmap(std::shared_ptr<const Pixmap> pm,
+ const Vector &o, const Vector &u, const Vector &v,
+ const Point2d &ta, const Point2d &tb, hFill hcf) override;
+ void InvalidatePixmap(std::shared_ptr<const Pixmap> pm) override;
+
+ // Geometry manipulation.
+ void CalculateBBox();
+ void ConvertBeziersToEdges();
+ void CullOccludedStrokes();
+
+ // Renderer operations.
+ void OutputInPaintOrder();
+
+ virtual bool CanOutputCurves() const = 0;
+ virtual bool CanOutputTriangles() const = 0;
+
+ virtual void OutputStart() = 0;
+ virtual void OutputBezier(const SBezier &b, hStroke hcs) = 0;
+ virtual void OutputTriangle(const STriangle &tr) = 0;
+ virtual void OutputEnd() = 0;
+
+ void OutputBezierAsNonrationalCubic(const SBezier &b, hStroke hcs);
+};
+
+//-----------------------------------------------------------------------------
+// 2d renderers
+//-----------------------------------------------------------------------------
+
+class CairoRenderer : public SurfaceRenderer {
+public:
+ cairo_t *context = NULL;
+ // Renderer configuration.
+ bool antialias = false;
+ // Renderer state.
+ struct {
+ hStroke hcs;
+ } current = {};
+
+ void Clear() override;
+
+ void StartFrame() override {}
+ void FlushFrame() override;
+ void FinishFrame() override {}
+ std::shared_ptr<Pixmap> ReadFrame() override;
+
+ void GetIdent(const char **vendor, const char **renderer, const char **version) override;
+
+ void SelectStroke(hStroke hcs);
+ void MoveTo(Vector p);
+ void FinishPath();
+
+ bool CanOutputCurves() const override { return true; }
+ bool CanOutputTriangles() const override { return true; }
+
+ void OutputStart() override;
+ void OutputBezier(const SBezier &b, hStroke hcs) override;
+ void OutputTriangle(const STriangle &tr) override;
+ void OutputEnd() override;
+};
+
+class CairoPixmapRenderer final : public CairoRenderer {
+public:
+ std::shared_ptr<Pixmap> pixmap;
+
+ cairo_surface_t *surface = NULL;
+
+ void Init();
+ void Clear() override;
+
+ std::shared_ptr<Pixmap> ReadFrame() override;
+};
+
+//-----------------------------------------------------------------------------
+// Factories
+//-----------------------------------------------------------------------------
+
+std::shared_ptr<ViewportCanvas> CreateRenderer();
+
+#endif
--- /dev/null
+//-----------------------------------------------------------------------------
+// Rendering projections to 2d surfaces: z-sorting, occlusion testing, etc.
+//
+// Copyright 2016 whitequark
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+
+namespace SolveSpace {
+
+// FIXME: The export coordinate system has a different handedness than display
+// coordinate system; lighting and occlusion calculations are right-handed.
+static Vector ProjectPoint3RH(const Camera &camera, Vector p) {
+ p = p.Plus(camera.offset);
+
+ Vector r;
+ r.x = p.Dot(camera.projRight);
+ r.y = p.Dot(camera.projUp);
+ r.z = p.Dot(camera.projRight.Cross(camera.projUp));
+
+ double w = 1 + r.z*camera.tangent*camera.scale;
+ return r.ScaledBy(camera.scale/w);
+}
+
+//-----------------------------------------------------------------------------
+// Accumulation of geometry
+//-----------------------------------------------------------------------------
+
+void SurfaceRenderer::DrawLine(const Vector &a, const Vector &b, hStroke hcs) {
+ edges[hcs].AddEdge(ProjectPoint3RH(camera, a),
+ ProjectPoint3RH(camera, b));
+}
+
+void SurfaceRenderer::DrawEdges(const SEdgeList &el, hStroke hcs) {
+ for(const SEdge &e : el.l) {
+ edges[hcs].AddEdge(ProjectPoint3RH(camera, e.a),
+ ProjectPoint3RH(camera, e.b));
+ }
+}
+
+bool SurfaceRenderer::DrawBeziers(const SBezierList &bl, hStroke hcs) {
+ if(!CanOutputCurves())
+ return false;
+
+ for(const SBezier &b : bl.l) {
+ SBezier pb = camera.ProjectBezier(b);
+ beziers[hcs].l.Add(&pb);
+ }
+ return true;
+}
+
+void SurfaceRenderer::DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) {
+ Vector projDir = camera.projRight.Cross(camera.projUp);
+ for(const SOutline &o : ol.l) {
+ if(drawAs == DrawOutlinesAs::EMPHASIZED_AND_CONTOUR &&
+ !(o.IsVisible(projDir) || o.tag != 0))
+ continue;
+ if(drawAs == DrawOutlinesAs::EMPHASIZED_WITHOUT_CONTOUR &&
+ !(!o.IsVisible(projDir) && o.tag != 0))
+ continue;
+ if(drawAs == DrawOutlinesAs::CONTOUR_ONLY &&
+ !(o.IsVisible(projDir)))
+ continue;
+
+ edges[hcs].AddEdge(ProjectPoint3RH(camera, o.a),
+ ProjectPoint3RH(camera, o.b));
+ }
+}
+
+void SurfaceRenderer::DrawVectorText(const std::string &text, double height,
+ const Vector &o, const Vector &u, const Vector &v,
+ hStroke hcs) {
+ auto traceEdge = [&](Vector a, Vector b) {
+ edges[hcs].AddEdge(ProjectPoint3RH(camera, a),
+ ProjectPoint3RH(camera, b));
+ };
+ VectorFont::Builtin()->Trace(height, o, u, v, text, traceEdge, camera);
+}
+
+void SurfaceRenderer::DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
+ hFill hcf) {
+ Fill *fill = fills.FindById(hcf);
+ ssassert(fill->layer == Layer::NORMAL ||
+ fill->layer == Layer::DEPTH_ONLY ||
+ fill->layer == Layer::FRONT ||
+ fill->layer == Layer::BACK, "Unexpected mesh layer");
+
+ Vector zOffset = {};
+ if(fill->layer == Layer::BACK) {
+ zOffset.z -= 1e6;
+ } else if(fill->layer == Layer::FRONT) {
+ zOffset.z += 1e6;
+ }
+ zOffset.z += camera.scale * fill->zIndex;
+
+ STriMeta meta = {};
+ if(fill->layer != Layer::DEPTH_ONLY) {
+ meta.color = fill->color;
+ }
+ Vector ta = ProjectPoint3RH(camera, a).Plus(zOffset),
+ tb = ProjectPoint3RH(camera, b).Plus(zOffset),
+ tc = ProjectPoint3RH(camera, c).Plus(zOffset),
+ td = ProjectPoint3RH(camera, d).Plus(zOffset);
+ mesh.AddTriangle(meta, tc, tb, ta);
+ mesh.AddTriangle(meta, ta, td, tc);
+}
+
+void SurfaceRenderer::DrawPoint(const Vector &o, Canvas::hStroke hcs) {
+ Stroke *stroke = strokes.FindById(hcs);
+
+ Fill fill = {};
+ fill.layer = stroke->layer;
+ fill.zIndex = stroke->zIndex;
+ fill.color = stroke->color;
+ hFill hcf = GetFill(fill);
+
+ Vector u = camera.projRight.ScaledBy(stroke->width/2.0/camera.scale),
+ v = camera.projUp.ScaledBy(stroke->width/2.0/camera.scale);
+ DrawQuad(o.Minus(u).Minus(v), o.Minus(u).Plus(v),
+ o.Plus(u).Plus(v), o.Plus(u).Minus(v), hcf);
+}
+
+void SurfaceRenderer::DrawPolygon(const SPolygon &p, hFill hcf) {
+ SMesh m = {};
+ p.TriangulateInto(&m);
+ DrawMesh(m, hcf, {});
+ m.Clear();
+}
+
+void SurfaceRenderer::DrawMesh(const SMesh &m,
+ hFill hcfFront, hFill hcfBack) {
+ Fill *fill = fills.FindById(hcfFront);
+ ssassert(fill->layer == Layer::NORMAL ||
+ fill->layer == Layer::DEPTH_ONLY, "Unexpected mesh layer");
+
+ Vector l0 = (lighting.lightDirection[0]).WithMagnitude(1),
+ l1 = (lighting.lightDirection[1]).WithMagnitude(1);
+ for(STriangle tr : m.l) {
+ tr.a = ProjectPoint3RH(camera, tr.a);
+ tr.b = ProjectPoint3RH(camera, tr.b);
+ tr.c = ProjectPoint3RH(camera, tr.c);
+
+ if(CanOutputTriangles() && fill->layer == Layer::NORMAL) {
+ if(fill->color.IsEmpty()) {
+ // Compute lighting, since we're going to draw the shaded triangles.
+ Vector n = tr.Normal().WithMagnitude(1);
+ double intensity = lighting.ambientIntensity +
+ max(0.0, (lighting.lightIntensity[0])*(n.Dot(l0))) +
+ max(0.0, (lighting.lightIntensity[1])*(n.Dot(l1)));
+ double r = min(1.0, tr.meta.color.redF() * intensity),
+ g = min(1.0, tr.meta.color.greenF() * intensity),
+ b = min(1.0, tr.meta.color.blueF() * intensity);
+ tr.meta.color = RGBf(r, g, b);
+ } else {
+ // We're going to draw this triangle, but it's not shaded.
+ tr.meta.color = fill->color;
+ }
+ } else {
+ // This triangle is just for occlusion testing.
+ tr.meta.color = {};
+ }
+ mesh.AddTriangle(&tr);
+ }
+}
+
+void SurfaceRenderer::DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) {
+ Fill *fill = fills.FindById(hcf);
+ ssassert(fill->layer == Layer::NORMAL ||
+ fill->layer == Layer::DEPTH_ONLY, "Unexpected mesh layer");
+
+ Vector zOffset = {};
+ zOffset.z += camera.scale * fill->zIndex;
+
+ size_t facesSize = faces.size();
+ for(STriangle tr : m.l) {
+ uint32_t face = tr.meta.face;
+ for(size_t j = 0; j < facesSize; j++) {
+ if(faces[j] != face) continue;
+ if(!fill->color.IsEmpty()) {
+ tr.meta.color = fill->color;
+ }
+ mesh.AddTriangle(tr.meta,
+ ProjectPoint3RH(camera, tr.a).Plus(zOffset),
+ ProjectPoint3RH(camera, tr.b).Plus(zOffset),
+ ProjectPoint3RH(camera, tr.c).Plus(zOffset));
+ break;
+ }
+ }
+}
+
+void SurfaceRenderer::DrawPixmap(std::shared_ptr<const Pixmap> pm,
+ const Vector &o, const Vector &u, const Vector &v,
+ const Point2d &ta, const Point2d &tb, hFill hcf) {
+ ssassert(false, "Not implemented");
+}
+
+void SurfaceRenderer::InvalidatePixmap(std::shared_ptr<const Pixmap> pm) {
+ ssassert(false, "Not implemented");
+}
+
+//-----------------------------------------------------------------------------
+// Processing of geometry
+//-----------------------------------------------------------------------------
+
+void SurfaceRenderer::CalculateBBox() {
+ bbox.minp = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
+ bbox.maxp = Vector::From(VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE);
+
+ for(auto &it : edges) {
+ SEdgeList &el = it.second;
+ for(SEdge &e : el.l) {
+ bbox.Include(e.a);
+ bbox.Include(e.b);
+ }
+ }
+
+ for(auto &it : beziers) {
+ SBezierList &bl = it.second;
+ for(SBezier &b : bl.l) {
+ for(int i = 0; i <= b.deg; i++) {
+ bbox.Include(b.ctrl[i]);
+ }
+ }
+ }
+
+ for(STriangle &tr : mesh.l) {
+ for(int i = 0; i < 3; i++) {
+ bbox.Include(tr.vertices[i]);
+ }
+ }
+}
+
+
+void SurfaceRenderer::ConvertBeziersToEdges() {
+ for(auto &it : beziers) {
+ hStroke hcs = it.first;
+ SBezierList &bl = it.second;
+
+ SEdgeList &el = edges[hcs];
+ for(const SBezier &b : bl.l) {
+ if(b.deg == 1) {
+ el.AddEdge(b.ctrl[0], b.ctrl[1]);
+ } else {
+ List<Vector> lv = {};
+ b.MakePwlInto(&lv, chordTolerance);
+ for(int i = 1; i < lv.n; i++) {
+ el.AddEdge(lv[i-1], lv[i]);
+ }
+ lv.Clear();
+ }
+ }
+ bl.l.Clear();
+ }
+ beziers.clear();
+}
+
+void SurfaceRenderer::CullOccludedStrokes() {
+ // Perform occlusion testing, if necessary.
+ if(mesh.l.IsEmpty())
+ return;
+
+ // We can't perform hidden line removal on exact curves.
+ ConvertBeziersToEdges();
+
+ // Remove hidden lines (on NORMAL layers), or remove visible lines (on OCCLUDED layers).
+ SKdNode *root = SKdNode::From(&mesh);
+ root->ClearTags();
+
+ int cnt = 1234;
+ for(auto &eit : edges) {
+ hStroke hcs = eit.first;
+ SEdgeList &el = eit.second;
+
+ Stroke *stroke = strokes.FindById(hcs);
+ if(stroke->layer != Layer::NORMAL &&
+ stroke->layer != Layer::OCCLUDED) continue;
+
+ SEdgeList nel = {};
+ for(const SEdge &e : el.l) {
+ SEdgeList oel = {};
+ oel.AddEdge(e.a, e.b);
+ root->OcclusionTestLine(e, &oel, cnt);
+
+ if(stroke->layer == Layer::OCCLUDED) {
+ for(SEdge &oe : oel.l) {
+ oe.tag = !oe.tag;
+ }
+ }
+ oel.l.RemoveTagged();
+
+ oel.MergeCollinearSegments(e.a, e.b);
+ for(const SEdge &oe : oel.l) {
+ nel.AddEdge(oe.a, oe.b);
+ }
+
+ oel.Clear();
+ cnt++;
+ }
+
+ el.l.Clear();
+ el.l = nel.l;
+ }
+}
+
+void SurfaceRenderer::OutputInPaintOrder() {
+ // Sort our strokes in paint order.
+ std::vector<std::pair<Layer, int>> paintOrder;
+ paintOrder.emplace_back(Layer::NORMAL, 0); // mesh
+ for(const Stroke &cs : strokes) {
+ paintOrder.emplace_back(cs.layer, cs.zIndex);
+ }
+
+ const Layer stackup[] = {
+ Layer::BACK, Layer::NORMAL, Layer::DEPTH_ONLY, Layer::OCCLUDED, Layer::FRONT
+ };
+ std::sort(paintOrder.begin(), paintOrder.end(),
+ [&](std::pair<Layer, int> a, std::pair<Layer, int> b) {
+ Layer aLayer = a.first,
+ bLayer = b.first;
+ int aZIndex = a.second,
+ bZIndex = b.second;
+
+ size_t aLayerIndex =
+ std::find(std::begin(stackup), std::end(stackup), aLayer) - std::begin(stackup);
+ size_t bLayerIndex =
+ std::find(std::begin(stackup), std::end(stackup), bLayer) - std::begin(stackup);
+ if(aLayerIndex == bLayerIndex) {
+ return aZIndex < bZIndex;
+ } else {
+ return aLayerIndex < bLayerIndex;
+ }
+ });
+
+ auto last = std::unique(paintOrder.begin(), paintOrder.end());
+ paintOrder.erase(last, paintOrder.end());
+
+ // Output geometry in paint order.
+ OutputStart();
+ for(auto &it : paintOrder) {
+ Layer layer = it.first;
+ int zIndex = it.second;
+
+ if(layer == Layer::NORMAL && zIndex == 0) {
+ SMesh mp = {};
+ SBsp3 *bsp = SBsp3::FromMesh(&mesh);
+ if(bsp) bsp->GenerateInPaintOrder(&mp);
+
+ for(const STriangle &tr : mp.l) {
+ // Cull back-facing and invisible triangles.
+ if(tr.Normal().z < 0) continue;
+ if(tr.meta.color.IsEmpty()) continue;
+ OutputTriangle(tr);
+ }
+
+ mp.Clear();
+ }
+
+ for(auto eit : edges) {
+ hStroke hcs = eit.first;
+ const SEdgeList &el = eit.second;
+
+ Stroke *stroke = strokes.FindById(hcs);
+ if(stroke->layer != layer || stroke->zIndex != zIndex) continue;
+
+ for(const SEdge &e : el.l) {
+ OutputBezier(SBezier::From(e.a, e.b), hcs);
+ }
+ }
+
+ for(auto &bit : beziers) {
+ hStroke hcs = bit.first;
+ const SBezierList &bl = bit.second;
+
+ Stroke *stroke = strokes.FindById(hcs);
+ if(stroke->layer != layer || stroke->zIndex != zIndex) continue;
+
+ for(const SBezier &b : bl.l) {
+ OutputBezier(b, hcs);
+ }
+ }
+ }
+ OutputEnd();
+}
+
+void SurfaceRenderer::Clear() {
+ Canvas::Clear();
+
+ for(auto &eit : edges) {
+ SEdgeList &el = eit.second;
+ el.l.Clear();
+ }
+ edges.clear();
+
+ for(auto &bit : beziers) {
+ SBezierList &bl = bit.second;
+ bl.l.Clear();
+ }
+ beziers.clear();
+
+ mesh.Clear();
+}
+
+void SurfaceRenderer::OutputBezierAsNonrationalCubic(const SBezier &b, hStroke hcs) {
+ // Arbitrary choice of tolerance; make it a little finer than pwl tolerance since
+ // it should be easier to achieve that with the smooth curves.
+ SBezierList bl;
+ b.MakeNonrationalCubicInto(&bl, chordTolerance / 2);
+ for(const SBezier &cb : bl.l) {
+ OutputBezier(cb, hcs);
+ }
+ bl.Clear();
+}
+
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// A rendering backend that draws on a Cairo surface.
+//
+// Copyright 2016 whitequark
+//-----------------------------------------------------------------------------
+#include <cairo.h>
+#include "solvespace.h"
+
+namespace SolveSpace {
+
+void CairoRenderer::Clear() {
+ SurfaceRenderer::Clear();
+
+ if(context != NULL) cairo_destroy(context);
+ context = NULL;
+}
+
+void CairoRenderer::GetIdent(const char **vendor, const char **renderer, const char **version) {
+ *vendor = "Cairo";
+ *renderer = "Cairo";
+ *version = cairo_version_string();
+}
+
+void CairoRenderer::FlushFrame() {
+ CullOccludedStrokes();
+ OutputInPaintOrder();
+
+ cairo_surface_flush(cairo_get_target(context));
+}
+
+std::shared_ptr<Pixmap> CairoRenderer::ReadFrame() {
+ ssassert(false, "generic Cairo renderer does not support pixmap readout");
+}
+
+void CairoRenderer::OutputStart() {
+ cairo_save(context);
+
+ RgbaColor bgColor = lighting.backgroundColor;
+ cairo_rectangle(context, 0.0, 0.0, (double)camera.width, (double)camera.height);
+ cairo_set_source_rgba(context, bgColor.redF(), bgColor.greenF(), bgColor.blueF(),
+ bgColor.alphaF());
+ cairo_fill(context);
+
+ cairo_translate(context, camera.width / 2.0, camera.height / 2.0);
+
+ // Avoid pixel boundaries; when not using antialiasing, we would otherwise
+ // get numerically unstable output.
+ cairo_translate(context, 0.1, 0.1);
+
+ cairo_set_line_join(context, CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_cap(context, CAIRO_LINE_CAP_ROUND);
+}
+
+void CairoRenderer::OutputEnd() {
+ FinishPath();
+
+ cairo_restore(context);
+}
+
+void CairoRenderer::SelectStroke(hStroke hcs) {
+ if(current.hcs == hcs) return;
+ FinishPath();
+
+ Stroke *stroke = strokes.FindById(hcs);
+ current.hcs = hcs;
+
+ RgbaColor color = stroke->color;
+ std::vector<double> dashes = StipplePatternDashes(stroke->stipplePattern);
+ for(double &dash : dashes) {
+ dash *= stroke->StippleScalePx(camera);
+ }
+ cairo_set_line_width(context, stroke->WidthPx(camera));
+ cairo_set_dash(context, dashes.data(), (int)dashes.size(), 0);
+ cairo_set_source_rgba(context, color.redF(), color.greenF(), color.blueF(),
+ color.alphaF());
+ if(antialias) {
+ cairo_set_antialias(context, CAIRO_ANTIALIAS_GRAY);
+ } else {
+ cairo_set_antialias(context, CAIRO_ANTIALIAS_NONE);
+ }
+}
+
+void CairoRenderer::MoveTo(Vector p) {
+ Point2d pos;
+ cairo_get_current_point(context, &pos.x, &pos.y);
+ if(cairo_has_current_point(context) && pos.Equals(p.ProjectXy())) return;
+ FinishPath();
+
+ cairo_move_to(context, p.x, p.y);
+}
+
+void CairoRenderer::FinishPath() {
+ if(!cairo_has_current_point(context)) return;
+
+ cairo_stroke(context);
+}
+
+void CairoRenderer::OutputBezier(const SBezier &b, hStroke hcs) {
+ SelectStroke(hcs);
+
+ Vector c, n = Vector::From(0, 0, 1);
+ double r;
+ if(b.deg == 1) {
+ MoveTo(b.ctrl[0]);
+ cairo_line_to(context,
+ b.ctrl[1].x, b.ctrl[1].y);
+ } else if(b.IsCircle(n, &c, &r)) {
+ MoveTo(b.ctrl[0]);
+ double theta0 = atan2(b.ctrl[0].y - c.y, b.ctrl[0].x - c.x),
+ theta1 = atan2(b.ctrl[2].y - c.y, b.ctrl[2].x - c.x),
+ dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI);
+ if(dtheta > 0) {
+ cairo_arc(context,
+ c.x, c.y, r, theta0, theta1);
+ } else {
+ cairo_arc_negative(context,
+ c.x, c.y, r, theta0, theta1);
+ }
+ } else if(b.deg == 3 && !b.IsRational()) {
+ MoveTo(b.ctrl[0]);
+ cairo_curve_to(context,
+ b.ctrl[1].x, b.ctrl[1].y,
+ b.ctrl[2].x, b.ctrl[2].y,
+ b.ctrl[3].x, b.ctrl[3].y);
+ } else {
+ OutputBezierAsNonrationalCubic(b, hcs);
+ }
+}
+
+void CairoRenderer::OutputTriangle(const STriangle &tr) {
+ FinishPath();
+ current.hcs = {};
+
+ RgbaColor color = tr.meta.color;
+ cairo_set_source_rgba(context, color.redF(), color.greenF(), color.blueF(),
+ color.alphaF());
+ cairo_set_antialias(context, CAIRO_ANTIALIAS_NONE);
+ cairo_move_to(context, tr.a.x, tr.a.y);
+ cairo_line_to(context, tr.b.x, tr.b.y);
+ cairo_line_to(context, tr.c.x, tr.c.y);
+ cairo_fill(context);
+}
+
+void CairoPixmapRenderer::Init() {
+ Clear();
+
+ pixmap = std::make_shared<Pixmap>();
+ pixmap->format = Pixmap::Format::BGRA;
+ pixmap->width = (size_t)camera.width;
+ pixmap->height = (size_t)camera.height;
+ pixmap->stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, (int)camera.width);
+ pixmap->data = std::vector<uint8_t>(pixmap->stride * pixmap->height);
+ surface =
+ cairo_image_surface_create_for_data(&pixmap->data[0], CAIRO_FORMAT_RGB24,
+ pixmap->width, pixmap->height,
+ pixmap->stride);
+ context = cairo_create(surface);
+}
+
+void CairoPixmapRenderer::Clear() {
+ CairoRenderer::Clear();
+
+ if(surface != NULL) cairo_surface_destroy(surface);
+ surface = NULL;
+}
+
+std::shared_ptr<Pixmap> CairoPixmapRenderer::ReadFrame() {
+ std::shared_ptr<Pixmap> result = pixmap->Copy();
+ result->ConvertTo(Pixmap::Format::RGBA);
+ return result;
+}
+
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// OpenGL 1 based rendering interface.
+//
+// Copyright 2016 whitequark
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+
+#ifdef WIN32
+// Include after solvespace.h to avoid identifier clashes.
+# include <windows.h> // required by GL headers
+#endif
+#ifdef __APPLE__
+# include <OpenGL/gl.h>
+# include <OpenGL/glu.h>
+#else
+# include <GL/gl.h>
+# include <GL/glu.h>
+#endif
+
+namespace SolveSpace {
+
+//-----------------------------------------------------------------------------
+// Checks for buggy OpenGL renderers
+//-----------------------------------------------------------------------------
+
+// Intel GPUs with Mesa on *nix render thin lines poorly.
+static bool HasIntelThinLineQuirk()
+{
+ static bool quirkChecked, quirkEnabled;
+ if(!quirkChecked) {
+ const char *ident = (const char*)glGetString(GL_VENDOR);
+ if(ident != NULL) {
+ quirkChecked = true;
+ quirkEnabled = !strcmp(ident, "Intel Open Source Technology Center");
+ }
+ }
+ return quirkEnabled;
+}
+
+// The default Windows GL renderer really does implement GL 1.1,
+// and cannot handle non-power-of-2 textures, which is legal.
+static bool HasGl1V1Quirk()
+{
+ static bool quirkChecked, quirkEnabled;
+ if(!quirkChecked) {
+ const char *ident = (const char*)glGetString(GL_VERSION);
+ if(ident != NULL) {
+ quirkChecked = true;
+ quirkEnabled = !strcmp(ident, "1.1.0");
+ }
+ }
+ return quirkEnabled;
+}
+
+//-----------------------------------------------------------------------------
+// Thin wrappers around OpenGL functions to fix bugs, adapt them to our
+// data structures, etc.
+//-----------------------------------------------------------------------------
+
+static inline void ssglNormal3v(Vector n) {
+ glNormal3d(n.x, n.y, n.z);
+}
+
+static inline void ssglVertex3v(Vector v) {
+ glVertex3d(v.x, v.y, v.z);
+}
+
+void ssglLineWidth(double width) {
+ if(HasIntelThinLineQuirk() && width < 1.6)
+ width = 1.6;
+
+ glLineWidth((GLfloat)width);
+}
+
+static inline void ssglColorRGBA(RgbaColor color) {
+ glColor4d(color.redF(), color.greenF(), color.blueF(), color.alphaF());
+}
+
+static inline void ssglMaterialRGBA(GLenum side, RgbaColor color) {
+ GLfloat mpb[] = { color.redF(), color.greenF(), color.blueF(), color.alphaF() };
+ glMaterialfv(side, GL_AMBIENT_AND_DIFFUSE, mpb);
+}
+
+static void ssglDepthRange(Canvas::Layer layer, int zIndex) {
+ switch(layer) {
+ case Canvas::Layer::NORMAL:
+ case Canvas::Layer::FRONT:
+ case Canvas::Layer::BACK:
+ glDepthFunc(GL_LEQUAL);
+ glDepthMask(GL_TRUE);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ break;
+
+ case Canvas::Layer::DEPTH_ONLY:
+ glDepthFunc(GL_LEQUAL);
+ glDepthMask(GL_TRUE);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ break;
+
+ case Canvas::Layer::OCCLUDED:
+ glDepthFunc(GL_GREATER);
+ glDepthMask(GL_FALSE);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ break;
+ }
+
+ switch(layer) {
+ case Canvas::Layer::FRONT:
+ glDepthRange(0.0, 0.0);
+ break;
+
+ case Canvas::Layer::BACK:
+ glDepthRange(1.0, 1.0);
+ break;
+
+ case Canvas::Layer::NORMAL:
+ case Canvas::Layer::DEPTH_ONLY:
+ case Canvas::Layer::OCCLUDED:
+ // The size of this step depends on the resolution of the Z buffer; for
+ // a 16-bit buffer, this should be fine.
+ double offset = 1.0 / (65535 * 0.8) * zIndex;
+ glDepthRange(0.1 - offset, 1.0 - offset);
+ break;
+ }
+}
+
+static void ssglFillPattern(Canvas::FillPattern pattern) {
+ static bool Init;
+ static GLubyte MaskA[(32*32)/8];
+ static GLubyte MaskB[(32*32)/8];
+ if(!Init) {
+ int x, y;
+ for(x = 0; x < 32; x++) {
+ for(y = 0; y < 32; y++) {
+ int i = y*4 + x/8, b = x % 8;
+ int ym = y % 4, xm = x % 4;
+ for(int k = 0; k < 2; k++) {
+ if(xm >= 1 && xm <= 2 && ym >= 1 && ym <= 2) {
+ (k == 0 ? MaskB : MaskA)[i] |= (0x80 >> b);
+ }
+ ym = (ym + 2) % 4; xm = (xm + 2) % 4;
+ }
+ }
+ }
+ Init = true;
+ }
+
+ switch(pattern) {
+ case Canvas::FillPattern::SOLID:
+ glDisable(GL_POLYGON_STIPPLE);
+ break;
+
+ case Canvas::FillPattern::CHECKERED_A:
+ glEnable(GL_POLYGON_STIPPLE);
+ glPolygonStipple(MaskA);
+ break;
+
+ case Canvas::FillPattern::CHECKERED_B:
+ glEnable(GL_POLYGON_STIPPLE);
+ glPolygonStipple(MaskB);
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// OpenGL 1 / compatibility profile based renderer
+//-----------------------------------------------------------------------------
+
+class OpenGl1Renderer final : public ViewportCanvas {
+public:
+ Camera camera;
+ Lighting lighting;
+ // Cached OpenGL state.
+ struct {
+ bool drawing;
+ GLenum mode;
+ hStroke hcs;
+ Stroke *stroke;
+ hFill hcf;
+ Fill *fill;
+ std::weak_ptr<const Pixmap> texture;
+ } current;
+
+ // List-initialize current to work around MSVC bug 746973.
+ OpenGl1Renderer() : camera(), lighting(), current({}) {}
+
+ const Camera &GetCamera() const override { return camera; }
+
+ void DrawLine(const Vector &a, const Vector &b, hStroke hcs) override;
+ void DrawEdges(const SEdgeList &el, hStroke hcs) override;
+ bool DrawBeziers(const SBezierList &bl, hStroke hcs) override { return false; }
+ void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) override;
+ void DrawVectorText(const std::string &text, double height,
+ const Vector &o, const Vector &u, const Vector &v,
+ hStroke hcs) override;
+
+ void DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
+ hFill hcf) override;
+ void DrawPoint(const Vector &o, hStroke hcs) override;
+ void DrawPolygon(const SPolygon &p, hFill hcf) override;
+ void DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack) override;
+ void DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) override;
+ void DrawPixmap(std::shared_ptr<const Pixmap> pm,
+ const Vector &o, const Vector &u, const Vector &v,
+ const Point2d &ta, const Point2d &tb, hFill hcf) override;
+ void InvalidatePixmap(std::shared_ptr<const Pixmap> pm) override;
+
+ void SelectPrimitive(unsigned mode);
+ void UnSelectPrimitive();
+ Stroke *SelectStroke(hStroke hcs);
+ Fill *SelectFill(hFill hcf);
+ void SelectTexture(std::shared_ptr<const Pixmap> pm);
+ void DoFatLineEndcap(const Vector &p, const Vector &u, const Vector &v);
+ void DoFatLine(const Vector &a, const Vector &b, double width);
+ void DoLine(const Vector &a, const Vector &b, hStroke hcs);
+ void DoPoint(Vector p, double radius);
+ void DoStippledLine(const Vector &a, const Vector &b, hStroke hcs, double phase = 0.0);
+
+ void UpdateProjection();
+ void SetCamera(const Camera &camera) override;
+ void SetLighting(const Lighting &lighting) override;
+
+ void StartFrame() override;
+ void FlushFrame() override;
+ void FinishFrame() override;
+ std::shared_ptr<Pixmap> ReadFrame() override;
+
+ void GetIdent(const char **vendor, const char **renderer, const char **version) override;
+};
+
+//-----------------------------------------------------------------------------
+// A simple OpenGL state tracker to group consecutive draw calls.
+//-----------------------------------------------------------------------------
+
+void OpenGl1Renderer::SelectPrimitive(GLenum mode) {
+ if(current.drawing && current.mode == mode) {
+ return;
+ } else if(current.drawing) {
+ glEnd();
+ }
+ glBegin(mode);
+ current.drawing = true;
+ current.mode = mode;
+}
+
+void OpenGl1Renderer::UnSelectPrimitive() {
+ if(!current.drawing) return;
+ glEnd();
+ current.drawing = false;
+}
+
+Canvas::Stroke *OpenGl1Renderer::SelectStroke(hStroke hcs) {
+ if(current.hcs == hcs) return current.stroke;
+
+ Stroke *stroke = strokes.FindById(hcs);
+ UnSelectPrimitive();
+ ssglColorRGBA(stroke->color);
+ ssglDepthRange(stroke->layer, stroke->zIndex);
+ ssglLineWidth(stroke->WidthPx(camera));
+ // Fat lines and points are quads affected by glPolygonStipple, so make sure
+ // they are displayed correctly.
+ ssglFillPattern(FillPattern::SOLID);
+ glDisable(GL_TEXTURE_2D);
+
+ current.hcs = hcs;
+ current.stroke = stroke;
+ current.hcf = {};
+ current.fill = NULL;
+ current.texture.reset();
+ return stroke;
+}
+
+Canvas::Fill *OpenGl1Renderer::SelectFill(hFill hcf) {
+ if(current.hcf == hcf) return current.fill;
+
+ Fill *fill = fills.FindById(hcf);
+ UnSelectPrimitive();
+ ssglColorRGBA(fill->color);
+ ssglDepthRange(fill->layer, fill->zIndex);
+ ssglFillPattern(fill->pattern);
+ glDisable(GL_TEXTURE_2D);
+
+ current.hcs = {};
+ current.stroke = NULL;
+ current.hcf = hcf;
+ current.fill = fill;
+ current.texture.reset();
+ return fill;
+}
+
+static int RoundUpToPowerOfTwo(int v)
+{
+ for(int i = 0; i < 31; i++) {
+ int vt = (1 << i);
+ if(vt >= v) {
+ return vt;
+ }
+ }
+ return 0;
+}
+
+void OpenGl1Renderer::SelectTexture(std::shared_ptr<const Pixmap> pm) {
+ if(current.texture.lock() == pm) return;
+
+ glBindTexture(GL_TEXTURE_2D, 1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ GLenum format = 0;
+ switch(pm->format) {
+ case Pixmap::Format::RGBA: format = GL_RGBA; break;
+ case Pixmap::Format::RGB: format = GL_RGB; break;
+ case Pixmap::Format::A: format = GL_ALPHA; break;
+ case Pixmap::Format::BGRA:
+ case Pixmap::Format::BGR:
+ ssassert(false, "Unexpected pixmap format");
+ }
+
+ if(!HasGl1V1Quirk()) {
+ glTexImage2D(GL_TEXTURE_2D, 0, format, pm->width, pm->height, 0,
+ format, GL_UNSIGNED_BYTE, &pm->data[0]);
+ } else {
+ GLsizei width = RoundUpToPowerOfTwo(pm->width);
+ GLsizei height = RoundUpToPowerOfTwo(pm->height);
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0,
+ format, GL_UNSIGNED_BYTE, 0);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, pm->width, pm->height,
+ format, GL_UNSIGNED_BYTE, &pm->data[0]);
+ }
+
+ glEnable(GL_TEXTURE_2D);
+
+ current.texture = pm;
+}
+
+//-----------------------------------------------------------------------------
+// OpenGL's GL_LINES mode does not work on lines thicker than about 3 px,
+// so we have to draw thicker lines using triangles.
+//-----------------------------------------------------------------------------
+
+void OpenGl1Renderer::DoFatLineEndcap(const Vector &p, const Vector &u, const Vector &v) {
+ // A table of cos and sin of (pi*i/10 + pi/2), as i goes from 0 to 10
+ static const double Circle[11][2] = {
+ { 0.0000, 1.0000 },
+ { -0.3090, 0.9511 },
+ { -0.5878, 0.8090 },
+ { -0.8090, 0.5878 },
+ { -0.9511, 0.3090 },
+ { -1.0000, 0.0000 },
+ { -0.9511, -0.3090 },
+ { -0.8090, -0.5878 },
+ { -0.5878, -0.8090 },
+ { -0.3090, -0.9511 },
+ { 0.0000, -1.0000 },
+ };
+
+ SelectPrimitive(GL_TRIANGLE_FAN);
+ for(auto pc : Circle) {
+ double c = pc[0], s = pc[1];
+ ssglVertex3v(p.Plus(u.ScaledBy(c)).Plus(v.ScaledBy(s)));
+ }
+ UnSelectPrimitive();
+}
+
+void OpenGl1Renderer::DoFatLine(const Vector &a, const Vector &b, double width) {
+ // The half-width of the line we're drawing.
+ double hw = width / 2;
+ Vector ab = b.Minus(a);
+ Vector gn = (camera.projRight).Cross(camera.projUp);
+ Vector abn = (ab.Cross(gn)).WithMagnitude(1);
+ abn = abn.Minus(gn.ScaledBy(gn.Dot(abn)));
+ // So now abn is normal to the projection of ab into the screen, so the
+ // line will always have constant thickness as the view is rotated.
+
+ abn = abn.WithMagnitude(hw);
+ ab = gn.Cross(abn);
+ ab = ab. WithMagnitude(hw);
+
+ // The body of a line is a quad
+ SelectPrimitive(GL_QUADS);
+ ssglVertex3v(a.Plus (abn));
+ ssglVertex3v(b.Plus (abn));
+ ssglVertex3v(b.Minus(abn));
+ ssglVertex3v(a.Minus(abn));
+ // And the line has two semi-circular end caps.
+ DoFatLineEndcap(a, ab, abn);
+ DoFatLineEndcap(b, ab.ScaledBy(-1), abn);
+}
+
+void OpenGl1Renderer::DoLine(const Vector &a, const Vector &b, hStroke hcs) {
+ if(a.Equals(b)) return;
+
+ Stroke *stroke = SelectStroke(hcs);
+ if(stroke->WidthPx(camera) <= 3.0) {
+ SelectPrimitive(GL_LINES);
+ ssglVertex3v(a);
+ ssglVertex3v(b);
+ } else {
+ DoFatLine(a, b, stroke->WidthPx(camera) / camera.scale);
+ }
+}
+
+void OpenGl1Renderer::DoPoint(Vector p, double d) {
+ if(d <= 3.0) {
+ Vector u = camera.projRight.WithMagnitude(d / 2.0 / camera.scale);
+
+ SelectPrimitive(GL_LINES);
+ ssglVertex3v(p.Minus(u));
+ ssglVertex3v(p.Plus(u));
+ } else {
+ Vector u = camera.projRight.WithMagnitude(d / 2.0 / camera.scale);
+ Vector v = camera.projUp.WithMagnitude(d / 2.0 / camera.scale);
+
+ DoFatLineEndcap(p, u, v);
+ DoFatLineEndcap(p, u.ScaledBy(-1.0), v);
+ }
+}
+
+void OpenGl1Renderer::DoStippledLine(const Vector &a, const Vector &b, hStroke hcs, double phase) {
+ Stroke *stroke = SelectStroke(hcs);
+
+ if(stroke->stipplePattern == StipplePattern::CONTINUOUS) {
+ DoLine(a, b, hcs);
+ return;
+ }
+
+ double scale = stroke->StippleScaleMm(camera);
+ const std::vector<double> &dashes = StipplePatternDashes(stroke->stipplePattern);
+ double length = StipplePatternLength(stroke->stipplePattern) * scale;
+
+ phase -= floor(phase / length) * length;
+
+ double curPhase = 0.0;
+ size_t curDash;
+ for(curDash = 0; curDash < dashes.size(); curDash++) {
+ curPhase += dashes[curDash] * scale;
+ if(phase < curPhase) break;
+ }
+
+ Vector dir = b.Minus(a);
+ double len = dir.Magnitude();
+ dir = dir.WithMagnitude(1.0);
+
+ double cur = 0.0;
+ Vector curPos = a;
+ double width = stroke->WidthMm(camera);
+
+ double curDashLen = (curPhase - phase) / scale;
+ while(cur < len) {
+ double next = std::min(len, cur + curDashLen * scale);
+ Vector nextPos = curPos.Plus(dir.ScaledBy(next - cur));
+ if(curDash % 2 == 0) {
+ if(curDashLen <= LENGTH_EPS) {
+ DoPoint(curPos, width);
+ } else {
+ DoLine(curPos, nextPos, hcs);
+ }
+ }
+ cur = next;
+ curPos = nextPos;
+ curDash++;
+ curDashLen = dashes[curDash % dashes.size()];
+ }
+}
+
+//-----------------------------------------------------------------------------
+// A canvas implemented using OpenGL 3 immediate mode.
+//-----------------------------------------------------------------------------
+
+void OpenGl1Renderer::DrawLine(const Vector &a, const Vector &b, hStroke hcs) {
+ DoStippledLine(a, b, hcs);
+}
+
+void OpenGl1Renderer::DrawEdges(const SEdgeList &el, hStroke hcs) {
+ double phase = 0.0;
+ for(const SEdge *e = el.l.First(); e; e = el.l.NextAfter(e)) {
+ DoStippledLine(e->a, e->b, hcs, phase);
+ phase += e->a.Minus(e->b).Magnitude();
+ }
+}
+
+void OpenGl1Renderer::DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) {
+ Vector projDir = camera.projRight.Cross(camera.projUp);
+ double phase = 0.0;
+ switch(drawAs) {
+ case DrawOutlinesAs::EMPHASIZED_AND_CONTOUR:
+ for(const SOutline &o : ol.l) {
+ if(o.IsVisible(projDir) || o.tag != 0) {
+ DoStippledLine(o.a, o.b, hcs, phase);
+ }
+ phase += o.a.Minus(o.b).Magnitude();
+ }
+ break;
+
+ case DrawOutlinesAs::EMPHASIZED_WITHOUT_CONTOUR:
+ for(const SOutline &o : ol.l) {
+ if(!o.IsVisible(projDir) && o.tag != 0) {
+ DoStippledLine(o.a, o.b, hcs, phase);
+ }
+ phase += o.a.Minus(o.b).Magnitude();
+ }
+ break;
+
+ case DrawOutlinesAs::CONTOUR_ONLY:
+ for(const SOutline &o : ol.l) {
+ if(o.IsVisible(projDir)) {
+ DoStippledLine(o.a, o.b, hcs, phase);
+ }
+ phase += o.a.Minus(o.b).Magnitude();
+ }
+ break;
+ }
+}
+
+void OpenGl1Renderer::DrawVectorText(const std::string &text, double height,
+ const Vector &o, const Vector &u, const Vector &v,
+ hStroke hcs) {
+ auto traceEdge = [&](Vector a, Vector b) { DoStippledLine(a, b, hcs); };
+ VectorFont::Builtin()->Trace(height, o, u, v, text, traceEdge, camera);
+}
+
+void OpenGl1Renderer::DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
+ hFill hcf) {
+ SelectFill(hcf);
+ SelectPrimitive(GL_QUADS);
+ ssglVertex3v(a);
+ ssglVertex3v(b);
+ ssglVertex3v(c);
+ ssglVertex3v(d);
+}
+
+void OpenGl1Renderer::DrawPoint(const Vector &o, Canvas::hStroke hcs) {
+ Stroke *stroke = SelectStroke(hcs);
+
+ Canvas::Fill fill = {};
+ fill.layer = stroke->layer;
+ fill.zIndex = stroke->zIndex;
+ fill.color = stroke->color;
+ hFill hcf = GetFill(fill);
+
+ Vector r = camera.projRight.ScaledBy(stroke->width/2.0/camera.scale);
+ Vector u = camera.projUp.ScaledBy(stroke->width/2.0/camera.scale);
+ Vector a = o.Plus (r).Plus (u),
+ b = o.Plus (r).Minus(u),
+ c = o.Minus(r).Minus(u),
+ d = o.Minus(r).Plus (u);
+ DrawQuad(a, b, c, d, hcf);
+}
+
+#ifdef WIN32
+#define SSGL_CALLBACK CALLBACK
+#else
+#define SSGL_CALLBACK
+#endif
+typedef void(SSGL_CALLBACK *GLUCallback)();
+
+static void SSGL_CALLBACK Vertex(Vector *p) {
+ ssglVertex3v(*p);
+}
+static void SSGL_CALLBACK Combine(double coords[3], void *vertexData[4],
+ float weight[4], void **outData) {
+ Vector *n = (Vector *)AllocTemporary(sizeof(Vector));
+ n->x = coords[0];
+ n->y = coords[1];
+ n->z = coords[2];
+
+ *outData = n;
+}
+void OpenGl1Renderer::DrawPolygon(const SPolygon &p, hFill hcf) {
+ UnSelectPrimitive();
+ SelectFill(hcf);
+
+ GLUtesselator *gt = gluNewTess();
+ gluTessCallback(gt, GLU_TESS_BEGIN, (GLUCallback) glBegin);
+ gluTessCallback(gt, GLU_TESS_VERTEX, (GLUCallback) Vertex);
+ gluTessCallback(gt, GLU_TESS_END, (GLUCallback) glEnd);
+ gluTessCallback(gt, GLU_TESS_COMBINE, (GLUCallback) Combine);
+
+ gluTessProperty(gt, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
+
+ ssglNormal3v(p.normal);
+ gluTessNormal(gt, p.normal.x, p.normal.y, p.normal.z);
+
+ gluTessBeginPolygon(gt, NULL);
+ for(const SContour &sc : p.l) {
+ gluTessBeginContour(gt);
+ for(const SPoint &sp : sc.l) {
+ double ap[3] = { sp.p.x, sp.p.y, sp.p.z };
+ gluTessVertex(gt, ap, (GLvoid *) &sp.p);
+ }
+ gluTessEndContour(gt);
+ }
+ gluTessEndPolygon(gt);
+
+ gluDeleteTess(gt);
+}
+
+void OpenGl1Renderer::DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack) {
+ UnSelectPrimitive();
+
+ RgbaColor frontColor = {},
+ backColor = {};
+
+ Fill *frontFill = SelectFill(hcfFront);
+ frontColor = frontFill->color;
+
+ ssglMaterialRGBA(GL_FRONT, frontFill->color);
+ if(hcfBack.v != 0) {
+ Fill *backFill = fills.FindById(hcfBack);
+ backColor = backFill->color;
+ ssassert(frontFill->layer == backFill->layer &&
+ frontFill->zIndex == backFill->zIndex,
+ "frontFill and backFill should belong to the same depth range");
+ ssassert(frontFill->pattern == backFill->pattern,
+ "frontFill and backFill should have the same pattern");
+ glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
+ ssglMaterialRGBA(GL_BACK, backFill->color);
+ } else {
+ glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
+ }
+
+ RgbaColor triangleColor = {};
+ glEnable(GL_LIGHTING);
+ glBegin(GL_TRIANGLES);
+ for(const STriangle &tr : m.l) {
+ if(frontColor.IsEmpty() || backColor.IsEmpty()) {
+ if(triangleColor.IsEmpty() || !triangleColor.Equals(tr.meta.color)) {
+ triangleColor = tr.meta.color;
+ if(frontColor.IsEmpty()) {
+ ssglMaterialRGBA(GL_FRONT, triangleColor);
+ }
+ if(backColor.IsEmpty()) {
+ ssglMaterialRGBA(GL_BACK, triangleColor);
+ }
+ }
+ }
+
+ if(tr.an.EqualsExactly(Vector::From(0, 0, 0))) {
+ // Compute the normal from the vertices
+ ssglNormal3v(tr.Normal());
+ ssglVertex3v(tr.a);
+ ssglVertex3v(tr.b);
+ ssglVertex3v(tr.c);
+ } else {
+ // Use the exact normals that are specified
+ ssglNormal3v(tr.an);
+ ssglVertex3v(tr.a);
+ ssglNormal3v(tr.bn);
+ ssglVertex3v(tr.b);
+ ssglNormal3v(tr.cn);
+ ssglVertex3v(tr.c);
+ }
+ }
+ glEnd();
+ glDisable(GL_LIGHTING);
+}
+
+void OpenGl1Renderer::DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) {
+ SelectFill(hcf);
+ SelectPrimitive(GL_TRIANGLES);
+ size_t facesSize = faces.size();
+ for(const STriangle &tr : m.l) {
+ uint32_t face = tr.meta.face;
+ for(size_t j = 0; j < facesSize; j++) {
+ if(faces[j] != face) continue;
+ ssglVertex3v(tr.a);
+ ssglVertex3v(tr.b);
+ ssglVertex3v(tr.c);
+ break;
+ }
+ }
+}
+
+void OpenGl1Renderer::DrawPixmap(std::shared_ptr<const Pixmap> pm,
+ const Vector &o, const Vector &u, const Vector &v,
+ const Point2d &ta, const Point2d &tb, hFill hcf) {
+ double xfactor = 1.0,
+ yfactor = 1.0;
+ if(HasGl1V1Quirk()) {
+ xfactor = (double)pm->width / RoundUpToPowerOfTwo(pm->width);
+ yfactor = (double)pm->height / RoundUpToPowerOfTwo(pm->height);
+ }
+
+ UnSelectPrimitive();
+ SelectFill(hcf);
+ SelectTexture(pm);
+ SelectPrimitive(GL_QUADS);
+ glTexCoord2d(ta.x * xfactor, ta.y * yfactor);
+ ssglVertex3v(o);
+ glTexCoord2d(ta.x * xfactor, tb.y * yfactor);
+ ssglVertex3v(o.Plus(v));
+ glTexCoord2d(tb.x * xfactor, tb.y * yfactor);
+ ssglVertex3v(o.Plus(u).Plus(v));
+ glTexCoord2d(tb.x * xfactor, ta.y * yfactor);
+ ssglVertex3v(o.Plus(u));
+}
+
+void OpenGl1Renderer::InvalidatePixmap(std::shared_ptr<const Pixmap> pm) {
+ if(current.texture.lock() == pm) {
+ current.texture.reset();
+ }
+}
+
+void OpenGl1Renderer::UpdateProjection() {
+ UnSelectPrimitive();
+
+ glViewport(0, 0,
+ (GLsizei)(camera.width * camera.pixelRatio),
+ (GLsizei)(camera.height * camera.pixelRatio));
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+
+ glScaled(camera.scale * 2.0 / camera.width,
+ camera.scale * 2.0 / camera.height,
+ camera.scale * 1.0 / 30000);
+
+ double mat[16];
+ // Last thing before display is to apply the perspective
+ double clp = camera.tangent * camera.scale;
+ MakeMatrix(mat, 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, clp, 1);
+ glMultMatrixd(mat);
+
+ // Before that, we apply the rotation
+ Vector projRight = camera.projRight,
+ projUp = camera.projUp,
+ n = camera.projUp.Cross(camera.projRight);
+ MakeMatrix(mat, projRight.x, projRight.y, projRight.z, 0,
+ projUp.x, projUp.y, projUp.z, 0,
+ n.x, n.y, n.z, 0,
+ 0, 0, 0, 1);
+ glMultMatrixd(mat);
+
+ // And before that, the translation
+ Vector offset = camera.offset;
+ MakeMatrix(mat, 1, 0, 0, offset.x,
+ 0, 1, 0, offset.y,
+ 0, 0, 1, offset.z,
+ 0, 0, 0, 1);
+ glMultMatrixd(mat);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glClearDepth(1.0);
+ glClear(GL_DEPTH_BUFFER_BIT);
+}
+
+void OpenGl1Renderer::StartFrame() {
+ glEnable(GL_NORMALIZE);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ glEnable(GL_LINE_SMOOTH);
+ glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+ // don't enable GL_POLYGON_SMOOTH; that looks ugly on some graphics cards,
+ // drawn with leaks in the mesh
+
+ glDepthFunc(GL_LEQUAL);
+ glEnable(GL_DEPTH_TEST);
+
+ if(EXACT(lighting.lightIntensity[0] != 0.0)) {
+ glEnable(GL_LIGHT0);
+ GLfloat f = (GLfloat)lighting.lightIntensity[0];
+ GLfloat li0[] = { f, f, f, 1.0f };
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, li0);
+ glLightfv(GL_LIGHT0, GL_SPECULAR, li0);
+
+ Vector ld = camera.VectorFromProjs(lighting.lightDirection[0]);
+ GLfloat ld0[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 };
+ glLightfv(GL_LIGHT0, GL_POSITION, ld0);
+ }
+
+ if(EXACT(lighting.lightIntensity[1] != 0.0)) {
+ glEnable(GL_LIGHT1);
+ GLfloat f = (GLfloat)lighting.lightIntensity[1];
+ GLfloat li0[] = { f, f, f, 1.0f };
+ glLightfv(GL_LIGHT1, GL_DIFFUSE, li0);
+ glLightfv(GL_LIGHT1, GL_SPECULAR, li0);
+
+ Vector ld = camera.VectorFromProjs(lighting.lightDirection[1]);
+ GLfloat ld0[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 };
+ glLightfv(GL_LIGHT1, GL_POSITION, ld0);
+ }
+
+ if(EXACT(lighting.ambientIntensity != 0.0)) {
+ GLfloat ambient[4] = { (float)lighting.ambientIntensity,
+ (float)lighting.ambientIntensity,
+ (float)lighting.ambientIntensity, 1 };
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
+ }
+
+ glClearColor(lighting.backgroundColor.redF(), lighting.backgroundColor.greenF(),
+ lighting.backgroundColor.blueF(), lighting.backgroundColor.alphaF());
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glClearDepth(1.0);
+ glClear(GL_DEPTH_BUFFER_BIT);
+}
+
+void OpenGl1Renderer::FlushFrame() {
+ UnSelectPrimitive();
+
+ glFlush();
+}
+
+void OpenGl1Renderer::FinishFrame() {
+ glFinish();
+
+ GLenum error = glGetError();
+ if(error != GL_NO_ERROR) {
+ dbp("glGetError() == 0x%X %s", error, gluErrorString(error));
+ }
+}
+
+std::shared_ptr<Pixmap> OpenGl1Renderer::ReadFrame() {
+ int width = (int)(camera.width * camera.pixelRatio);
+ int height = (int)(camera.height * camera.pixelRatio);
+ std::shared_ptr<Pixmap> pixmap =
+ Pixmap::Create(Pixmap::Format::RGB, (size_t)width, (size_t)height);
+ glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, &pixmap->data[0]);
+ ssassert(glGetError() == GL_NO_ERROR, "Unexpected glReadPixels error");
+ return pixmap;
+}
+
+void OpenGl1Renderer::GetIdent(const char **vendor, const char **renderer, const char **version) {
+ *vendor = (const char *)glGetString(GL_VENDOR);
+ *renderer = (const char *)glGetString(GL_RENDERER);
+ *version = (const char *)glGetString(GL_VERSION);
+}
+
+void OpenGl1Renderer::SetCamera(const Camera &c) {
+ camera = c;
+ UpdateProjection();
+}
+
+void OpenGl1Renderer::SetLighting(const Lighting &l) {
+ lighting = l;
+}
+
+std::shared_ptr<ViewportCanvas> CreateRenderer() {
+ return std::shared_ptr<ViewportCanvas>(new OpenGl1Renderer());
+}
+
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// OpenGL ES 2.0 and OpenGL 3.0 based rendering interface.
+//
+// Copyright 2016 Aleksey Egorov
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+#include "gl3shader.h"
+
+namespace SolveSpace {
+
+class TextureCache {
+public:
+ std::map<std::weak_ptr<const Pixmap>, GLuint,
+ std::owner_less<std::weak_ptr<const Pixmap>>> items;
+
+ bool Lookup(std::shared_ptr<const Pixmap> ptr, GLuint *result) {
+ auto it = items.find(ptr);
+ if(it == items.end()) {
+ GLuint id;
+ glGenTextures(1, &id);
+ items[ptr] = id;
+ *result = id;
+ return false;
+ }
+
+ *result = it->second;
+ return true;
+ }
+
+ void CleanupUnused() {
+ for(auto it = items.begin(); it != items.end();) {
+ if(it->first.expired()) {
+ glDeleteTextures(1, &it->second);
+ it = items.erase(it);
+ continue;
+ }
+ it++;
+ }
+ }
+};
+
+// A canvas that uses the core OpenGL 3 profile, for desktop systems.
+class OpenGl3Renderer final : public ViewportCanvas {
+public:
+ struct SEdgeListItem {
+ hStroke h;
+ SEdgeList lines;
+
+ void Clear() { lines.Clear(); }
+ };
+
+ struct SMeshListItem {
+ hFill h;
+ SIndexedMesh mesh;
+
+ void Clear() { mesh.Clear(); }
+ };
+
+ struct SPointListItem {
+ hStroke h;
+ SIndexedMesh points;
+
+ void Clear() { points.Clear(); }
+ };
+
+ IdList<SEdgeListItem, hStroke> lines;
+ IdList<SMeshListItem, hFill> meshes;
+ IdList<SPointListItem, hStroke> points;
+
+ TextureCache pixmapCache;
+ std::shared_ptr<Pixmap> masks[3];
+
+ bool initialized;
+ StippleAtlas atlas;
+ MeshRenderer meshRenderer;
+ IndexedMeshRenderer imeshRenderer;
+ EdgeRenderer edgeRenderer;
+ OutlineRenderer outlineRenderer;
+
+ Camera camera;
+ Lighting lighting;
+ // Cached OpenGL state.
+ struct {
+ hStroke hcs;
+ Stroke *stroke;
+ hFill hcf;
+ Fill *fill;
+ std::weak_ptr<const Pixmap> texture;
+ } current;
+ const char *vendor = "<uninitialized>";
+ const char *renderer = "<uninitialized>";
+ const char *version = "<uninitialized>";
+
+ // List-initialize current to work around MSVC bug 746973.
+ OpenGl3Renderer() :
+ lines(), meshes(), points(), pixmapCache(), masks(),
+ initialized(), atlas(), meshRenderer(), imeshRenderer(),
+ edgeRenderer(), outlineRenderer(), camera(), lighting(),
+ current({}) {}
+
+ void Init();
+
+ const Camera &GetCamera() const override { return camera; }
+
+ void DrawLine(const Vector &a, const Vector &b, hStroke hcs) override;
+ void DrawEdges(const SEdgeList &el, hStroke hcs) override;
+ bool DrawBeziers(const SBezierList &bl, hStroke hcs) override { return false; }
+ void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs mode) override;
+ void DrawVectorText(const std::string &text, double height,
+ const Vector &o, const Vector &u, const Vector &v,
+ hStroke hcs) override;
+
+ void DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
+ hFill hcf) override;
+ void DrawPoint(const Vector &o, hStroke hs) override;
+ void DrawPolygon(const SPolygon &p, hFill hcf) override;
+ void DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack) override;
+ void DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) override;
+ void DrawPixmap(std::shared_ptr<const Pixmap> pm,
+ const Vector &o, const Vector &u, const Vector &v,
+ const Point2d &ta, const Point2d &tb, hFill hcf) override;
+ void InvalidatePixmap(std::shared_ptr<const Pixmap> pm) override;
+
+ std::shared_ptr<BatchCanvas> CreateBatch() override;
+
+ Stroke *SelectStroke(hStroke hcs);
+ Fill *SelectFill(hFill hcf);
+ void SelectMask(FillPattern pattern);
+ void SelectTexture(std::shared_ptr<const Pixmap> pm);
+ void DoFatLineEndcap(const Vector &p, const Vector &u, const Vector &v);
+ void DoFatLine(const Vector &a, const Vector &b, double width);
+ void DoLine(const Vector &a, const Vector &b, hStroke hcs);
+ void DoPoint(Vector p, hStroke hs);
+ void DoStippledLine(const Vector &a, const Vector &b, hStroke hcs);
+
+ void UpdateProjection();
+ void SetCamera(const Camera &c) override;
+ void SetLighting(const Lighting &l) override;
+
+ void StartFrame() override;
+ void FlushFrame() override;
+ void FinishFrame() override;
+ void Clear() override;
+ std::shared_ptr<Pixmap> ReadFrame() override;
+
+ void GetIdent(const char **vendor, const char **renderer, const char **version) override;
+};
+
+//-----------------------------------------------------------------------------
+// Thin wrappers around OpenGL functions to fix bugs, adapt them to our
+// data structures, etc.
+//-----------------------------------------------------------------------------
+
+static void ssglDepthRange(Canvas::Layer layer, int zIndex) {
+ switch(layer) {
+ case Canvas::Layer::NORMAL:
+ case Canvas::Layer::FRONT:
+ case Canvas::Layer::BACK:
+ glDepthFunc(GL_LEQUAL);
+ glDepthMask(GL_TRUE);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ break;
+
+ case Canvas::Layer::DEPTH_ONLY:
+ glDepthFunc(GL_LEQUAL);
+ glDepthMask(GL_TRUE);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ break;
+
+ case Canvas::Layer::OCCLUDED:
+ glDepthFunc(GL_GREATER);
+ glDepthMask(GL_FALSE);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ break;
+ }
+
+ switch(layer) {
+ case Canvas::Layer::FRONT:
+ glDepthRangef(0.0f, 0.0f);
+ break;
+
+ case Canvas::Layer::BACK:
+ glDepthRangef(1.0f, 1.0f);
+ break;
+
+ case Canvas::Layer::NORMAL:
+ case Canvas::Layer::DEPTH_ONLY:
+ case Canvas::Layer::OCCLUDED:
+ // The size of this step depends on the resolution of the Z buffer; for
+ // a 16-bit buffer, this should be fine.
+ double offset = 1.0 / (65535 * 0.8) * zIndex;
+ glDepthRangef((float)(0.1 - offset), (float)(1.0 - offset));
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// A simple OpenGL state tracker to group consecutive draw calls.
+//-----------------------------------------------------------------------------
+
+Canvas::Stroke *OpenGl3Renderer::SelectStroke(hStroke hcs) {
+ if(current.hcs == hcs) return current.stroke;
+
+ Stroke *stroke = strokes.FindById(hcs);
+ ssglDepthRange(stroke->layer, stroke->zIndex);
+
+ current.hcs = hcs;
+ current.stroke = stroke;
+ current.hcf = {};
+ current.fill = NULL;
+ current.texture.reset();
+ return stroke;
+}
+
+void OpenGl3Renderer::SelectMask(FillPattern pattern) {
+ if(!masks[0]) {
+ masks[0] = Pixmap::Create(Pixmap::Format::A, 32, 32);
+ masks[1] = Pixmap::Create(Pixmap::Format::A, 32, 32);
+ masks[2] = Pixmap::Create(Pixmap::Format::A, 32, 32);
+
+ for(int x = 0; x < 32; x++) {
+ for(int y = 0; y < 32; y++) {
+ masks[0]->data[y * 32 + x] = ((x / 2) % 2 == 0 && (y / 2) % 2 == 0) ? 0xFF : 0x00;
+ masks[1]->data[y * 32 + x] = ((x / 2) % 2 == 1 && (y / 2) % 2 == 1) ? 0xFF : 0x00;
+ masks[2]->data[y * 32 + x] = 0xFF;
+ }
+ }
+ }
+
+ switch(pattern) {
+ case Canvas::FillPattern::SOLID:
+ SelectTexture(masks[2]);
+ break;
+
+ case Canvas::FillPattern::CHECKERED_A:
+ SelectTexture(masks[0]);
+ break;
+
+ case Canvas::FillPattern::CHECKERED_B:
+ SelectTexture(masks[1]);
+ break;
+
+ default: ssassert(false, "Unexpected fill pattern");
+ }
+}
+
+Canvas::Fill *OpenGl3Renderer::SelectFill(hFill hcf) {
+ if(current.hcf == hcf) return current.fill;
+
+ Fill *fill = fills.FindById(hcf);
+ ssglDepthRange(fill->layer, fill->zIndex);
+
+ current.hcs = {};
+ current.stroke = NULL;
+ current.hcf = hcf;
+ current.fill = fill;
+ if(fill->pattern != FillPattern::SOLID) {
+ SelectMask(fill->pattern);
+ } else if(fill->texture) {
+ SelectTexture(fill->texture);
+ } else {
+ SelectMask(FillPattern::SOLID);
+ }
+ return fill;
+}
+
+static bool IsPowerOfTwo(size_t n) {
+ return (n & (n - 1)) == 0;
+}
+
+void OpenGl3Renderer::InvalidatePixmap(std::shared_ptr<const Pixmap> pm) {
+ GLuint id;
+ pixmapCache.Lookup(pm, &id);
+ glBindTexture(GL_TEXTURE_2D, id);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ if(IsPowerOfTwo(pm->width) && IsPowerOfTwo(pm->height)) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ GLenum format = 0;
+ switch(pm->format) {
+ case Pixmap::Format::RGBA: format = GL_RGBA; break;
+ case Pixmap::Format::RGB: format = GL_RGB; break;
+#if defined(HAVE_GLES)
+ case Pixmap::Format::A: format = GL_ALPHA; break;
+#else
+ case Pixmap::Format::A: format = GL_RED; break;
+#endif
+ case Pixmap::Format::BGRA:
+ case Pixmap::Format::BGR:
+ ssassert(false, "Unexpected pixmap format");
+ }
+ glTexImage2D(GL_TEXTURE_2D, 0, format, pm->width, pm->height, 0,
+ format, GL_UNSIGNED_BYTE, &pm->data[0]);
+}
+
+void OpenGl3Renderer::SelectTexture(std::shared_ptr<const Pixmap> pm) {
+ if(current.texture.lock() == pm) return;
+
+ GLuint id;
+ if(!pixmapCache.Lookup(pm, &id)) {
+ InvalidatePixmap(pm);
+ }
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, id);
+ current.texture = pm;
+}
+
+void OpenGl3Renderer::DoLine(const Vector &a, const Vector &b, hStroke hcs) {
+ SEdgeListItem *eli = lines.FindByIdNoOops(hcs);
+ if(eli == NULL) {
+ SEdgeListItem item = {};
+ item.h = hcs;
+ lines.Add(&item);
+ eli = lines.FindByIdNoOops(hcs);
+ }
+
+ eli->lines.AddEdge(a, b);
+}
+
+void OpenGl3Renderer::DoPoint(Vector p, hStroke hs) {
+ SPointListItem *pli = points.FindByIdNoOops(hs);
+ if(pli == NULL) {
+ SPointListItem item = {};
+ item.h = hs;
+ points.Add(&item);
+ pli = points.FindByIdNoOops(hs);
+ }
+
+ pli->points.AddPoint(p);
+}
+
+void OpenGl3Renderer::DoStippledLine(const Vector &a, const Vector &b, hStroke hcs) {
+ Stroke *stroke = strokes.FindById(hcs);
+ if(stroke->stipplePattern != StipplePattern::FREEHAND &&
+ stroke->stipplePattern != StipplePattern::ZIGZAG)
+ {
+ DoLine(a, b, hcs);
+ return;
+ }
+
+ const char *patternSeq = NULL;
+ Stroke s = *stroke;
+ s.stipplePattern = StipplePattern::CONTINUOUS;
+ hcs = GetStroke(s);
+ switch(stroke->stipplePattern) {
+ case StipplePattern::CONTINUOUS: DoLine(a, b, hcs); return;
+ case StipplePattern::SHORT_DASH: patternSeq = "- "; break;
+ case StipplePattern::DASH: patternSeq = "- "; break;
+ case StipplePattern::LONG_DASH: patternSeq = "_ "; break;
+ case StipplePattern::DASH_DOT: patternSeq = "-."; break;
+ case StipplePattern::DASH_DOT_DOT: patternSeq = "-.."; break;
+ case StipplePattern::DOT: patternSeq = "."; break;
+ case StipplePattern::FREEHAND: patternSeq = "~"; break;
+ case StipplePattern::ZIGZAG: patternSeq = "~__"; break;
+ }
+
+ Vector dir = b.Minus(a);
+ double len = dir.Magnitude();
+ dir = dir.WithMagnitude(1.0);
+
+ const char *si = patternSeq;
+ double end = len;
+ double ss = stroke->stippleScale / 2.0;
+ do {
+ double start = end;
+ switch(*si) {
+ case ' ':
+ end -= 1.0 * ss;
+ break;
+
+ case '-':
+ start = max(start - 0.5 * ss, 0.0);
+ end = max(start - 2.0 * ss, 0.0);
+ if(start == end) break;
+ DoLine(a.Plus(dir.ScaledBy(start)), a.Plus(dir.ScaledBy(end)), hcs);
+ end = max(end - 0.5 * ss, 0.0);
+ break;
+
+ case '_':
+ end = max(end - 4.0 * ss, 0.0);
+ DoLine(a.Plus(dir.ScaledBy(start)), a.Plus(dir.ScaledBy(end)), hcs);
+ break;
+
+ case '.':
+ end = max(end - 0.5 * ss, 0.0);
+ if(end == 0.0) break;
+ DoPoint(a.Plus(dir.ScaledBy(end)), hcs);
+ end = max(end - 0.5 * ss, 0.0);
+ break;
+
+ case '~': {
+ Vector ab = b.Minus(a);
+ Vector gn = (camera.projRight).Cross(camera.projUp);
+ Vector abn = (ab.Cross(gn)).WithMagnitude(1);
+ abn = abn.Minus(gn.ScaledBy(gn.Dot(abn)));
+ double pws = 2.0 * stroke->width / camera.scale;
+
+ end = max(end - 0.5 * ss, 0.0);
+ Vector aa = a.Plus(dir.ScaledBy(start));
+ Vector bb = a.Plus(dir.ScaledBy(end))
+ .Plus(abn.ScaledBy(pws * (start - end) / (0.5 * ss)));
+ DoLine(aa, bb, hcs);
+ if(end == 0.0) break;
+
+ start = end;
+ end = max(end - 1.0 * ss, 0.0);
+ aa = a.Plus(dir.ScaledBy(end))
+ .Plus(abn.ScaledBy(pws))
+ .Minus(abn.ScaledBy(2.0 * pws * (start - end) / ss));
+ DoLine(bb, aa, hcs);
+ if(end == 0.0) break;
+
+ start = end;
+ end = max(end - 0.5 * ss, 0.0);
+ bb = a.Plus(dir.ScaledBy(end))
+ .Minus(abn.ScaledBy(pws))
+ .Plus(abn.ScaledBy(pws * (start - end) / (0.5 * ss)));
+ DoLine(aa, bb, hcs);
+ break;
+ }
+
+ default: ssassert(false, "Unexpected stipple pattern element");
+ }
+ if(*(++si) == 0) si = patternSeq;
+ } while(end > 0.0);
+}
+
+//-----------------------------------------------------------------------------
+// A canvas implemented using OpenGL 3 vertex buffer objects.
+//-----------------------------------------------------------------------------
+
+void OpenGl3Renderer::Init() {
+ atlas.Init();
+ edgeRenderer.Init(&atlas);
+ outlineRenderer.Init(&atlas);
+ meshRenderer.Init();
+ imeshRenderer.Init();
+
+ vendor = (const char *)glGetString(GL_VENDOR);
+ renderer = (const char *)glGetString(GL_RENDERER);
+ version = (const char *)glGetString(GL_VERSION);
+
+#if !defined(HAVE_GLES) && !defined(__APPLE__)
+ GLuint array;
+ glGenVertexArrays(1, &array);
+ glBindVertexArray(array);
+#endif
+ UpdateProjection();
+}
+
+void OpenGl3Renderer::DrawLine(const Vector &a, const Vector &b, hStroke hcs) {
+ DoStippledLine(a, b, hcs);
+}
+
+void OpenGl3Renderer::DrawEdges(const SEdgeList &el, hStroke hcs) {
+ for(const SEdge &e : el.l) {
+ DoStippledLine(e.a, e.b, hcs);
+ }
+}
+
+void OpenGl3Renderer::DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs mode) {
+ if(ol.l.IsEmpty())
+ return;
+
+ Stroke *stroke = SelectStroke(hcs);
+ ssassert(stroke->stipplePattern != StipplePattern::ZIGZAG &&
+ stroke->stipplePattern != StipplePattern::FREEHAND,
+ "ZIGZAG and FREEHAND not supported for outlines");
+
+ outlineRenderer.SetStroke(*stroke, 1.0 / camera.scale);
+ outlineRenderer.Draw(ol, mode);
+}
+
+void OpenGl3Renderer::DrawVectorText(const std::string &text, double height,
+ const Vector &o, const Vector &u, const Vector &v,
+ hStroke hcs) {
+ SEdgeListItem *eli = lines.FindByIdNoOops(hcs);
+ if(eli == NULL) {
+ SEdgeListItem item = {};
+ item.h = hcs;
+ lines.Add(&item);
+ eli = lines.FindByIdNoOops(hcs);
+ }
+ SEdgeList &lines = eli->lines;
+ auto traceEdge = [&](Vector a, Vector b) { lines.AddEdge(a, b); };
+ VectorFont::Builtin()->Trace(height, o, u, v, text, traceEdge, camera);
+}
+
+void OpenGl3Renderer::DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
+ hFill hcf) {
+ SMeshListItem *li = meshes.FindByIdNoOops(hcf);
+ if(li == NULL) {
+ SMeshListItem item = {};
+ item.h = hcf;
+ meshes.Add(&item);
+ li = meshes.FindByIdNoOops(hcf);
+ }
+ li->mesh.AddQuad(a, b, c, d);
+}
+
+void OpenGl3Renderer::DrawPoint(const Vector &o, hStroke hs) {
+ DoPoint(o, hs);
+}
+
+void OpenGl3Renderer::DrawPolygon(const SPolygon &p, hFill hcf) {
+ Fill *fill = SelectFill(hcf);
+
+ SMesh m = {};
+ p.TriangulateInto(&m);
+ meshRenderer.UseFilled(*fill);
+ meshRenderer.Draw(m);
+ m.Clear();
+}
+
+void OpenGl3Renderer::DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack) {
+ ssassert(false, "Not implemented");
+}
+
+void OpenGl3Renderer::DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) {
+ if(faces.empty()) return;
+
+ Fill *fill = SelectFill(hcf);
+
+ SMesh facesMesh = {};
+ for(uint32_t f : faces) {
+ for(const STriangle &t : m.l) {
+ if(f != t.meta.face) continue;
+ facesMesh.l.Add(&t);
+ }
+ }
+
+ meshRenderer.UseFilled(*fill);
+ meshRenderer.Draw(facesMesh);
+ facesMesh.Clear();
+}
+
+void OpenGl3Renderer::DrawPixmap(std::shared_ptr<const Pixmap> pm,
+ const Vector &o, const Vector &u, const Vector &v,
+ const Point2d &ta, const Point2d &tb, hFill hcf) {
+ Fill fill = *fills.FindById(hcf);
+ fill.texture = pm;
+ hcf = GetFill(fill);
+
+ SMeshListItem *mli = meshes.FindByIdNoOops(hcf);
+ if(mli == NULL) {
+ SMeshListItem item = {};
+ item.h = hcf;
+ meshes.Add(&item);
+ mli = meshes.FindByIdNoOops(hcf);
+ }
+
+ mli->mesh.AddPixmap(o, u, v, ta, tb);
+}
+
+void OpenGl3Renderer::UpdateProjection() {
+ glViewport(0, 0,
+ (int)(camera.width * camera.pixelRatio),
+ (int)(camera.height * camera.pixelRatio));
+
+ double mat1[16];
+ double mat2[16];
+
+ double sx = camera.scale * 2.0 / camera.width;
+ double sy = camera.scale * 2.0 / camera.height;
+ double sz = camera.scale * 1.0 / 30000;
+
+ MakeMatrix(mat1,
+ sx, 0, 0, 0,
+ 0, sy, 0, 0,
+ 0, 0, sz, 0,
+ 0, 0, 0, 1
+ );
+
+ // Last thing before display is to apply the perspective
+ double clp = camera.tangent * camera.scale;
+ MakeMatrix(mat2,
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, clp, 1
+ );
+
+ double projection[16];
+ MultMatrix(mat1, mat2, projection);
+
+ // Before that, we apply the rotation
+ Vector u = camera.projRight,
+ v = camera.projUp,
+ n = camera.projUp.Cross(camera.projRight);
+ MakeMatrix(mat1,
+ u.x, u.y, u.z, 0,
+ v.x, v.y, v.z, 0,
+ n.x, n.y, n.z, 0,
+ 0, 0, 0, 1
+ );
+
+ // And before that, the translation
+ Vector o = camera.offset;
+ MakeMatrix(mat2,
+ 1, 0, 0, o.x,
+ 0, 1, 0, o.y,
+ 0, 0, 1, o.z,
+ 0, 0, 0, 1
+ );
+
+ double modelview[16];
+ MultMatrix(mat1, mat2, modelview);
+
+ imeshRenderer.SetProjection(projection);
+ imeshRenderer.SetModelview(modelview);
+ meshRenderer.SetProjection(projection);
+ meshRenderer.SetModelview(modelview);
+ edgeRenderer.SetProjection(projection);
+ edgeRenderer.SetModelview(modelview);
+ outlineRenderer.SetProjection(projection);
+ outlineRenderer.SetModelview(modelview);
+
+ glClearDepthf(1.0f);
+ glClear(GL_DEPTH_BUFFER_BIT);
+}
+
+void OpenGl3Renderer::StartFrame() {
+ if(!initialized) {
+ Init();
+ initialized = true;
+ }
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ glDepthFunc(GL_LEQUAL);
+ glEnable(GL_DEPTH_TEST);
+
+ RgbaColor backgroundColor = lighting.backgroundColor;
+ glClearColor(backgroundColor.redF(), backgroundColor.greenF(),
+ backgroundColor.blueF(), backgroundColor.alphaF());
+ glClearDepthf(1.0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glFrontFace(GL_CW);
+}
+
+void OpenGl3Renderer::FlushFrame() {
+ for(SMeshListItem &li : meshes) {
+ Fill *fill = SelectFill(li.h);
+
+ imeshRenderer.UseFilled(*fill);
+ imeshRenderer.Draw(li.mesh);
+ li.mesh.Clear();
+ }
+ meshes.Clear();
+
+ for(SEdgeListItem &eli : lines) {
+ Stroke *stroke = SelectStroke(eli.h);
+
+ edgeRenderer.SetStroke(*stroke, 1.0 / camera.scale);
+ edgeRenderer.Draw(eli.lines);
+ eli.lines.Clear();
+ }
+ lines.Clear();
+
+ for(SPointListItem &li : points) {
+ Stroke *stroke = SelectStroke(li.h);
+
+ imeshRenderer.UsePoint(*stroke, 1.0 / camera.scale);
+ imeshRenderer.Draw(li.points);
+ li.points.Clear();
+ }
+ points.Clear();
+
+ glFlush();
+}
+
+void OpenGl3Renderer::FinishFrame() {
+ glFinish();
+
+ GLenum error = glGetError();
+ if(error != GL_NO_ERROR) {
+ dbp("glGetError() == 0x%X", error);
+ }
+}
+
+void OpenGl3Renderer::Clear() {
+ ViewportCanvas::Clear();
+ pixmapCache.CleanupUnused();
+}
+
+std::shared_ptr<Pixmap> OpenGl3Renderer::ReadFrame() {
+ int width = (int)(camera.width * camera.pixelRatio);
+ int height = (int)(camera.height * camera.pixelRatio);
+ std::shared_ptr<Pixmap> pixmap =
+ Pixmap::Create(Pixmap::Format::RGBA, (size_t)width, (size_t)height);
+ glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, &pixmap->data[0]);
+ ssassert(glGetError() == GL_NO_ERROR, "Unexpected glReadPixels error");
+ return pixmap;
+}
+
+void OpenGl3Renderer::GetIdent(const char **vendor, const char **renderer, const char **version) {
+ *vendor = this->vendor;
+ *renderer = this->renderer;
+ *version = this->version;
+}
+
+void OpenGl3Renderer::SetCamera(const Camera &c) {
+ camera = c;
+ if(initialized) {
+ UpdateProjection();
+ }
+}
+
+void OpenGl3Renderer::SetLighting(const Lighting &l) {
+ lighting = l;
+}
+
+//-----------------------------------------------------------------------------
+// A batch canvas implemented using OpenGL 3 vertex buffer objects.
+//-----------------------------------------------------------------------------
+
+class DrawCall {
+public:
+ virtual Canvas::Layer GetLayer() const = 0;
+ virtual int GetZIndex() const = 0;
+
+ virtual void Draw(OpenGl3Renderer *renderer) = 0;
+ virtual void Remove(OpenGl3Renderer *renderer) = 0;
+};
+
+class EdgeDrawCall final : public DrawCall {
+public:
+ // Key
+ Canvas::Stroke stroke;
+ // Data
+ EdgeRenderer::Handle handle;
+
+ Canvas::Layer GetLayer() const override { return stroke.layer; }
+ int GetZIndex() const override { return stroke.zIndex; }
+
+ static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SEdgeList &el,
+ Canvas::Stroke *stroke) {
+ EdgeDrawCall *dc = new EdgeDrawCall();
+ dc->stroke = *stroke;
+ dc->handle = renderer->edgeRenderer.Add(el);
+ return std::shared_ptr<DrawCall>(dc);
+ }
+
+ void Draw(OpenGl3Renderer *renderer) override {
+ ssglDepthRange(stroke.layer, stroke.zIndex);
+ renderer->edgeRenderer.SetStroke(stroke, 1.0 / renderer->camera.scale);
+ renderer->edgeRenderer.Draw(handle);
+ }
+
+ void Remove(OpenGl3Renderer *renderer) override {
+ renderer->edgeRenderer.Remove(handle);
+ }
+};
+
+class OutlineDrawCall final : public DrawCall {
+public:
+ // Key
+ Canvas::Stroke stroke;
+ // Data
+ OutlineRenderer::Handle handle;
+ Canvas::DrawOutlinesAs drawAs;
+
+ Canvas::Layer GetLayer() const override { return stroke.layer; }
+ int GetZIndex() const override { return stroke.zIndex; }
+
+ static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SOutlineList &ol,
+ Canvas::Stroke *stroke,
+ Canvas::DrawOutlinesAs drawAs) {
+ OutlineDrawCall *dc = new OutlineDrawCall();
+ dc->stroke = *stroke;
+ dc->handle = renderer->outlineRenderer.Add(ol);
+ dc->drawAs = drawAs;
+ return std::shared_ptr<DrawCall>(dc);
+ }
+
+ void Draw(OpenGl3Renderer *renderer) override {
+ ssglDepthRange(stroke.layer, stroke.zIndex);
+ renderer->outlineRenderer.SetStroke(stroke, 1.0 / renderer->camera.scale);
+ renderer->outlineRenderer.Draw(handle, drawAs);
+ }
+
+ void Remove(OpenGl3Renderer *renderer) override {
+ renderer->outlineRenderer.Remove(handle);
+ }
+};
+
+class PointDrawCall final : public DrawCall {
+public:
+ // Key
+ Canvas::Stroke stroke;
+ // Data
+ IndexedMeshRenderer::Handle handle;
+
+ Canvas::Layer GetLayer() const override { return stroke.layer; }
+ int GetZIndex() const override { return stroke.zIndex; }
+
+ static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SIndexedMesh &mesh,
+ Canvas::Stroke *stroke) {
+ PointDrawCall *dc = new PointDrawCall();
+ dc->stroke = *stroke;
+ dc->handle = renderer->imeshRenderer.Add(mesh);
+ return std::shared_ptr<DrawCall>(dc);
+ }
+
+ void Draw(OpenGl3Renderer *renderer) override {
+ ssglDepthRange(stroke.layer, stroke.zIndex);
+ renderer->imeshRenderer.UsePoint(stroke, 1.0 / renderer->camera.scale);
+ renderer->imeshRenderer.Draw(handle);
+ }
+
+ void Remove(OpenGl3Renderer *renderer) override {
+ renderer->imeshRenderer.Remove(handle);
+ }
+};
+
+class PixmapDrawCall final : public DrawCall {
+public:
+ // Key
+ Canvas::Fill fill;
+ // Data
+ IndexedMeshRenderer::Handle handle;
+
+ Canvas::Layer GetLayer() const override { return fill.layer; }
+ int GetZIndex() const override { return fill.zIndex; }
+
+ static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SIndexedMesh &mesh,
+ Canvas::Fill *fill) {
+ PixmapDrawCall *dc = new PixmapDrawCall();
+ dc->fill = *fill;
+ dc->handle = renderer->imeshRenderer.Add(mesh);
+ return std::shared_ptr<DrawCall>(dc);
+ }
+
+ void Draw(OpenGl3Renderer *renderer) override {
+ ssglDepthRange(fill.layer, fill.zIndex);
+ if(fill.pattern != Canvas::FillPattern::SOLID) {
+ renderer->SelectMask(fill.pattern);
+ } else if(fill.texture) {
+ renderer->SelectTexture(fill.texture);
+ } else {
+ renderer->SelectMask(Canvas::FillPattern::SOLID);
+ }
+ renderer->imeshRenderer.UseFilled(fill);
+ renderer->imeshRenderer.Draw(handle);
+ }
+
+ void Remove(OpenGl3Renderer *renderer) override {
+ renderer->imeshRenderer.Remove(handle);
+ }
+};
+
+class MeshDrawCall final : public DrawCall {
+public:
+ // Key
+ Canvas::Fill fillFront;
+ // Data
+ MeshRenderer::Handle handle;
+ Canvas::Fill fillBack;
+ bool hasFillBack;
+ bool isShaded;
+
+ Canvas::Layer GetLayer() const override { return fillFront.layer; }
+ int GetZIndex() const override { return fillFront.zIndex; }
+
+ static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SMesh &m,
+ Canvas::Fill *fillFront, Canvas::Fill *fillBack = NULL,
+ bool isShaded = false) {
+ MeshDrawCall *dc = new MeshDrawCall();
+ dc->fillFront = *fillFront;
+ dc->handle = renderer->meshRenderer.Add(m);
+ dc->fillBack = *fillBack;
+ dc->isShaded = isShaded;
+ dc->hasFillBack = (fillBack != NULL);
+ return std::shared_ptr<DrawCall>(dc);
+ }
+
+ void DrawFace(OpenGl3Renderer *renderer, GLenum cullFace, const Canvas::Fill &fill) {
+ glCullFace(cullFace);
+ ssglDepthRange(fill.layer, fill.zIndex);
+ if(fill.pattern != Canvas::FillPattern::SOLID) {
+ renderer->SelectMask(fill.pattern);
+ } else if(fill.texture) {
+ renderer->SelectTexture(fill.texture);
+ } else {
+ renderer->SelectMask(Canvas::FillPattern::SOLID);
+ }
+ if(isShaded) {
+ renderer->meshRenderer.UseShaded(renderer->lighting);
+ } else {
+ renderer->meshRenderer.UseFilled(fill);
+ }
+ renderer->meshRenderer.Draw(handle, /*useColors=*/fill.color.IsEmpty(), fill.color);
+ }
+
+ void Draw(OpenGl3Renderer *renderer) override {
+ glEnable(GL_CULL_FACE);
+ if(hasFillBack)
+ DrawFace(renderer, GL_BACK, fillBack);
+ DrawFace(renderer, GL_FRONT, fillFront);
+ glDisable(GL_CULL_FACE);
+ }
+
+ void Remove(OpenGl3Renderer *renderer) override {
+ renderer->meshRenderer.Remove(handle);
+ }
+};
+
+struct CompareDrawCall {
+ bool operator()(const std::shared_ptr<DrawCall> &a, const std::shared_ptr<DrawCall> &b) const {
+ const Canvas::Layer stackup[] = {
+ Canvas::Layer::BACK,
+ Canvas::Layer::DEPTH_ONLY,
+ Canvas::Layer::NORMAL,
+ Canvas::Layer::OCCLUDED,
+ Canvas::Layer::FRONT
+ };
+
+ int aLayerIndex =
+ std::find(std::begin(stackup), std::end(stackup), a->GetLayer()) - std::begin(stackup);
+ int bLayerIndex =
+ std::find(std::begin(stackup), std::end(stackup), b->GetLayer()) - std::begin(stackup);
+ if(aLayerIndex == bLayerIndex) {
+ return a->GetZIndex() < b->GetZIndex();
+ } else {
+ return aLayerIndex < bLayerIndex;
+ }
+ }
+};
+
+class OpenGl3RendererBatch final : public BatchCanvas {
+public:
+ struct EdgeBuffer {
+ hStroke h;
+ SEdgeList edges;
+
+ void Clear() {
+ edges.Clear();
+ }
+ };
+
+ struct PointBuffer {
+ hStroke h;
+ SIndexedMesh points;
+
+ void Clear() {
+ points.Clear();
+ }
+ };
+
+ OpenGl3Renderer *renderer;
+
+ IdList<EdgeBuffer, hStroke> edgeBuffer;
+ IdList<PointBuffer, hStroke> pointBuffer;
+
+ std::multiset<std::shared_ptr<DrawCall>, CompareDrawCall> drawCalls;
+
+ OpenGl3RendererBatch() : renderer(), edgeBuffer(), pointBuffer() {}
+
+ void DrawLine(const Vector &a, const Vector &b, hStroke hcs) override {
+ EdgeBuffer *eb = edgeBuffer.FindByIdNoOops(hcs);
+ if(!eb) {
+ EdgeBuffer neb = {};
+ neb.h = hcs;
+ edgeBuffer.Add(&neb);
+ eb = edgeBuffer.FindById(hcs);
+ }
+
+ eb->edges.AddEdge(a, b);
+ }
+
+ void DrawEdges(const SEdgeList &el, hStroke hcs) override {
+ EdgeBuffer *eb = edgeBuffer.FindByIdNoOops(hcs);
+ if(!eb) {
+ EdgeBuffer neb = {};
+ neb.h = hcs;
+ edgeBuffer.Add(&neb);
+ eb = edgeBuffer.FindById(hcs);
+ }
+
+ for(const SEdge &e : el.l) {
+ eb->edges.AddEdge(e.a, e.b);
+ }
+ }
+
+ bool DrawBeziers(const SBezierList &bl, hStroke hcs) override {
+ return false;
+ }
+
+ void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) override {
+ drawCalls.emplace(OutlineDrawCall::Create(renderer, ol, strokes.FindById(hcs), drawAs));
+ }
+
+ void DrawVectorText(const std::string &text, double height,
+ const Vector &o, const Vector &u, const Vector &v,
+ hStroke hcs) override {
+ ssassert(false, "Not implemented");
+ }
+
+ void DrawQuad(const Vector &a, const Vector &b, const Vector &c, const Vector &d,
+ hFill hcf) override {
+ ssassert(false, "Not implemented");
+ }
+
+ void DrawPoint(const Vector &o, hStroke hcs) override {
+ PointBuffer *pb = pointBuffer.FindByIdNoOops(hcs);
+ if(!pb) {
+ PointBuffer npb = {};
+ npb.h = hcs;
+ pointBuffer.Add(&npb);
+ pb = pointBuffer.FindById(hcs);
+ }
+
+ pb->points.AddPoint(o);
+ }
+
+ void DrawPolygon(const SPolygon &p, hFill hcf) override {
+ SMesh m = {};
+ p.TriangulateInto(&m);
+ drawCalls.emplace(MeshDrawCall::Create(renderer, m, fills.FindById(hcf),
+ fills.FindById(hcf)));
+ m.Clear();
+ }
+
+ void DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack = {}) override {
+ drawCalls.emplace(MeshDrawCall::Create(renderer, m, fills.FindById(hcfFront),
+ fills.FindByIdNoOops(hcfBack),
+ /*isShaded=*/true));
+ }
+
+ void DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) override {
+ ssassert(false, "Not implemented");
+ }
+
+ void DrawPixmap(std::shared_ptr<const Pixmap> pm,
+ const Vector &o, const Vector &u, const Vector &v,
+ const Point2d &ta, const Point2d &tb, hFill hcf) override {
+ Fill fill = *fills.FindById(hcf);
+ fill.texture = pm;
+ hcf = GetFill(fill);
+
+ SIndexedMesh mesh = {};
+ mesh.AddPixmap(o, u, v, ta, tb);
+ drawCalls.emplace(PixmapDrawCall::Create(renderer, mesh, fills.FindByIdNoOops(hcf)));
+ mesh.Clear();
+ }
+
+ void InvalidatePixmap(std::shared_ptr<const Pixmap> pm) override {
+ ssassert(false, "Not implemented");
+ }
+
+ void Finalize() override {
+ for(const EdgeBuffer &eb : edgeBuffer) {
+ drawCalls.emplace(EdgeDrawCall::Create(renderer, eb.edges, strokes.FindById(eb.h)));
+ }
+ edgeBuffer.Clear();
+
+ for(const PointBuffer &pb : pointBuffer) {
+ drawCalls.emplace(PointDrawCall::Create(renderer, pb.points, strokes.FindById(pb.h)));
+ }
+ pointBuffer.Clear();
+ }
+
+ void Draw() override {
+ renderer->current = {};
+
+ for(std::shared_ptr<DrawCall> dc : drawCalls) {
+ dc->Draw(renderer);
+ }
+ }
+
+ void Clear() override {
+ for(std::shared_ptr<DrawCall> dc : drawCalls) {
+ dc->Remove(renderer);
+ }
+ drawCalls.clear();
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Factory functions.
+//-----------------------------------------------------------------------------
+
+std::shared_ptr<BatchCanvas> OpenGl3Renderer::CreateBatch() {
+ OpenGl3RendererBatch *batch = new OpenGl3RendererBatch();
+ batch->renderer = this;
+ return std::shared_ptr<BatchCanvas>(batch);
+}
+
+std::shared_ptr<ViewportCanvas> CreateRenderer() {
+ return std::shared_ptr<ViewportCanvas>(new OpenGl3Renderer());
+}
+
+}
const hRequest Request::HREQUEST_REFERENCE_YZ = { 2 };
const hRequest Request::HREQUEST_REFERENCE_ZX = { 3 };
-const EntReqTable::TableEntry EntReqTable::Table[] = {
-// request type entity type pts xtra? norml dist description
-{ Request::WORKPLANE, Entity::WORKPLANE, 1, false, true, false, "workplane" },
-{ Request::DATUM_POINT, 0, 1, false, false, false, "datum-point" },
-{ Request::LINE_SEGMENT, Entity::LINE_SEGMENT, 2, false, false, false, "line-segment" },
-{ Request::CUBIC, Entity::CUBIC, 4, true, false, false, "cubic-bezier" },
-{ Request::CUBIC_PERIODIC, Entity::CUBIC_PERIODIC, 3, true, false, false, "periodic-cubic" },
-{ Request::CIRCLE, Entity::CIRCLE, 1, false, true, true, "circle" },
-{ Request::ARC_OF_CIRCLE, Entity::ARC_OF_CIRCLE, 3, false, true, false, "arc-of-circle" },
-{ Request::TTF_TEXT, Entity::TTF_TEXT, 2, false, true, false, "ttf-text" },
-{ 0, 0, 0, false, false, false, 0 },
+struct EntReqMapping {
+ Request::Type reqType;
+ Entity::Type entType;
+ int points;
+ bool useExtraPoints;
+ bool hasNormal;
+ bool hasDistance;
+};
+static const EntReqMapping EntReqMap[] = {
+// request type entity type pts xtra? norml dist
+{ Request::Type::WORKPLANE, Entity::Type::WORKPLANE, 1, false, true, false },
+{ Request::Type::DATUM_POINT, (Entity::Type)0, 1, false, false, false },
+{ Request::Type::LINE_SEGMENT, Entity::Type::LINE_SEGMENT, 2, false, false, false },
+{ Request::Type::CUBIC, Entity::Type::CUBIC, 4, true, false, false },
+{ Request::Type::CUBIC_PERIODIC, Entity::Type::CUBIC_PERIODIC, 3, true, false, false },
+{ Request::Type::CIRCLE, Entity::Type::CIRCLE, 1, false, true, true },
+{ Request::Type::ARC_OF_CIRCLE, Entity::Type::ARC_OF_CIRCLE, 3, false, true, false },
+{ Request::Type::TTF_TEXT, Entity::Type::TTF_TEXT, 4, false, true, false },
+{ Request::Type::IMAGE, Entity::Type::IMAGE, 4, false, true, false },
};
-const char *EntReqTable::DescriptionForRequest(int req) {
- for(int i = 0; Table[i].reqType; i++) {
- if(req == Table[i].reqType) {
- return Table[i].description;
- }
- }
- return "???";
-}
-
-void EntReqTable::CopyEntityInfo(const TableEntry *te, int extraPoints,
- int *ent, int *req, int *pts, bool *hasNormal, bool *hasDistance)
+static void CopyEntityInfo(const EntReqMapping *te, int extraPoints,
+ Entity::Type *ent, Request::Type *req,
+ int *pts, bool *hasNormal, bool *hasDistance)
{
int points = te->points;
if(te->useExtraPoints) points += extraPoints;
if(hasDistance) *hasDistance = te->hasDistance;
}
-bool EntReqTable::GetRequestInfo(int req, int extraPoints,
- int *ent, int *pts, bool *hasNormal, bool *hasDistance)
+bool EntReqTable::GetRequestInfo(Request::Type req, int extraPoints,
+ Entity::Type *ent, int *pts, bool *hasNormal, bool *hasDistance)
{
- for(int i = 0; Table[i].reqType; i++) {
- const TableEntry *te = &(Table[i]);
- if(req == te->reqType) {
- CopyEntityInfo(te, extraPoints,
- ent, NULL, pts, hasNormal, hasDistance);
+ for(const EntReqMapping &te : EntReqMap) {
+ if(req == te.reqType) {
+ CopyEntityInfo(&te, extraPoints, ent, NULL, pts, hasNormal, hasDistance);
return true;
}
}
return false;
}
-bool EntReqTable::GetEntityInfo(int ent, int extraPoints,
- int *req, int *pts, bool *hasNormal, bool *hasDistance)
+bool EntReqTable::GetEntityInfo(Entity::Type ent, int extraPoints,
+ Request::Type *req, int *pts, bool *hasNormal, bool *hasDistance)
{
- for(int i = 0; Table[i].reqType; i++) {
- const TableEntry *te = &(Table[i]);
- if(ent == te->entType) {
- CopyEntityInfo(te, extraPoints,
- NULL, req, pts, hasNormal, hasDistance);
+ for(const EntReqMapping &te : EntReqMap) {
+ if(ent == te.entType) {
+ CopyEntityInfo(&te, extraPoints, NULL, req, pts, hasNormal, hasDistance);
return true;
}
}
return false;
}
-int EntReqTable::GetRequestForEntity(int ent) {
- int req = 0;
- GetEntityInfo(ent, 0, &req, NULL, NULL, NULL);
+Request::Type EntReqTable::GetRequestForEntity(Entity::Type ent) {
+ Request::Type req;
+ ssassert(GetEntityInfo(ent, 0, &req, NULL, NULL, NULL),
+ "No entity for request");
return req;
}
-
void Request::Generate(IdList<Entity,hEntity> *entity,
IdList<Param,hParam> *param)
{
int points = 0;
- int et = 0;
+ Entity::Type et = (Entity::Type)0;
bool hasNormal = false;
bool hasDistance = false;
int i;
+ // Request-specific generation.
+ switch(type) {
+ case Type::TTF_TEXT: {
+ double actualAspectRatio = SS.fonts.AspectRatio(font, str);
+ if(EXACT(actualAspectRatio != 0.0)) {
+ // We could load the font, so use the actual value.
+ aspectRatio = actualAspectRatio;
+ }
+ if(EXACT(aspectRatio == 0.0)) {
+ // We couldn't load the font and we don't have anything saved,
+ // so just use 1:1, which is valid for the missing font symbol anyhow.
+ aspectRatio = 1.0;
+ }
+ break;
+ }
+
+ case Type::IMAGE: {
+ auto image = SS.images.find(file);
+ if(image != SS.images.end()) {
+ std::shared_ptr<Pixmap> pixmap = (*image).second;
+ if(pixmap != NULL) {
+ aspectRatio = (double)pixmap->width / (double)pixmap->height;
+ }
+ }
+ if(EXACT(aspectRatio == 0.0)) {
+ aspectRatio = 1.0;
+ }
+ break;
+ }
+
+ default: // most requests don't do anything else
+ break;
+ }
+
Entity e = {};
- EntReqTable::GetRequestInfo(type, extraPoints,
- &et, &points, &hasNormal, &hasDistance);
+ EntReqTable::GetRequestInfo(type, extraPoints, &et, &points, &hasNormal, &hasDistance);
// Generate the entity that's specific to this request.
e.type = et;
e.construction = construction;
e.str = str;
e.font = font;
+ e.file = file;
+ e.aspectRatio = aspectRatio;
e.h = h.entity(0);
// And generate entities for the points
Entity p = {};
p.workplane = workplane;
// points start from entity 1, except for datum point case
- p.h = h.entity(i+(et ? 1 : 0));
+ p.h = h.entity(i+((et != (Entity::Type)0) ? 1 : 0));
p.group = group;
p.style = style;
-
- if(workplane.v == Entity::FREE_IN_3D.v) {
- p.type = Entity::POINT_IN_3D;
+ p.construction = e.construction;
+ if(workplane == Entity::FREE_IN_3D) {
+ p.type = Entity::Type::POINT_IN_3D;
// params for x y z
p.param[0] = AddParam(param, h.param(16 + 3*i + 0));
p.param[1] = AddParam(param, h.param(16 + 3*i + 1));
p.param[2] = AddParam(param, h.param(16 + 3*i + 2));
} else {
- p.type = Entity::POINT_IN_2D;
+ p.type = Entity::Type::POINT_IN_2D;
// params for u v
p.param[0] = AddParam(param, h.param(16 + 3*i + 0));
p.param[1] = AddParam(param, h.param(16 + 3*i + 1));
n.h = h.entity(32);
n.group = group;
n.style = style;
- if(workplane.v == Entity::FREE_IN_3D.v) {
- n.type = Entity::NORMAL_IN_3D;
+ n.construction = e.construction;
+ if(workplane == Entity::FREE_IN_3D) {
+ n.type = Entity::Type::NORMAL_IN_3D;
n.param[0] = AddParam(param, h.param(32+0));
n.param[1] = AddParam(param, h.param(32+1));
n.param[2] = AddParam(param, h.param(32+2));
n.param[3] = AddParam(param, h.param(32+3));
} else {
- n.type = Entity::NORMAL_IN_2D;
+ n.type = Entity::Type::NORMAL_IN_2D;
// and this is just a copy of the workplane quaternion,
// so no params required
}
- if(points < 1) oops();
+ ssassert(points >= 1, "Positioning a normal requires a point");
// The point determines where the normal gets displayed on-screen;
// it's entirely cosmetic.
n.point[0] = e.point[0];
d.h = h.entity(64);
d.group = group;
d.style = style;
- d.type = Entity::DISTANCE;
+ d.type = Entity::Type::DISTANCE;
d.param[0] = AddParam(param, h.param(64));
entity->Add(&d);
e.distance = d.h;
}
- if(et) entity->Add(&e);
+ if(et != (Entity::Type)0) entity->Add(&e);
}
-std::string Request::DescriptionString(void) {
- const char *s;
- if(h.v == Request::HREQUEST_REFERENCE_XY.v) {
+std::string Request::DescriptionString() const {
+ const char *s = "";
+ if(h == Request::HREQUEST_REFERENCE_XY) {
s = "#XY";
- } else if(h.v == Request::HREQUEST_REFERENCE_YZ.v) {
+ } else if(h == Request::HREQUEST_REFERENCE_YZ) {
s = "#YZ";
- } else if(h.v == Request::HREQUEST_REFERENCE_ZX.v) {
+ } else if(h == Request::HREQUEST_REFERENCE_ZX) {
s = "#ZX";
} else {
- s = EntReqTable::DescriptionForRequest(type);
+ switch(type) {
+ case Type::WORKPLANE: s = "workplane"; break;
+ case Type::DATUM_POINT: s = "datum-point"; break;
+ case Type::LINE_SEGMENT: s = "line-segment"; break;
+ case Type::CUBIC: s = "cubic-bezier"; break;
+ case Type::CUBIC_PERIODIC: s = "periodic-cubic"; break;
+ case Type::CIRCLE: s = "circle"; break;
+ case Type::ARC_OF_CIRCLE: s = "arc-of-circle"; break;
+ case Type::TTF_TEXT: s = "ttf-text"; break;
+ case Type::IMAGE: s = "image"; break;
+ }
}
-
+ ssassert(s != NULL, "Unexpected request type");
return ssprintf("r%03x-%s", h.v, s);
}
-int Request::IndexOfPoint(hEntity he) {
- if(type == DATUM_POINT) {
- return (he.v == h.entity(0).v) ? 0 : -1;
+int Request::IndexOfPoint(hEntity he) const {
+ if(type == Type::DATUM_POINT) {
+ return (he == h.entity(0)) ? 0 : -1;
}
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
- if(he.v == h.entity(i + 1).v) {
+ if(he == h.entity(i + 1)) {
return i;
}
}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Discovery and loading of our resources (icons, fonts, templates, etc).
+//
+// Copyright 2016 whitequark
+//-----------------------------------------------------------------------------
+#include <zlib.h>
+#include <png.h>
+#include <regex>
+#include "solvespace.h"
+
+namespace SolveSpace {
+
+//-----------------------------------------------------------------------------
+// Resource loading functions
+//-----------------------------------------------------------------------------
+
+std::string LoadString(const std::string &name) {
+ size_t size;
+ const void *data = Platform::LoadResource(name, &size);
+ std::string result(static_cast<const char *>(data), size);
+
+ // When editing resources under Windows, Windows newlines may sneak in.
+ // Any files with them won't be merged, but ignoring them during development
+ // helps external contributors.
+ result.erase(std::remove(result.begin(), result.end(), '\r'),
+ result.end());
+
+ return result;
+}
+
+std::string LoadStringFromGzip(const std::string &name) {
+ size_t deflatedSize;
+ const void *data = Platform::LoadResource(name, &deflatedSize);
+
+ z_stream stream;
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+ const int windowBits = /*maximum window*/ 15 + /*decode gzip header*/16;
+ ssassert(inflateInit2(&stream, windowBits) == Z_OK, "Cannot start inflation");
+
+ // Extract length mod 2**32 from the gzip trailer.
+ std::string result;
+ ssassert(deflatedSize >= 4, "Resource too small to have gzip trailer");
+
+ // *(uint32_t *) may perform an unaligned access, so do a memcpy.
+ uint32_t inflatedSize;
+ memcpy(&inflatedSize, (uint8_t *)((uintptr_t)data + deflatedSize - 4), sizeof(uint32_t));
+ result.resize(inflatedSize);
+
+ stream.next_in = (Bytef *)data;
+ stream.avail_in = (uInt)deflatedSize;
+ stream.next_out = (Bytef *)&result[0];
+ stream.avail_out = (uInt)result.length();
+ ssassert(inflate(&stream, Z_NO_FLUSH) == Z_STREAM_END, "Cannot inflate resource");
+ ssassert(stream.avail_out == 0, "Inflated resource larger than what trailer indicates");
+
+ inflateEnd(&stream);
+
+ return result;
+}
+
+std::shared_ptr<Pixmap> LoadPng(const std::string &name) {
+ size_t size;
+ const void *data = Platform::LoadResource(name, &size);
+
+ std::shared_ptr<Pixmap> pixmap = Pixmap::FromPng(static_cast<const uint8_t *>(data), size);
+ ssassert(pixmap != nullptr, "Cannot load pixmap");
+
+ return pixmap;
+}
+
+//-----------------------------------------------------------------------------
+// Pixmap manipulation
+//-----------------------------------------------------------------------------
+
+size_t Pixmap::GetBytesPerPixel() const {
+ switch(format) {
+ case Format::RGBA: return 4;
+ case Format::BGRA: return 4;
+ case Format::RGB: return 3;
+ case Format::BGR: return 3;
+ case Format::A: return 1;
+ }
+ ssassert(false, "Unexpected pixmap format");
+}
+
+RgbaColor Pixmap::GetPixel(size_t x, size_t y) const {
+ const uint8_t *pixel = &data[y * stride + x * GetBytesPerPixel()];
+
+ switch(format) {
+ case Format::RGBA:
+ return RgbaColor::From(pixel[0], pixel[1], pixel[2], pixel[3]);
+
+ case Format::RGB:
+ return RgbaColor::From(pixel[0], pixel[1], pixel[2], 255);
+
+ case Format::BGRA:
+ return RgbaColor::From(pixel[2], pixel[1], pixel[0], pixel[3]);
+
+ case Format::BGR:
+ return RgbaColor::From(pixel[2], pixel[1], pixel[0], 255);
+
+ case Format::A:
+ return RgbaColor::From( 255, 255, 255, pixel[0]);
+ }
+ ssassert(false, "Unexpected resource format");
+}
+
+void Pixmap::SetPixel(size_t x, size_t y, RgbaColor color) {
+ uint8_t *pixel = &data[y * stride + x * GetBytesPerPixel()];
+
+ switch(format) {
+ case Format::RGBA:
+ pixel[0] = color.red;
+ pixel[1] = color.green;
+ pixel[2] = color.blue;
+ pixel[3] = color.alpha;
+ break;
+
+ case Format::RGB:
+ pixel[0] = color.red;
+ pixel[1] = color.green;
+ pixel[2] = color.blue;
+ break;
+
+ case Format::BGRA:
+ pixel[0] = color.blue;
+ pixel[1] = color.green;
+ pixel[2] = color.red;
+ pixel[3] = color.alpha;
+ break;
+
+ case Format::BGR:
+ pixel[0] = color.blue;
+ pixel[1] = color.green;
+ pixel[2] = color.red;
+ break;
+
+ case Format::A:
+ pixel[0] = color.alpha;
+ break;
+ }
+}
+
+void Pixmap::ConvertTo(Format newFormat) {
+ switch(format) {
+ case Format::RGBA:
+ ssassert(newFormat == Format::BGRA, "Unexpected target format");
+ break;
+
+ case Format::BGRA:
+ ssassert(newFormat == Format::RGBA, "Unexpected target format");
+ break;
+
+ case Format::RGB:
+ ssassert(newFormat == Format::BGR, "Unexpected target format");
+ break;
+
+ case Format::BGR:
+ ssassert(newFormat == Format::RGB, "Unexpected target format");
+ break;
+
+ case Format::A:
+ ssassert(false, "Unexpected target format");
+ }
+
+ size_t bpp = GetBytesPerPixel();
+ for(size_t j = 0; j != height; j++) {
+ uint8_t *row = &data[j * stride];
+ for(size_t i = 0; i != width * bpp; i += bpp) {
+ // This handles both RGB<>BGR and RGBA<>BGRA.
+ std::swap(row[i], row[i + 2]);
+ }
+ }
+
+ format = newFormat;
+}
+
+static std::shared_ptr<Pixmap> ReadPngIntoPixmap(png_struct *png_ptr, png_info *info_ptr,
+ bool flip) {
+ png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_GRAY_TO_RGB, NULL);
+
+ std::shared_ptr<Pixmap> pixmap = std::make_shared<Pixmap>();
+ pixmap->width = png_get_image_width(png_ptr, info_ptr);
+ pixmap->height = png_get_image_height(png_ptr, info_ptr);
+ if((png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA) != 0) {
+ pixmap->format = Pixmap::Format::RGBA;
+ } else {
+ pixmap->format = Pixmap::Format::RGB;
+ }
+
+ size_t stride = pixmap->width * pixmap->GetBytesPerPixel();
+ if(stride % 4 != 0) stride += 4 - stride % 4;
+ pixmap->stride = stride;
+
+ pixmap->data = std::vector<uint8_t>(pixmap->stride * pixmap->height);
+ uint8_t **rows = png_get_rows(png_ptr, info_ptr);
+ for(size_t y = 0; y < pixmap->height; y++) {
+ uint8_t *srcRow = flip ? rows[pixmap->height - y - 1] : rows[y];
+ memcpy(&pixmap->data[pixmap->stride * y], srcRow,
+ pixmap->width * pixmap->GetBytesPerPixel());
+ }
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return pixmap;
+}
+
+std::shared_ptr<Pixmap> Pixmap::FromPng(const uint8_t *data, size_t size, bool flip) {
+ struct Slice { const uint8_t *data; size_t size; };
+ Slice dataSlice = { data, size };
+ png_struct *png_ptr = NULL;
+ png_info *info_ptr = NULL;
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if(!png_ptr) goto exit;
+ info_ptr = png_create_info_struct(png_ptr);
+ if(!info_ptr) goto exit;
+
+ if(setjmp(png_jmpbuf(png_ptr))) goto exit;
+
+ png_set_read_fn(png_ptr, &dataSlice,
+ [](png_struct *png_ptr, uint8_t *data, size_t size) {
+ Slice *dataSlice = (Slice *)png_get_io_ptr(png_ptr);
+ if(size <= dataSlice->size) {
+ memcpy(data, dataSlice->data, size);
+ dataSlice->data += size;
+ dataSlice->size -= size;
+ } else {
+ png_error(png_ptr, "EOF");
+ }
+ });
+
+ return ReadPngIntoPixmap(png_ptr, info_ptr, flip);
+
+exit:
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return nullptr;
+}
+
+std::shared_ptr<Pixmap> Pixmap::ReadPng(FILE *f, bool flip) {
+ png_struct *png_ptr = NULL;
+ png_info *info_ptr = NULL;
+
+ uint8_t header[8];
+ if(fread(header, 1, sizeof(header), f) != sizeof(header)) goto exit;
+ if(png_sig_cmp(header, 0, sizeof(header))) goto exit;
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if(!png_ptr) goto exit;
+ info_ptr = png_create_info_struct(png_ptr);
+ if(!info_ptr) goto exit;
+
+ if(setjmp(png_jmpbuf(png_ptr))) goto exit;
+
+ png_init_io(png_ptr, f);
+ png_set_sig_bytes(png_ptr, sizeof(header));
+
+ return ReadPngIntoPixmap(png_ptr, info_ptr, flip);
+
+exit:
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return nullptr;
+}
+
+std::shared_ptr<Pixmap> Pixmap::ReadPng(const Platform::Path &filename, bool flip) {
+ FILE *f = OpenFile(filename, "rb");
+ if(!f) return NULL;
+ std::shared_ptr<Pixmap> pixmap = ReadPng(f, flip);
+ fclose(f);
+ return pixmap;
+}
+
+bool Pixmap::WritePng(FILE *f, bool flip) {
+ int colorType = 0;
+ bool bgr = false;
+ switch(format) {
+ case Format::RGBA: colorType = PNG_COLOR_TYPE_RGBA; bgr = false; break;
+ case Format::BGRA: colorType = PNG_COLOR_TYPE_RGBA; bgr = true; break;
+ case Format::RGB: colorType = PNG_COLOR_TYPE_RGB; bgr = false; break;
+ case Format::BGR: colorType = PNG_COLOR_TYPE_RGB; bgr = true; break;
+ case Format::A: colorType = PNG_COLOR_TYPE_GRAY; bgr = false; break;
+ }
+
+ std::vector<uint8_t *> rows;
+ for(size_t y = 0; y < height; y++) {
+ if(flip) {
+ rows.push_back(&data[stride * (height - y - 1)]);
+ } else {
+ rows.push_back(&data[stride * y]);
+ }
+ }
+
+ png_struct *png_ptr = NULL;
+ png_info *info_ptr = NULL;
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if(!png_ptr) goto exit;
+ info_ptr = png_create_info_struct(png_ptr);
+ if(!info_ptr) goto exit;
+
+ if(setjmp(png_jmpbuf(png_ptr))) goto exit;
+
+ png_init_io(png_ptr, f);
+ png_set_IHDR(png_ptr, info_ptr,
+ (png_uint_32)width, (png_uint_32)height, 8, colorType,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ if(bgr) png_set_bgr(png_ptr);
+ png_write_info(png_ptr, info_ptr);
+ png_write_image(png_ptr, &rows[0]);
+ png_write_end(png_ptr, info_ptr);
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return true;
+
+exit:
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return false;
+}
+
+bool Pixmap::WritePng(const Platform::Path &filename, bool flip) {
+ FILE *f = OpenFile(filename, "wb");
+ if(!f) return false;
+ bool success = WritePng(f, flip);
+ fclose(f);
+ return success;
+}
+
+bool Pixmap::Equals(const Pixmap &other) const {
+ if(format != other.format || width != other.width || height != other.height) {
+ return false;
+ }
+
+ size_t rowLength = width * GetBytesPerPixel();
+ for(size_t y = 0; y < height; y++) {
+ if(memcmp(&data[y * stride], &other.data[y * other.stride], rowLength)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+std::shared_ptr<Pixmap> Pixmap::Create(Format format, size_t width, size_t height) {
+ std::shared_ptr<Pixmap> pixmap = std::make_shared<Pixmap>();
+ pixmap->format = format;
+ pixmap->width = width;
+ pixmap->height = height;
+ // Align to fulfill OpenGL texture requirements.
+ size_t stride = pixmap->width * pixmap->GetBytesPerPixel();
+ if(stride % 4 != 0) stride += 4 - stride % 4;
+ pixmap->stride = stride;
+ pixmap->data = std::vector<uint8_t>(pixmap->stride * pixmap->height);
+ return pixmap;
+}
+
+std::shared_ptr<Pixmap> Pixmap::Copy() {
+ std::shared_ptr<Pixmap> pixmap = std::make_shared<Pixmap>();
+ pixmap->format = format;
+ pixmap->width = width;
+ pixmap->height = height;
+ pixmap->stride = stride;
+ pixmap->data = data;
+ return pixmap;
+}
+
+//-----------------------------------------------------------------------------
+// ASCII sequence parsing
+//-----------------------------------------------------------------------------
+
+class ASCIIReader {
+public:
+ std::string::const_iterator pos, end;
+
+ static ASCIIReader From(const std::string &str) {
+ return ASCIIReader({ str.cbegin(), str.cend() });
+ }
+
+ bool AtEnd() const {
+ return pos == end;
+ }
+
+ bool SkipSpace() {
+ bool skipped = false;
+ while(!AtEnd()) {
+ char c = *pos;
+ if(!(c == ' ' || c == '\t' || c == '\n')) break;
+ skipped = true;
+ pos++;
+ }
+ return skipped;
+ }
+
+ char PeekChar() {
+ ssassert(!AtEnd(), "Unexpected EOF");
+ return *pos;
+ }
+
+ char ReadChar() {
+ ssassert(!AtEnd(), "Unexpected EOF");
+ return *pos++;
+ }
+
+ bool TryChar(char c) {
+ if(AtEnd()) {
+ return false;
+ } else if(*pos == c) {
+ pos++;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void ExpectChar(char c) {
+ if(!TryChar(c)) {
+ dbp("Expecting character '%c'", c);
+ ssassert(false, "Unexpected character");
+ }
+ }
+
+ bool TryString(const std::string &s) {
+ if((size_t)(end - pos) >= s.size() && std::string(pos, pos + s.size()) == s) {
+ pos += s.size();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void ExpectString(const std::string &s) {
+ if(!TryString(s)) {
+ dbp("Expecting string '%s'", s.c_str());
+ ssassert(false, "Unexpected string");
+ }
+ }
+
+ size_t CountUntilEol() const {
+ return std::find(pos, end, '\n') - pos;
+ }
+
+ void SkipUntilEol() {
+ pos = std::find(pos, end, '\n');
+ }
+
+ std::string ReadUntilEol() {
+ auto eol = std::find(pos, end, '\n');
+ std::string result(pos, eol);
+ if(eol != end) {
+ pos = eol + 1;
+ } else {
+ pos = end;
+ }
+ return result;
+ }
+
+ uint8_t Read4HexBits() {
+ char c = ReadChar();
+ if(c >= '0' && c <= '9') {
+ return c - '0';
+ } else if(c >= 'a' && c <= 'f') {
+ return 10 + (c - 'a');
+ } else if(c >= 'A' && c <= 'F') {
+ return 10 + (c - 'A');
+ } else ssassert(false, "Unexpected hex digit");
+ }
+
+ uint8_t Read8HexBits() {
+ uint8_t h = Read4HexBits(),
+ l = Read4HexBits();
+ return (h << 4) + l;
+ }
+
+ uint16_t Read16HexBits() {
+ uint16_t h = Read8HexBits(),
+ l = Read8HexBits();
+ return (h << 8) + l;
+ }
+
+ long ReadIntegerDecimal(int base = 10) {
+ char *endptr;
+ long l = strtol(&*pos, &endptr, base);
+ ssassert(&*pos != endptr, "Cannot read an integer number");
+ pos += endptr - &*pos;
+ return l;
+ }
+
+ double ReadFloatDecimal() {
+ char *endptr;
+ double d = strtod(&*pos, &endptr);
+ ssassert(&*pos != endptr, "Cannot read a floating-point number");
+ pos += endptr - &*pos;
+ return d;
+ }
+
+ bool TryRegex(const std::regex &re, std::smatch *m) {
+ if(std::regex_search(pos, end, *m, re, std::regex_constants::match_continuous)) {
+ pos += m->length();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void ExpectRegex(const std::regex &re, std::smatch *m) {
+ ssassert(TryRegex(re, m), "Unmatched regex");
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Bitmap font manipulation
+//-----------------------------------------------------------------------------
+
+static uint8_t *BitmapFontTextureRow(std::shared_ptr<Pixmap> texture,
+ uint16_t position, size_t y) {
+ // position = 0;
+ size_t col = position % (texture->width / 16),
+ row = position / (texture->width / 16);
+ return &texture->data[texture->stride * (16 * row + y) + 16 * col];
+}
+
+BitmapFont BitmapFont::From(std::string &&unifontData) {
+ BitmapFont font = {};
+ font.unifontData = std::move(unifontData);
+ font.texture = Pixmap::Create(Pixmap::Format::A, 1024, 1024);
+
+ return font;
+}
+
+void BitmapFont::AddGlyph(char32_t codepoint, std::shared_ptr<const Pixmap> pixmap) {
+ ssassert((pixmap->width == 8 || pixmap->width == 16) && pixmap->height == 16,
+ "Unexpected pixmap dimensions");
+ ssassert(pixmap->format == Pixmap::Format::RGB,
+ "Unexpected pixmap format");
+ ssassert(glyphs.find(codepoint) == glyphs.end(),
+ "Glyph with this codepoint already exists");
+ ssassert(nextPosition != 0xffff,
+ "Too many glyphs for current texture size");
+
+ BitmapFont::Glyph glyph = {};
+ glyph.advanceCells = (uint8_t)(pixmap->width / 8);
+ glyph.position = nextPosition++;
+ glyphs.emplace(codepoint, glyph);
+
+ for(size_t y = 0; y < pixmap->height; y++) {
+ uint8_t *row = BitmapFontTextureRow(texture, glyph.position, y);
+ for(size_t x = 0; x < pixmap->width; x++) {
+ if((pixmap->GetPixel(x, y).ToPackedInt() & 0xffffff) != 0) {
+ row[x] = 255;
+ }
+ }
+ }
+}
+
+const BitmapFont::Glyph &BitmapFont::GetGlyph(char32_t codepoint) {
+ auto it = glyphs.find(codepoint);
+ if(it != glyphs.end()) {
+ return (*it).second;
+ }
+
+ ssassert(nextPosition != 0xffff,
+ "Too many glyphs for current texture size");
+
+ // Find the hex representation in the (sorted) Unifont file.
+ auto first = unifontData.cbegin(),
+ last = unifontData.cend();
+ while(first <= last) {
+ auto mid = first + (last - first) / 2;
+ while(mid != unifontData.cbegin()) {
+ if(*mid == '\n') {
+ mid++;
+ break;
+ }
+ mid--;
+ }
+
+ ASCIIReader reader = { mid, unifontData.cend() };
+ if(reader.AtEnd()) break;
+
+ // Read the codepoint.
+ char32_t foundCodepoint = reader.Read16HexBits();
+ reader.ExpectChar(':');
+
+ if(foundCodepoint > codepoint) {
+ last = mid - 1;
+ continue; // and first stays the same
+ }
+ if(foundCodepoint < codepoint) {
+ first = mid + 1;
+ while(first != unifontData.cend()) {
+ if(*first == '\n') break;
+ first++;
+ }
+ continue; // and last stays the same
+ }
+
+ // Found the codepoint.
+ Glyph glyph = {};
+ glyph.position = nextPosition++;
+
+ // Read glyph bits.
+ unsigned short glyphBits[16];
+ size_t glyphLength = reader.CountUntilEol();
+ if(glyphLength == 4 * 16) {
+ glyph.advanceCells = 2;
+ for(size_t i = 0; i < 16; i++) {
+ glyphBits[i] = reader.Read16HexBits();
+ }
+ } else if(glyphLength == 2 * 16) {
+ glyph.advanceCells = 1;
+ for(size_t i = 0; i < 16; i++) {
+ glyphBits[i] = (uint16_t)reader.Read8HexBits() << 8;
+ }
+ } else ssassert(false, "Unexpected glyph bitmap length");
+
+ // Fill in the texture (one texture byte per glyph bit).
+ for(size_t y = 0; y < 16; y++) {
+ uint8_t *row = BitmapFontTextureRow(texture, glyph.position, y);
+ for(size_t x = 0; x < 16; x++) {
+ if(glyphBits[y] & (1 << (15 - x))) {
+ row[x] = 255;
+ }
+ }
+ }
+
+ it = glyphs.emplace(codepoint, glyph).first;
+
+ textureUpdated = true;
+ return (*it).second;
+ }
+
+ // Glyph doesn't exist; return replacement glyph instead.
+ ssassert(codepoint != 0xfffd, "Cannot parse replacement glyph");
+ return GetGlyph(0xfffd);
+}
+
+void BitmapFont::LocateGlyph(char32_t codepoint,
+ double *s0, double *t0, double *s1, double *t1,
+ size_t *w, size_t *h) {
+ const Glyph &glyph = GetGlyph(codepoint);
+ *w = glyph.advanceCells * 8;
+ *h = 16;
+ *s0 = (16.0 * (glyph.position % (texture->width / 16))) / texture->width;
+ *s1 = *s0 + (double)(*w) / texture->width;
+ *t0 = (16.0 * (glyph.position / (texture->width / 16))) / texture->height;
+ *t1 = *t0 + (double)(*h) / texture->height;
+}
+
+size_t BitmapFont::GetWidth(char32_t codepoint) {
+ if(codepoint >= 0xe000 && codepoint <= 0xefff) {
+ // These are special-cased because checkboxes predate support for 2 cell wide
+ // characters; and so all Printf() calls pad them with spaces.
+ return 1;
+ }
+
+ return GetGlyph(codepoint).advanceCells;
+}
+
+size_t BitmapFont::GetWidth(const std::string &str) {
+ size_t width = 0;
+ for(char32_t codepoint : ReadUTF8(str)) {
+ width += GetWidth(codepoint);
+ }
+ return width;
+}
+
+BitmapFont BitmapFont::Create() {
+ BitmapFont Font = BitmapFont::From(LoadStringFromGzip("fonts/unifont.hex.gz"));
+ // Unifont doesn't have a glyph for U+0020.
+ Font.AddGlyph(0x0020, Pixmap::Create(Pixmap::Format::RGB, 8, 16));
+ Font.AddGlyph(0xE000, LoadPng("fonts/private/0-check-false.png"));
+ Font.AddGlyph(0xE001, LoadPng("fonts/private/1-check-true.png"));
+ Font.AddGlyph(0xE002, LoadPng("fonts/private/2-radio-false.png"));
+ Font.AddGlyph(0xE003, LoadPng("fonts/private/3-radio-true.png"));
+ Font.AddGlyph(0xE004, LoadPng("fonts/private/4-stipple-dot.png"));
+ Font.AddGlyph(0xE005, LoadPng("fonts/private/5-stipple-dash-long.png"));
+ Font.AddGlyph(0xE006, LoadPng("fonts/private/6-stipple-dash.png"));
+ Font.AddGlyph(0xE007, LoadPng("fonts/private/7-stipple-zigzag.png"));
+ return Font;
+}
+
+//-----------------------------------------------------------------------------
+// Vector font manipulation
+//-----------------------------------------------------------------------------
+
+const static int ARC_POINTS = 8;
+static void MakePwlArc(VectorFont::Contour *contour, bool isReversed,
+ const Point2d &cp, double radius, double a1, double a2) {
+ if(radius < LENGTH_EPS) return;
+
+ double aSign = 1.0;
+ if(isReversed) {
+ if(a1 <= a2 + LENGTH_EPS) a1 += 2.0 * M_PI;
+ aSign = -1.0;
+ } else {
+ if(a2 <= a1 + LENGTH_EPS) a2 += 2.0 * M_PI;
+ }
+
+ double aStep = aSign * fabs(a2 - a1) / (double)ARC_POINTS;
+ for(int i = 0; i <= ARC_POINTS; i++) {
+ contour->points.emplace_back(cp.Plus(Point2d::FromPolar(radius, a1 + aStep * i)));
+ }
+}
+
+static void MakePwlBulge(VectorFont::Contour *contour, const Point2d &v, double bulge) {
+ bool reversed = bulge < 0.0;
+ double alpha = atan(bulge) * 4.0;
+ const Point2d &point = contour->points.back();
+
+ Point2d middle = point.Plus(v).ScaledBy(0.5);
+ double dist = point.DistanceTo(v) / 2.0;
+ double angle = point.AngleTo(v);
+
+ // alpha can't be 0.0 at this point
+ double radius = fabs(dist / sin(alpha / 2.0));
+ double wu = fabs(radius*radius - dist*dist);
+ double h = sqrt(wu);
+
+ if(bulge > 0.0) {
+ angle += M_PI_2;
+ } else {
+ angle -= M_PI_2;
+ }
+
+ if(fabs(alpha) > M_PI) {
+ h = -h;
+ }
+
+ Point2d center = Point2d::FromPolar(h, angle).Plus(middle);
+ double a1 = center.AngleTo(point);
+ double a2 = center.AngleTo(v);
+ MakePwlArc(contour, reversed, center, radius, a1, a2);
+}
+
+static void GetGlyphBBox(const VectorFont::Glyph &glyph,
+ double *rminx, double *rmaxx, double *rminy, double *rmaxy) {
+ double minx = 0.0, maxx = 0.0, miny = 0.0, maxy = 0.0;
+ if(!glyph.contours.empty()) {
+ const Point2d &start = glyph.contours[0].points[0];
+ minx = maxx = start.x;
+ miny = maxy = start.y;
+ for(const VectorFont::Contour &c : glyph.contours) {
+ for(const Point2d &p : c.points) {
+ maxx = std::max(maxx, p.x);
+ minx = std::min(minx, p.x);
+ maxy = std::max(maxy, p.y);
+ miny = std::min(miny, p.y);
+ }
+ }
+ }
+
+ if(rminx) *rminx = minx;
+ if(rmaxx) *rmaxx = maxx;
+ if(rminy) *rminy = miny;
+ if(rmaxy) *rmaxy = maxy;
+}
+
+VectorFont VectorFont::From(std::string &&lffData) {
+ VectorFont font = {};
+ font.lffData = std::move(lffData);
+
+ ASCIIReader reader = ASCIIReader::From(font.lffData);
+ std::smatch m;
+ while(reader.TryRegex(std::regex("#\\s*(\\w+)\\s*:\\s*(.+?)\n"), &m)) {
+ std::string name = m.str(1),
+ value = m.str(2);
+ std::transform(name.begin(), name.end(), name.begin(), ::tolower);
+ if(name == "letterspacing") {
+ font.rightSideBearing = std::stod(value);
+ } else if(name == "wordspacing") {
+ Glyph space = {};
+ space.advanceWidth = std::stod(value);
+ font.glyphs.emplace(' ', std::move(space));
+ }
+ }
+
+ GetGlyphBBox(font.GetGlyph('A'), nullptr, nullptr, nullptr, &font.capHeight);
+ GetGlyphBBox(font.GetGlyph('h'), nullptr, nullptr, nullptr, &font.ascender);
+ GetGlyphBBox(font.GetGlyph('p'), nullptr, nullptr, &font.descender, nullptr);
+
+ ssassert(!font.IsEmpty(), "Expected to load a font");
+ return font;
+}
+
+const VectorFont::Glyph &VectorFont::GetGlyph(char32_t codepoint) {
+ auto it = glyphs.find(codepoint);
+ if(it != glyphs.end()) {
+ return (*it).second;
+ }
+
+ auto firstGlyph = std::find(lffData.cbegin(), lffData.cend(), '[');
+ ssassert(firstGlyph != lffData.cend(), "Vector font contains no glyphs");
+
+ // Find the serialized representation in the (sorted) lff file.
+ auto first = firstGlyph,
+ last = lffData.cend();
+ while(first <= last) {
+ auto mid = first + (last - first) / 2;
+ while(mid > first) {
+ if(*mid == '[' && *(mid - 1) == '\n') break;
+ mid--;
+ }
+
+ ASCIIReader reader = { mid, lffData.cend() };
+ if(reader.AtEnd()) break;
+
+ // Read the codepoint.
+ reader.ExpectChar('[');
+ char32_t foundCodepoint = reader.Read16HexBits();
+ reader.ExpectChar(']');
+ reader.SkipUntilEol();
+
+ if(foundCodepoint > codepoint) {
+ last = mid - 1;
+ continue; // and first stays the same
+ }
+ if(foundCodepoint < codepoint) {
+ first = mid + 1;
+ while(first != lffData.cend()) {
+ if(*first == '[' && *(first - 1) == '\n') break;
+ first++;
+ }
+ continue; // and last stays the same
+ }
+
+ // Found the codepoint.
+ VectorFont::Glyph glyph = {};
+
+ // Read glyph contours.
+ while(!reader.AtEnd()) {
+ if(reader.TryChar('\n')) {
+ // Skip.
+ } else if(reader.TryChar('[')) {
+ // End of glyph.
+ if(glyph.contours.back().points.empty()) {
+ // Remove an useless empty contour, if any.
+ glyph.contours.pop_back();
+ }
+ break;
+ } else if(reader.TryChar('C')) {
+ // Another character is referenced in this one.
+ char32_t baseCodepoint = reader.Read16HexBits();
+ const VectorFont::Glyph &baseGlyph = GetGlyph(baseCodepoint);
+ std::copy(baseGlyph.contours.begin(), baseGlyph.contours.end(),
+ std::back_inserter(glyph.contours));
+ } else {
+ Contour contour;
+ do {
+ Point2d p;
+ p.x = reader.ReadFloatDecimal();
+ reader.ExpectChar(',');
+ p.y = reader.ReadFloatDecimal();
+
+ if(reader.TryChar(',')) {
+ // Point with a bulge.
+ reader.ExpectChar('A');
+ double bulge = reader.ReadFloatDecimal();
+ MakePwlBulge(&contour, p, bulge);
+ } else {
+ // Just a point.
+ contour.points.emplace_back(std::move(p));
+ }
+ } while(reader.TryChar(';'));
+ reader.ExpectChar('\n');
+ glyph.contours.emplace_back(std::move(contour));
+ }
+ }
+
+ // Calculate metrics.
+ GetGlyphBBox(glyph, &glyph.leftSideBearing, &glyph.boundingWidth, nullptr, nullptr);
+ glyph.advanceWidth = glyph.leftSideBearing + glyph.boundingWidth + rightSideBearing;
+
+ it = glyphs.emplace(codepoint, std::move(glyph)).first;
+ return (*it).second;
+ }
+
+ // Glyph doesn't exist; return replacement glyph instead.
+ ssassert(codepoint != 0xfffd, "Cannot parse replacement glyph");
+ return GetGlyph(0xfffd);
+}
+
+VectorFont *VectorFont::Builtin() {
+ static VectorFont Font;
+ if(Font.IsEmpty()) {
+ Font = VectorFont::From(LoadStringFromGzip("fonts/unicode.lff.gz"));
+ }
+ return &Font;
+}
+
+double VectorFont::GetCapHeight(double forCapHeight) const {
+ ssassert(!IsEmpty(), "Expected a loaded font");
+
+ return forCapHeight;
+}
+
+double VectorFont::GetHeight(double forCapHeight) const {
+ ssassert(!IsEmpty(), "Expected a loaded font");
+
+ return (ascender - descender) * (forCapHeight / capHeight);
+}
+
+double VectorFont::GetWidth(double forCapHeight, const std::string &str) {
+ ssassert(!IsEmpty(), "Expected a loaded font");
+
+ double width = 0;
+ for(char32_t codepoint : ReadUTF8(str)) {
+ width += GetGlyph(codepoint).advanceWidth;
+ }
+ width -= rightSideBearing;
+ return width * (forCapHeight / capHeight);
+}
+
+Vector VectorFont::GetExtents(double forCapHeight, const std::string &str) {
+ Vector ex = {};
+ ex.x = GetWidth(forCapHeight, str);
+ ex.y = GetHeight(forCapHeight);
+ return ex;
+}
+
+void VectorFont::Trace(double forCapHeight, Vector o, Vector u, Vector v, const std::string &str,
+ const std::function<void(Vector, Vector)> &traceEdge) {
+ ssassert(!IsEmpty(), "Expected a loaded font");
+
+ double scale = (forCapHeight / capHeight);
+ u = u.ScaledBy(scale);
+ v = v.ScaledBy(scale);
+
+ for(char32_t codepoint : ReadUTF8(str)) {
+ const Glyph &glyph = GetGlyph(codepoint);
+
+ for(const VectorFont::Contour &contour : glyph.contours) {
+ Vector prevp;
+ bool penUp = true;
+ for(const Point2d &pt : contour.points) {
+ Vector p = o.Plus(u.ScaledBy(pt.x))
+ .Plus(v.ScaledBy(pt.y));
+ if(!penUp) traceEdge(prevp, p);
+ prevp = p;
+ penUp = false;
+ }
+ }
+
+ o = o.Plus(u.ScaledBy(glyph.advanceWidth));
+ }
+}
+
+void VectorFont::Trace(double forCapHeight, Vector o, Vector u, Vector v, const std::string &str,
+ const std::function<void(Vector, Vector)> &traceEdge, const Camera &camera) {
+ ssassert(!IsEmpty(), "Expected a loaded font");
+
+ // Perform grid-fitting only when the text is parallel to the view plane.
+ if(camera.gridFit && !(u.WithMagnitude(1).Equals(camera.projRight) &&
+ v.WithMagnitude(1).Equals(camera.projUp))) {
+ return Trace(forCapHeight, o, u, v, str, traceEdge);
+ }
+
+ double scale = forCapHeight / capHeight;
+ u = u.ScaledBy(scale);
+ v = v.ScaledBy(scale);
+
+ for(char32_t codepoint : ReadUTF8(str)) {
+ const Glyph &glyph = GetGlyph(codepoint);
+ double actualWidth = std::max(1.0, glyph.boundingWidth);
+
+ // Align (o+lsb), (o+lsb+u) and (o+lsb+v) to pixel grid.
+ Vector ao = o.Plus(u.ScaledBy(glyph.leftSideBearing));
+ Vector au = ao.Plus(u.ScaledBy(actualWidth));
+ Vector av = ao.Plus(v.ScaledBy(capHeight));
+
+ ao = camera.AlignToPixelGrid(ao);
+ au = camera.AlignToPixelGrid(au);
+ av = camera.AlignToPixelGrid(av);
+
+ au = au.Minus(ao).ScaledBy(1.0 / actualWidth);
+ av = av.Minus(ao).ScaledBy(1.0 / capHeight);
+
+ for(const VectorFont::Contour &contour : glyph.contours) {
+ Vector prevp;
+ bool penUp = true;
+ for(const Point2d &pt : contour.points) {
+ Vector p = ao.Plus(au.ScaledBy(pt.x - glyph.leftSideBearing))
+ .Plus(av.ScaledBy(pt.y));
+ if(!penUp) traceEdge(prevp, p);
+ prevp = p;
+ penUp = false;
+ }
+ }
+
+ o = o.Plus(u.ScaledBy(glyph.advanceWidth));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Gettext plural expression evaluation
+//-----------------------------------------------------------------------------
+
+class PluralExpr {
+public:
+ class Token {
+ public:
+ enum class Type {
+ END,
+ VALUE,
+ BINARY_OP,
+ QUERY,
+ COLON,
+ PAREN_LEFT,
+ PAREN_RIGHT,
+ };
+
+ // Only valid for type == BINARY_OP.
+ enum class Op {
+ NONE,
+ // comparison
+ EQ, // ==
+ NEQ, // !=
+ LT, // <
+ GT, // >
+ LE, // <=
+ GE, // >=
+ // logical
+ AND, // &&
+ OR, // ||
+ // arithmetic
+ MOD, // %
+ };
+
+ Type type;
+ Op op;
+ unsigned value;
+
+ int Precedence();
+ };
+
+ ASCIIReader reader;
+ std::vector<Token> stack;
+ unsigned value;
+
+ Token Lex();
+
+ Token PopToken();
+ void Reduce();
+ void Eval();
+
+ static unsigned Eval(const std::string &s, unsigned n);
+};
+
+int PluralExpr::Token::Precedence() {
+ switch(type) {
+ case Type::BINARY_OP:
+ switch(op) {
+ case Op::MOD:
+ return 7;
+
+ case Op::LT:
+ case Op::GT:
+ case Op::LE:
+ case Op::GE:
+ return 6;
+
+ case Op::EQ:
+ case Op::NEQ:
+ return 5;
+
+ case Op::AND:
+ return 4;
+
+ case Op::OR:
+ return 3;
+
+ case Op::NONE:
+ ;
+ }
+ ssassert(false, "Unexpected operator");
+
+ case Type::QUERY:
+ case Type::COLON:
+ return 1;
+
+ case Type::VALUE:
+ return 0;
+
+ default:
+ ssassert(false, "Unexpected token op");
+ }
+}
+
+PluralExpr::Token PluralExpr::Lex() {
+ Token t = {};
+
+ reader.SkipSpace();
+
+ char c = reader.PeekChar();
+ if(c >= '0' && c <= '9') {
+ t.type = Token::Type::VALUE;
+ t.value = reader.ReadIntegerDecimal();
+ } else if(reader.TryChar('n')) {
+ t.type = Token::Type::VALUE;
+ t.value = value;
+ } else if(reader.TryChar('%')) {
+ t.type = Token::Type::BINARY_OP;
+ t.op = Token::Op::MOD;
+ } else if(reader.TryChar('<')) {
+ t.type = Token::Type::BINARY_OP;
+ if(reader.TryChar('=')) {
+ t.op = Token::Op::LE;
+ } else {
+ t.op = Token::Op::LT;
+ }
+ } else if(reader.TryChar('>')) {
+ t.type = Token::Type::BINARY_OP;
+ if(reader.TryChar('=')) {
+ t.op = Token::Op::GE;
+ } else {
+ t.op = Token::Op::GT;
+ }
+ } else if(reader.TryChar('!')) {
+ reader.ExpectChar('=');
+ t.type = Token::Type::BINARY_OP;
+ t.op = Token::Op::NEQ;
+ } else if(reader.TryChar('=')) {
+ reader.ExpectChar('=');
+ t.type = Token::Type::BINARY_OP;
+ t.op = Token::Op::EQ;
+ } else if(reader.TryChar('&')) {
+ reader.ExpectChar('&');
+ t.type = Token::Type::BINARY_OP;
+ t.op = Token::Op::AND;
+ } else if(reader.TryChar('|')) {
+ reader.ExpectChar('|');
+ t.type = Token::Type::BINARY_OP;
+ t.op = Token::Op::OR;
+ } else if(reader.TryChar('?')) {
+ t.type = Token::Type::QUERY;
+ } else if(reader.TryChar(':')) {
+ t.type = Token::Type::COLON;
+ } else if(reader.TryChar('(')) {
+ t.type = Token::Type::PAREN_LEFT;
+ } else if(reader.TryChar(')')) {
+ t.type = Token::Type::PAREN_RIGHT;
+ } else if(reader.AtEnd()) {
+ t.type = Token::Type::END;
+ } else {
+ ssassert(false, "Unexpected character");
+ }
+
+ return t;
+}
+
+PluralExpr::Token PluralExpr::PopToken() {
+ ssassert(!stack.empty(), "Expected a non-empty stack");
+ Token t = stack.back();
+ stack.pop_back();
+ return t;
+}
+
+void PluralExpr::Reduce() {
+ Token r;
+ r.type = Token::Type::VALUE;
+
+ Token a = PopToken();
+ ssassert(a.type == Token::Type::VALUE, "Expected 1st operand to be a value");
+
+ Token op = PopToken();
+ switch(op.type) {
+ case Token::Type::BINARY_OP: {
+ Token b = PopToken();
+ ssassert(b.type == Token::Type::VALUE, "Expected 2nd operand to be a value");
+
+ switch(op.op) {
+ case Token::Op::EQ:
+ r.value = (a.value == b.value ? 1 : 0);
+ break;
+ case Token::Op::NEQ:
+ r.value = (a.value != b.value ? 1 : 0);
+ break;
+ case Token::Op::LT:
+ r.value = (b.value < a.value ? 1 : 0);
+ break;
+ case Token::Op::GT:
+ r.value = (b.value > a.value ? 1 : 0);
+ break;
+ case Token::Op::LE:
+ r.value = (b.value <= a.value ? 1 : 0);
+ break;
+ case Token::Op::GE:
+ r.value = (b.value >= a.value ? 1 : 0);
+ break;
+ case Token::Op::AND:
+ r.value = a.value && b.value;
+ break;
+ case Token::Op::OR:
+ r.value = a.value || b.value;
+ break;
+ case Token::Op::MOD:
+ r.value = b.value % a.value;
+ break;
+ case Token::Op::NONE:
+ ssassert(false, "Unexpected operator");
+ }
+ break;
+ }
+
+ case Token::Type::COLON: {
+ Token b = PopToken();
+ ssassert(PopToken().type == Token::Type::QUERY, "Expected ?");
+ Token c = PopToken();
+ r.value = c.value ? b.value : a.value;
+ break;
+ }
+
+ default:
+ ssassert(false, "Unexpected operator type");
+ }
+
+ stack.push_back(r);
+}
+
+void PluralExpr::Eval() {
+ while(true) {
+ Token t = Lex();
+ switch(t.type) {
+ case Token::Type::END:
+ case Token::Type::PAREN_RIGHT:
+ while(stack.size() > 1 &&
+ stack.end()[-2].type != Token::Type::PAREN_LEFT) {
+ Reduce();
+ }
+ if(t.type == Token::Type::PAREN_RIGHT) {
+ ssassert(stack.size() > 1, "Expected (");
+ stack.push_back(t);
+ }
+ return;
+
+ case Token::Type::PAREN_LEFT:
+ stack.push_back(t);
+ Eval();
+ if(stack.back().type != Token::Type::PAREN_RIGHT) {
+ ssassert(false, "Expected )");
+ }
+ stack.pop_back();
+ stack.erase(stack.end() - 2);
+ break;
+
+ case Token::Type::VALUE:
+ stack.push_back(t);
+ break;
+
+ case Token::Type::BINARY_OP:
+ case Token::Type::QUERY:
+ case Token::Type::COLON:
+ while(stack.size() > 1 &&
+ stack.end()[-2].type != Token::Type::PAREN_LEFT &&
+ t.Precedence() < stack.end()[-2].Precedence()) {
+ Reduce();
+ }
+ stack.push_back(t);
+ break;
+ }
+ }
+}
+
+unsigned PluralExpr::Eval(const std::string &s, unsigned n) {
+ PluralExpr expr = {};
+ expr.reader = ASCIIReader::From(s);
+ expr.value = n;
+ expr.Eval();
+
+ Token t = expr.PopToken();
+ ssassert(t.type == Token::Type::VALUE, "Expected a value");
+ return t.value;
+}
+
+//-----------------------------------------------------------------------------
+// Gettext message keys
+//-----------------------------------------------------------------------------
+
+class TranslationKey {
+public:
+ bool hasContext;
+ std::string context;
+ std::string ident;
+};
+
+struct TranslationKeyLess {
+ bool operator()(const TranslationKey &a, const TranslationKey &b) const {
+ return a.hasContext < b.hasContext ||
+ (a.hasContext == b.hasContext && a.context < b.context) ||
+ (a.hasContext == b.hasContext && a.context == b.context && a.ident < b.ident);
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Gettext .po file parsing
+//-----------------------------------------------------------------------------
+
+class GettextParser {
+public:
+ ASCIIReader reader;
+
+ unsigned pluralCount;
+ std::string pluralExpr;
+
+ std::map<TranslationKey, std::vector<std::string>, TranslationKeyLess> messages;
+
+ void SkipSpace();
+ std::string ReadString();
+ void ParseHeader(const std::string &header);
+ void Parse();
+};
+
+void GettextParser::SkipSpace() {
+ while(!reader.AtEnd()) {
+ if(reader.TryChar('#')) {
+ reader.SkipUntilEol();
+ } else if(!reader.SkipSpace()) {
+ break;
+ }
+ }
+}
+
+std::string GettextParser::ReadString() {
+ SkipSpace();
+ reader.ExpectChar('"');
+
+ std::string result;
+ while(true) {
+ if(reader.AtEnd()) {
+ ssassert(false, "Unexpected EOF within a string");
+ } else if(reader.TryChar('\"')) {
+ SkipSpace();
+ if(!reader.TryChar('\"')) {
+ break;
+ }
+ } else if(reader.TryChar('\\')) {
+ if(reader.TryChar('\\')) {
+ result += '\\';
+ } else if(reader.TryChar('n')) {
+ result += '\n';
+ } else if(reader.TryChar('t')) {
+ result += '\t';
+ } else if(reader.TryChar('"')) {
+ result += '"';
+ } else {
+ ssassert(false, "Unexpected escape sequence");
+ }
+ } else {
+ result += reader.ReadChar();
+ }
+ }
+ return result;
+}
+
+void GettextParser::ParseHeader(const std::string &header) {
+ ASCIIReader reader = ASCIIReader::From(header);
+ while(!reader.AtEnd()) {
+ reader.SkipSpace();
+ if(reader.TryString("Plural-Forms:")) {
+ reader.SkipSpace();
+ reader.ExpectString("nplurals=");
+ reader.SkipSpace();
+ pluralCount = reader.ReadIntegerDecimal();
+ reader.SkipSpace();
+ reader.ExpectString(";");
+ reader.SkipSpace();
+ reader.ExpectString("plural=");
+ pluralExpr = reader.ReadUntilEol();
+ } else {
+ reader.SkipUntilEol();
+ }
+ }
+}
+
+void GettextParser::Parse() {
+ // Default to a single form, in case a header is missing.
+ pluralCount = 1;
+ pluralExpr = "0";
+
+ SkipSpace();
+ while(!reader.AtEnd()) {
+ TranslationKey key = {};
+
+ if(reader.TryString("msgctxt")) {
+ key.hasContext = true;
+ key.context = ReadString();
+ }
+
+ reader.ExpectString("msgid");
+ key.ident = ReadString();
+
+ if(reader.TryString("msgid_plural")) {
+ ReadString(); // we don't need it
+ }
+
+ std::vector<std::string> msgstrs;
+ while(reader.TryString("msgstr")) {
+ if(reader.TryChar('[')) {
+ unsigned index = reader.ReadIntegerDecimal();
+ reader.ExpectChar(']');
+ if(msgstrs.size() <= index) {
+ msgstrs.resize(index + 1);
+ }
+ msgstrs[index] = ReadString();
+ } else {
+ msgstrs.emplace_back(ReadString());
+ break;
+ }
+ }
+
+ if(key.ident.empty()) {
+ ssassert(msgstrs.size() == 1,
+ "Expected exactly one header msgstr");
+ ParseHeader(msgstrs[0]);
+ } else {
+ ssassert(msgstrs.size() == 1 ||
+ msgstrs.size() == pluralCount,
+ "Expected msgstr count to match plural form count");
+ messages.emplace(key, msgstrs);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Translation management
+//-----------------------------------------------------------------------------
+
+class Translation {
+public:
+ unsigned pluralCount;
+ std::string pluralExpr;
+
+ std::map<TranslationKey, std::vector<std::string>, TranslationKeyLess> messages;
+
+ static Translation From(const std::string &poData);
+
+ const std::string &Translate(const TranslationKey &key);
+ const std::string &TranslatePlural(const TranslationKey &key, unsigned n);
+};
+
+Translation Translation::From(const std::string &poData) {
+ GettextParser parser = {};
+ parser.reader = ASCIIReader::From(poData);
+ parser.Parse();
+
+ Translation trans = {};
+ trans.pluralCount = parser.pluralCount;
+ trans.pluralExpr = parser.pluralExpr;
+ trans.messages = parser.messages;
+ return trans;
+}
+
+const std::string &Translation::Translate(const TranslationKey &key) {
+ auto it = messages.find(key);
+ if(it == messages.end()) {
+ dbp("Missing (absent) translation for %s'%s'", key.context.c_str(), key.ident.c_str());
+ messages[key].emplace_back(key.ident);
+ it = messages.find(key);
+ }
+ if(it->second[0].empty()) {
+ dbp("Missing (empty) translation for %s'%s'", key.context.c_str(), key.ident.c_str());
+ it->second[0] = key.ident;
+ }
+ if(it->second.size() != 1) {
+ dbp("Incorrect use of translated message %s'%s'", key.context.c_str(), key.ident.c_str());
+ ssassert(false, "Using a message with a plural form without a number");
+ }
+ return it->second[0];
+}
+
+const std::string &Translation::TranslatePlural(const TranslationKey &key, unsigned n) {
+ unsigned pluralForm = PluralExpr::Eval(pluralExpr, n);
+
+ auto it = messages.find(key);
+ if(it == messages.end()) {
+ dbp("Missing (absent) translation for %s'%s'", key.context.c_str(), key.ident.c_str());
+ for(unsigned i = 0; i < pluralCount; i++) {
+ messages[key].emplace_back(key.ident);
+ }
+ it = messages.find(key);
+ }
+ if(it->second[pluralForm].empty()) {
+ dbp("Missing (empty) translation for %s'%s'[%d]",
+ key.context.c_str(), key.ident.c_str(), pluralForm);
+ it->second[pluralForm] = key.ident;
+ }
+ return it->second[pluralForm];
+}
+
+//-----------------------------------------------------------------------------
+// Locale management
+//-----------------------------------------------------------------------------
+
+static std::set<Locale, LocaleLess> locales;
+static std::map<Locale, Translation, LocaleLess> translations;
+static Translation dummyTranslation;
+static Translation *currentTranslation = &dummyTranslation;
+
+const std::set<Locale, LocaleLess> &Locales() {
+ if(!locales.empty()) return locales;
+
+ std::string localeList = LoadString("locales.txt");
+ ASCIIReader reader = ASCIIReader::From(localeList);
+ while(!reader.AtEnd()) {
+ reader.SkipSpace();
+ if(reader.TryChar('#')) {
+ reader.SkipUntilEol();
+ continue;
+ }
+
+ std::smatch m;
+ reader.ExpectRegex(std::regex("([a-z]{2})-([A-Z]{2}),([0-9A-F]{4}),(.+?)\n"), &m);
+ Locale locale = {};
+ locale.language = m.str(1);
+ locale.region = m.str(2);
+ locale.lcid = std::stoi(m.str(3), NULL, 16);
+ locale.displayName = m.str(4);
+ locales.emplace(locale);
+ }
+ return locales;
+}
+
+template<class Predicate>
+bool SetLocale(Predicate pred) {
+ auto it = std::find_if(Locales().begin(), Locales().end(), pred);
+ if(it != locales.end()) {
+ std::string filename = "locales/" + it->language + "_" + it->region + ".po";
+ translations[*it] = Translation::From(LoadString(filename));
+ currentTranslation = &translations[*it];
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool SetLocale(const std::string &name) {
+ return SetLocale([&](const Locale &locale) {
+ if(name == locale.language + "-" + locale.region) {
+ return true;
+ } else if(name == locale.language + "_" + locale.region) {
+ return true;
+ } else if(name == locale.language) {
+ return true;
+ } else {
+ return false;
+ }
+ });
+}
+
+bool SetLocale(uint16_t lcid) {
+ return SetLocale([&](const Locale &locale) {
+ return locale.lcid == lcid;
+ });
+}
+
+const std::string &Translate(const char *msgid) {
+ TranslationKey key = {};
+ key.ident = msgid;
+ return currentTranslation->Translate(key);
+}
+
+const std::string &Translate(const char *msgctxt, const char *msgid) {
+ TranslationKey key = {};
+ key.hasContext = true;
+ key.context = msgctxt;
+ key.ident = msgid;
+ return currentTranslation->Translate(key);
+}
+
+const std::string &TranslatePlural(const char *msgid, unsigned n) {
+ TranslationKey key = {};
+ key.ident = msgid;
+ return currentTranslation->TranslatePlural(key, n);
+}
+
+const std::string &TranslatePlural(const char *msgctxt, const char *msgid, unsigned n) {
+ TranslationKey key = {};
+ key.hasContext = true;
+ key.context = msgctxt;
+ key.ident = msgid;
+ return currentTranslation->TranslatePlural(key, n);
+}
+
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Discovery and loading of our resources (icons, fonts, templates, etc).
+//
+// Copyright 2016 whitequark
+//-----------------------------------------------------------------------------
+
+#ifndef SOLVESPACE_RESOURCE_H
+#define SOLVESPACE_RESOURCE_H
+
+class Camera;
+class Point2d;
+class Pixmap;
+class Vector;
+class RgbaColor;
+
+std::string LoadString(const std::string &name);
+std::string LoadStringFromGzip(const std::string &name);
+std::shared_ptr<Pixmap> LoadPng(const std::string &name);
+
+class Pixmap {
+public:
+ enum class Format { BGRA, RGBA, BGR, RGB, A };
+
+ Format format;
+ size_t width;
+ size_t height;
+ size_t stride;
+ std::vector<uint8_t> data;
+
+ static std::shared_ptr<Pixmap> Create(Format format, size_t width, size_t height);
+ static std::shared_ptr<Pixmap> FromPng(const uint8_t *data, size_t size, bool flip = false);
+
+ static std::shared_ptr<Pixmap> ReadPng(FILE *f, bool flip = false);
+ static std::shared_ptr<Pixmap> ReadPng(const Platform::Path &filename, bool flip = false);
+ bool WritePng(FILE *f, bool flip = false);
+ bool WritePng(const Platform::Path &filename, bool flip = false);
+
+ size_t GetBytesPerPixel() const;
+ RgbaColor GetPixel(size_t x, size_t y) const;
+ bool Equals(const Pixmap &other) const;
+
+ void ConvertTo(Format newFormat);
+ void SetPixel(size_t x, size_t y, RgbaColor color);
+
+ std::shared_ptr<Pixmap> Copy();
+};
+
+class BitmapFont {
+public:
+ struct Glyph {
+ uint8_t advanceCells;
+ uint16_t position;
+ };
+
+ std::string unifontData;
+ std::map<char32_t, Glyph> glyphs;
+ std::shared_ptr<Pixmap> texture;
+ bool textureUpdated;
+ uint16_t nextPosition;
+
+ static BitmapFont From(std::string &&unifontData);
+ static BitmapFont Create();
+
+ bool IsEmpty() const { return unifontData.empty(); }
+ const Glyph &GetGlyph(char32_t codepoint);
+ void LocateGlyph(char32_t codepoint, double *s0, double *t0, double *s1, double *t1,
+ size_t *advanceWidth, size_t *boundingHeight);
+
+ void AddGlyph(char32_t codepoint, std::shared_ptr<const Pixmap> pixmap);
+
+ size_t GetWidth(char32_t codepoint);
+ size_t GetWidth(const std::string &str);
+};
+
+class VectorFont {
+public:
+ struct Contour {
+ std::vector<Point2d> points;
+ };
+
+ struct Glyph {
+ std::vector<Contour> contours;
+ double leftSideBearing;
+ double boundingWidth;
+ double advanceWidth;
+ };
+
+ std::string lffData;
+ std::map<char32_t, Glyph> glyphs;
+ double rightSideBearing;
+ double capHeight;
+ double ascender;
+ double descender;
+
+ static VectorFont From(std::string &&lffData);
+ static VectorFont *Builtin();
+
+ bool IsEmpty() const { return lffData.empty(); }
+ const Glyph &GetGlyph(char32_t codepoint);
+
+ double GetCapHeight(double forCapHeight) const;
+ double GetHeight(double forCapHeight) const;
+ double GetWidth(double forCapHeight, const std::string &str);
+ Vector GetExtents(double forCapHeight, const std::string &str);
+
+ void Trace(double forCapHeight, Vector o, Vector u, Vector v, const std::string &str,
+ const std::function<void(Vector, Vector)> &traceEdge);
+ void Trace(double forCapHeight, Vector o, Vector u, Vector v, const std::string &str,
+ const std::function<void(Vector, Vector)> &traceEdge, const Camera &camera);
+};
+
+#endif
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
-#ifndef __SKETCH_H
-#define __SKETCH_H
+#ifndef SOLVESPACE_SKETCH_H
+#define SOLVESPACE_SKETCH_H
class hGroup;
class hRequest;
class Equation;
class Style;
+enum class PolyError : uint32_t {
+ GOOD = 0,
+ NOT_CLOSED = 1,
+ NOT_COPLANAR = 2,
+ SELF_INTERSECTING = 3,
+ ZERO_LEN_EDGE = 4
+};
+
+enum class StipplePattern : uint32_t {
+ CONTINUOUS = 0,
+ SHORT_DASH = 1,
+ DASH = 2,
+ LONG_DASH = 3,
+ DASH_DOT = 4,
+ DASH_DOT_DOT = 5,
+ DOT = 6,
+ FREEHAND = 7,
+ ZIGZAG = 8,
+
+ LAST = ZIGZAG
+};
+
+const std::vector<double> &StipplePatternDashes(StipplePattern pattern);
+double StipplePatternLength(StipplePattern pattern);
+
+enum class Command : uint32_t;
// All of the hWhatever handles are a 32-bit ID, that is used to represent
// some data structure in the sketch.
// bits 15: 0 -- group index
uint32_t v;
- inline hEntity entity(int i);
- inline hParam param(int i);
- inline hEquation equation(int i);
+ inline hEntity entity(int i) const;
+ inline hParam param(int i) const;
+ inline hEquation equation(int i) const;
};
+
+template<>
+struct IsHandleOracle<hGroup> : std::true_type {};
+
class hRequest {
public:
// bits 15: 0 -- request index
uint32_t v;
- inline hEntity entity(int i);
- inline hParam param(int i);
+ inline hEntity entity(int i) const;
+ inline hParam param(int i) const;
- inline bool IsFromReferences(void);
+ inline bool IsFromReferences() const;
};
+
+template<>
+struct IsHandleOracle<hRequest> : std::true_type {};
+
class hEntity {
public:
// bits 15: 0 -- entity index
// 31:16 -- request index
uint32_t v;
- inline bool isFromRequest(void);
- inline hRequest request(void);
- inline hGroup group(void);
- inline hEquation equation(int i);
+ inline bool isFromRequest() const;
+ inline hRequest request() const;
+ inline hGroup group() const;
+ inline hEquation equation(int i) const;
};
+
+template<>
+struct IsHandleOracle<hEntity> : std::true_type {};
+
class hParam {
public:
// bits 15: 0 -- param index
// 31:16 -- request index
uint32_t v;
- inline hRequest request(void);
+ inline hRequest request() const;
};
+template<>
+struct IsHandleOracle<hParam> : std::true_type {};
+
class hStyle {
public:
uint32_t v;
};
+template<>
+struct IsHandleOracle<hStyle> : std::true_type {};
-class EntityId {
-public:
+struct EntityId {
uint32_t v; // entity ID, starting from 0
};
-class EntityMap {
-public:
- int tag;
- EntityId h;
+template<>
+struct IsHandleOracle<EntityId> : std::true_type {};
+
+struct EntityKey {
hEntity input;
int copyNumber;
// (input, copyNumber) gets mapped to ((Request)xxx).entity(h.v)
-
- void Clear(void) {}
};
+struct EntityKeyHash {
+ size_t operator()(const EntityKey &k) const {
+ size_t h1 = std::hash<uint32_t>{}(k.input.v),
+ h2 = std::hash<uint32_t>{}(k.copyNumber);
+ return h1 ^ (h2 << 1);
+ }
+};
+struct EntityKeyEqual {
+ bool operator()(const EntityKey &a, const EntityKey &b) const {
+ return std::tie(a.input, a.copyNumber) == std::tie(b.input, b.copyNumber);
+ }
+};
+typedef std::unordered_map<EntityKey, EntityId, EntityKeyHash, EntityKeyEqual> EntityMap;
// A set of requests. Every request must have an associated group.
class Group {
int tag;
hGroup h;
- enum {
+ enum class CopyAs {
+ NUMERIC,
+ N_TRANS,
+ N_ROT_AA,
+ N_ROT_TRANS,
+ N_ROT_AXIS_TRANS,
+ };
+
+ enum class Type : uint32_t {
DRAWING_3D = 5000,
DRAWING_WORKPLANE = 5001,
EXTRUDE = 5100,
LATHE = 5101,
+ REVOLVE = 5102,
+ HELIX = 5103,
ROTATE = 5200,
TRANSLATE = 5201,
LINKED = 5300
};
- int type;
+ Group::Type type;
int order;
double scale;
bool clean;
+ bool dofCheckOk;
hEntity activeWorkplane;
double valA;
double valB;
RgbaColor color;
struct {
- int how;
+ SolveResult how;
int dof;
+ int findToFixTimeout;
+ bool timeout;
List<hConstraint> remove;
} solved;
- enum {
+ enum class Subtype : uint32_t {
// For drawings in 2d
WORKPLANE_BY_POINT_ORTHO = 6000,
WORKPLANE_BY_LINE_SEGMENTS = 6001,
ONE_SIDED = 7000,
TWO_SIDED = 7001
};
- int subtype;
+ Group::Subtype subtype;
bool skipFirst; // for step and repeat ops
SPolygon polyLoops;
SBezierLoopSetSet bezierLoops;
- SBezierList bezierOpens;
- enum {
- POLY_GOOD = 0,
- POLY_NOT_CLOSED = 1,
- POLY_NOT_COPLANAR = 2,
- POLY_SELF_INTERSECTING = 3,
- POLY_ZERO_LEN_EDGE = 4
- };
+ SBezierLoopSet bezierOpens;
+
struct {
- int how;
+ PolyError how;
SEdge notClosedAt;
Vector errorPointAt;
} polyError;
bool displayDirty;
SMesh displayMesh;
- SEdgeList displayEdges;
SOutlineList displayOutlines;
- enum {
- COMBINE_AS_UNION = 0,
- COMBINE_AS_DIFFERENCE = 1,
- COMBINE_AS_ASSEMBLE = 2
+ enum class CombineAs : uint32_t {
+ UNION = 0,
+ DIFFERENCE = 1,
+ ASSEMBLE = 2,
+ INTERSECTION = 3
};
- int meshCombine;
+ CombineAs meshCombine;
bool forceToMesh;
- IdList<EntityMap,EntityId> remap;
- enum { REMAP_PRIME = 19477 };
- int remapCache[REMAP_PRIME];
+ EntityMap remap;
- std::string linkFile;
- std::string linkFileRel;
+ Platform::Path linkFile;
SMesh impMesh;
SShell impShell;
EntityList impEntity;
std::string name;
- void Activate(void);
- std::string DescriptionString(void);
- void Clear(void);
+ void Activate();
+ std::string DescriptionString();
+ void Clear();
static void AddParam(ParamList *param, hParam hp, double v);
void Generate(EntityList *entity, ParamList *param);
bool IsSolvedOkay();
void TransformImportedBy(Vector t, Quaternion q);
+ bool IsForcedToMeshBySource() const;
+ bool IsForcedToMesh() const;
// When a request generates entities from entities, and the source
// entities may have come from multiple requests, it's necessary to
// remap the entity ID so that it's still unique. We do this with a
REMAP_LATHE_END = 1007,
REMAP_PT_TO_ARC = 1008,
REMAP_PT_TO_NORMAL = 1009,
+ REMAP_LATHE_ARC_CENTER = 1010,
};
hEntity Remap(hEntity in, int copyNumber);
void MakeExtrusionLines(EntityList *el, hEntity in);
- void MakeLatheCircles(IdList<Entity,hEntity> *el, IdList<Param,hParam> *param, hEntity in, Vector pt, Vector axis, int ai);
+ void MakeLatheCircles(IdList<Entity,hEntity> *el, IdList<Param,hParam> *param, hEntity in, Vector pt, Vector axis);
+ void MakeLatheSurfacesSelectable(IdList<Entity, hEntity> *el, hEntity in, Vector axis);
+ void MakeRevolveEndFaces(IdList<Entity,hEntity> *el, hEntity pt, int ai, int af);
void MakeExtrusionTopBottomFaces(EntityList *el, hEntity pt);
void CopyEntity(EntityList *el,
Entity *ep, int timesApplied, int remap,
hParam dx, hParam dy, hParam dz,
- hParam qw, hParam qvx, hParam qvy, hParam qvz,
- bool asTrans, bool asAxisAngle);
+ hParam qw, hParam qvx, hParam qvy, hParam qvz, hParam dist,
+ CopyAs as);
void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index);
void GenerateEquations(IdList<Equation,hEquation> *l);
- bool IsVisible(void);
- int GetNumConstraints();
+ bool IsVisible();
+ size_t GetNumConstraints();
Vector ExtrusionGetVector();
void ExtrusionForceVectorTo(const Vector &v);
// Assembling the curves into loops, and into a piecewise linear polygon
// at the same time.
void AssembleLoops(bool *allClosed, bool *allCoplanar, bool *allNonZeroLen);
- void GenerateLoops(void);
+ void GenerateLoops();
// And the mesh stuff
- Group *PreviousGroup(void);
- Group *RunningMeshGroup(void);
+ Group *PreviousGroup() const;
+ Group *RunningMeshGroup() const;
bool IsMeshGroup();
- void GenerateShellAndMesh(void);
- template<class T> void GenerateForStepAndRepeat(T *steps, T *outs);
- template<class T> void GenerateForBoolean(T *a, T *b, T *o, int how);
- void GenerateDisplayItems(void);
- void DrawDisplayItems(int t);
- void Draw(void);
- RgbaColor GetLoopSetFillColor(SBezierLoopSet *sbls,
- bool *allSame, Vector *errorAt);
- void FillLoopSetAsPolygon(SBezierLoopSet *sbls);
- void DrawFilledPaths(void);
-
- SPolygon GetPolygon(void);
-
- static void MenuGroup(int id);
+
+ void GenerateShellAndMesh();
+ template<class T> void GenerateForStepAndRepeat(T *steps, T *outs, Group::CombineAs forWhat);
+ template<class T> void GenerateForBoolean(T *a, T *b, T *o, Group::CombineAs how);
+ void GenerateDisplayItems();
+
+ enum class DrawMeshAs { DEFAULT, HOVERED, SELECTED };
+ void DrawMesh(DrawMeshAs how, Canvas *canvas);
+ void Draw(Canvas *canvas);
+ void DrawPolyError(Canvas *canvas);
+ void DrawFilledPaths(Canvas *canvas);
+ void DrawContourAreaLabels(Canvas *canvas);
+
+ SPolygon GetPolygon();
+
+ static void MenuGroup(Command id);
+ static void MenuGroup(Command id, Platform::Path linkFile);
};
// A user request for some primitive or derived operation; for example a
hRequest h;
// Types of requests
- enum {
+ enum class Type : uint32_t {
WORKPLANE = 100,
DATUM_POINT = 101,
LINE_SEGMENT = 200,
CUBIC_PERIODIC = 301,
CIRCLE = 400,
ARC_OF_CIRCLE = 500,
- TTF_TEXT = 600
+ TTF_TEXT = 600,
+ IMAGE = 700
};
- int type;
+ Request::Type type;
int extraPoints;
hEntity workplane; // or Entity::FREE_IN_3D
hStyle style;
bool construction;
+
std::string str;
std::string font;
+ Platform::Path file;
+ double aspectRatio;
static hParam AddParam(ParamList *param, hParam hp);
void Generate(EntityList *entity, ParamList *param);
- std::string DescriptionString(void);
- int IndexOfPoint(hEntity he);
+ std::string DescriptionString() const;
+ int IndexOfPoint(hEntity he) const;
- void Clear(void) {}
+ void Clear() {}
};
#define MAX_POINTS_IN_ENTITY (12)
static const hEntity FREE_IN_3D;
static const hEntity NO_ENTITY;
- enum {
+ enum class Type : uint32_t {
POINT_IN_3D = 2000,
POINT_IN_2D = 2001,
POINT_N_TRANS = 2010,
POINT_N_ROT_TRANS = 2011,
POINT_N_COPY = 2012,
POINT_N_ROT_AA = 2013,
+ POINT_N_ROT_AXIS_TRANS = 2014,
NORMAL_IN_3D = 3000,
NORMAL_IN_2D = 3001,
FACE_N_ROT_TRANS = 5002,
FACE_N_TRANS = 5003,
FACE_N_ROT_AA = 5004,
-
+ FACE_ROT_NORMAL_PT = 5005,
+ FACE_N_ROT_AXIS_TRANS = 5006,
WORKPLANE = 10000,
LINE_SEGMENT = 11000,
CUBIC_PERIODIC = 12001,
CIRCLE = 13000,
ARC_OF_CIRCLE = 14000,
- TTF_TEXT = 15000
+ TTF_TEXT = 15000,
+ IMAGE = 16000
};
- int type;
+ Type type;
hGroup group;
hEntity workplane; // or Entity::FREE_IN_3D
hEntity distance;
// The only types that have their own params are points, normals,
// and directions.
- hParam param[7];
+ hParam param[8];
// Transformed points/normals/distances have their numerical base
Vector numPoint;
std::string str;
std::string font;
+ Platform::Path file;
+ double aspectRatio;
// For entities that are derived by a transformation, the number of
// times to apply the transformation.
int timesApplied;
- Quaternion GetAxisAngleQuaternion(int param0);
- ExprQuaternion GetAxisAngleQuaternionExprs(int param0);
+ Quaternion GetAxisAngleQuaternion(int param0) const;
+ ExprQuaternion GetAxisAngleQuaternionExprs(int param0) const;
- bool IsCircle(void);
- Expr *CircleGetRadiusExpr(void);
- double CircleGetRadiusNum(void);
- void ArcGetAngles(double *thetaa, double *thetab, double *dtheta);
+ bool IsCircle() const;
+ Expr *CircleGetRadiusExpr() const;
+ double CircleGetRadiusNum() const;
+ void ArcGetAngles(double *thetaa, double *thetab, double *dtheta) const;
- bool HasVector(void);
- ExprVector VectorGetExprs(void);
- Vector VectorGetNum(void);
- Vector VectorGetRefPoint(void);
- Vector VectorGetStartPoint(void);
+ bool HasVector() const;
+ ExprVector VectorGetExprs() const;
+ ExprVector VectorGetExprsInWorkplane(hEntity wrkpl) const;
+ Vector VectorGetNum() const;
+ Vector VectorGetRefPoint() const;
+ Vector VectorGetStartPoint() const;
// For distances
- bool IsDistance(void);
- double DistanceGetNum(void);
- Expr *DistanceGetExpr(void);
+ bool IsDistance() const;
+ double DistanceGetNum() const;
+ Expr *DistanceGetExpr() const;
void DistanceForceTo(double v);
- bool IsWorkplane(void);
+ bool IsWorkplane() const;
// The plane is points P such that P dot (xn, yn, zn) - d = 0
- void WorkplaneGetPlaneExprs(ExprVector *n, Expr **d);
- ExprVector WorkplaneGetOffsetExprs(void);
- Vector WorkplaneGetOffset(void);
- EntityBase *Normal(void);
-
- bool IsFace(void);
- ExprVector FaceGetNormalExprs(void);
- Vector FaceGetNormalNum(void);
- ExprVector FaceGetPointExprs(void);
- Vector FaceGetPointNum(void);
-
- bool IsPoint(void);
+ void WorkplaneGetPlaneExprs(ExprVector *n, Expr **d) const;
+ ExprVector WorkplaneGetOffsetExprs() const;
+ Vector WorkplaneGetOffset() const;
+ EntityBase *Normal() const;
+
+ bool IsFace() const;
+ ExprVector FaceGetNormalExprs() const;
+ Vector FaceGetNormalNum() const;
+ ExprVector FaceGetPointExprs() const;
+ Vector FaceGetPointNum() const;
+
+ bool IsPoint() const;
// Applies for any of the point types
- Vector PointGetNum(void);
- ExprVector PointGetExprs(void);
- void PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v);
+ Vector PointGetNum() const;
+ ExprVector PointGetExprs() const;
+ void PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) const;
+ ExprVector PointGetExprsInWorkplane(hEntity wrkpl) const;
void PointForceTo(Vector v);
+ void PointForceParamTo(Vector v);
// These apply only the POINT_N_ROT_TRANS, which has an assoc rotation
- Quaternion PointGetQuaternion(void);
+ Quaternion PointGetQuaternion() const;
void PointForceQuaternionTo(Quaternion q);
- bool IsNormal(void);
+ bool IsNormal() const;
// Applies for any of the normal types
- Quaternion NormalGetNum(void);
- ExprQuaternion NormalGetExprs(void);
+ Quaternion NormalGetNum() const;
+ ExprQuaternion NormalGetExprs() const;
void NormalForceTo(Quaternion q);
- Vector NormalU(void);
- Vector NormalV(void);
- Vector NormalN(void);
- ExprVector NormalExprsU(void);
- ExprVector NormalExprsV(void);
- ExprVector NormalExprsN(void);
+ Vector NormalU() const;
+ Vector NormalV() const;
+ Vector NormalN() const;
+ ExprVector NormalExprsU() const;
+ ExprVector NormalExprsV() const;
+ ExprVector NormalExprsN() const;
- Vector CubicGetStartNum(void);
- Vector CubicGetFinishNum(void);
- ExprVector CubicGetStartTangentExprs(void);
- ExprVector CubicGetFinishTangentExprs(void);
- Vector CubicGetStartTangentNum(void);
- Vector CubicGetFinishTangentNum(void);
+ Vector CubicGetStartNum() const;
+ Vector CubicGetFinishNum() const;
+ ExprVector CubicGetStartTangentExprs() const;
+ ExprVector CubicGetFinishTangentExprs() const;
+ Vector CubicGetStartTangentNum() const;
+ Vector CubicGetFinishTangentNum() const;
- bool HasEndpoints(void);
- Vector EndpointStart();
- Vector EndpointFinish();
+ bool HasEndpoints() const;
+ Vector EndpointStart() const;
+ Vector EndpointFinish() const;
+ bool IsInPlane(Vector norm, double distance) const;
- void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index);
- void GenerateEquations(IdList<Equation,hEquation> *l);
+ void RectGetPointsExprs(ExprVector *eap, ExprVector *ebp) const;
- void Clear(void) {}
+ void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index) const;
+ void GenerateEquations(IdList<Equation,hEquation> *l) const;
+
+ void Clear() {}
};
class Entity : public EntityBase {
// POD members with indeterminate value.
Entity() : EntityBase({}), forceHidden(), actPoint(), actNormal(),
actDistance(), actVisible(), style(), construction(),
- beziers(), edges(), edgesChordTol(), screenBBox(), screenBBoxValid(),
- dogd() {};
+ beziers(), edges(), edgesChordTol(), screenBBox(), screenBBoxValid() {};
// A linked entity that was hidden in the source file ends up hidden
// here too.
BBox screenBBox;
bool screenBBoxValid;
- // Routines to draw and hit-test the representation of the entity
- // on-screen.
- struct {
- bool drawing;
- Point2d mp;
- double dmin;
- Vector refp;
- double lineWidth;
- double stippleScale;
- int stippleType;
- int data;
- } dogd; // state for drawing or getting distance (for hit testing)
- void LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat=false, int userData = -1);
- void DrawOrGetDistance(void);
-
- bool IsStylable();
- bool IsVisible(void);
- bool PointIsFromReferences(void);
-
- void ComputeInterpolatingSpline(SBezierList *sbl, bool periodic);
- void GenerateBezierCurves(SBezierList *sbl);
- void GenerateEdges(SEdgeList *el, bool includingConstruction=false);
-
- static void DrawAll(bool drawAsHidden);
- void Draw(bool drawAsHidden);
- double GetDistance(Point2d mp);
- Vector GetReferencePos(void);
+ bool IsStylable() const;
+ bool IsVisible() const;
+ bool CanBeDragged() const;
- void CalculateNumerical(bool forExport);
+ enum class DrawAs { DEFAULT, OVERLAY, HIDDEN, HOVERED, SELECTED };
+ void Draw(DrawAs how, Canvas *canvas);
+ void GetReferencePoints(std::vector<Vector> *refs);
+ int GetPositionOfPoint(const Camera &camera, Point2d p);
- std::string DescriptionString(void);
+ void ComputeInterpolatingSpline(SBezierList *sbl, bool periodic) const;
+ void GenerateBezierCurves(SBezierList *sbl) const;
+ void GenerateEdges(SEdgeList *el);
SBezierList *GetOrGenerateBezierCurves();
SEdgeList *GetOrGenerateEdges();
BBox GetOrGenerateScreenBBox(bool *hasBBox);
+ void CalculateNumerical(bool forExport);
+
+ std::string DescriptionString() const;
+
void Clear() {
beziers.l.Clear();
edges.l.Clear();
class EntReqTable {
public:
- typedef struct {
- int reqType;
- int entType;
- int points;
- bool useExtraPoints;
- bool hasNormal;
- bool hasDistance;
- const char *description;
- } TableEntry;
-
- static const TableEntry Table[];
-
- static const char *DescriptionForRequest(int req);
- static void CopyEntityInfo(const TableEntry *te, int extraPoints,
- int *ent, int *req, int *pts, bool *hasNormal, bool *hasDistance);
- static bool GetRequestInfo(int req, int extraPoints,
- int *ent, int *pts, bool *hasNormal, bool *hasDistance);
- static bool GetEntityInfo(int ent, int extraPoints,
- int *req, int *pts, bool *hasNormal, bool *hasDistance);
- static int GetRequestForEntity(int ent);
+ static bool GetRequestInfo(Request::Type req, int extraPoints,
+ EntityBase::Type *ent, int *pts, bool *hasNormal, bool *hasDistance);
+ static bool GetEntityInfo(EntityBase::Type ent, int extraPoints,
+ Request::Type *req, int *pts, bool *hasNormal, bool *hasDistance);
+ static Request::Type GetRequestForEntity(EntityBase::Type ent);
};
class Param {
static const hParam NO_PARAM;
- void Clear(void) {}
+ void Clear() {}
};
public:
uint32_t v;
- inline hEquation equation(int i);
+ inline hEquation equation(int i) const;
+ inline hParam param(int i) const;
};
+template<>
+struct IsHandleOracle<hConstraint> : std::true_type {};
+
class ConstraintBase {
public:
int tag;
static const hConstraint NO_CONSTRAINT;
- enum {
+ enum class Type : uint32_t {
POINTS_COINCIDENT = 20,
PT_PT_DISTANCE = 30,
PT_PLANE_DISTANCE = 31,
COMMENT = 1000
};
- int type;
+ Type type;
hGroup group;
hEntity workplane;
// These are the parameters for the constraint.
double valA;
+ hParam valP;
hEntity ptA;
hEntity ptB;
hEntity entityA;
bool reference; // a ref dimension, that generates no eqs
std::string comment; // since comments are represented as constraints
- bool HasLabel(void);
+ bool Equals(const ConstraintBase &c) const {
+ return type == c.type && group == c.group && workplane == c.workplane &&
+ valA == c.valA && valP == c.valP && ptA == c.ptA && ptB == c.ptB &&
+ entityA == c.entityA && entityB == c.entityB &&
+ entityC == c.entityC && entityD == c.entityD &&
+ other == c.other && other2 == c.other2 && reference == c.reference &&
+ comment == c.comment;
+ }
+
+ bool HasLabel() const;
+ bool IsProjectible() const;
- void Generate(IdList<Equation,hEquation> *l);
- void GenerateReal(IdList<Equation,hEquation> *l);
+ void Generate(IdList<Param, hParam> *param);
+
+ void GenerateEquations(IdList<Equation,hEquation> *entity,
+ bool forReference = false) const;
// Some helpers when generating symbolic constraint equations
- void ModifyToSatisfy(void);
- void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index);
+ void ModifyToSatisfy();
+ void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index) const;
+ void AddEq(IdList<Equation,hEquation> *l, const ExprVector &v, int baseIndex = 0) const;
static Expr *DirectionCosine(hEntity wrkpl, ExprVector ae, ExprVector be);
static Expr *Distance(hEntity workplane, hEntity pa, hEntity pb);
static Expr *PointLineDistance(hEntity workplane, hEntity pt, hEntity ln);
static Expr *PointPlaneDistance(ExprVector p, hEntity plane);
- static Expr *VectorsParallel(int eq, ExprVector a, ExprVector b);
+ static ExprVector VectorsParallel3d(ExprVector a, ExprVector b, hParam p);
static ExprVector PointInThreeSpace(hEntity workplane, Expr *u, Expr *v);
- void Clear(void) {}
+ void Clear() {}
};
class Constraint : public ConstraintBase {
public:
// See Entity::Entity().
- Constraint() : ConstraintBase({}), disp(), dogd() {}
+ Constraint() : ConstraintBase({}), disp() {}
// These define how the constraint is drawn on-screen.
struct {
hStyle style;
} disp;
- // State for drawing or getting distance (for hit testing).
- struct {
- bool drawing;
- Point2d mp;
- double dmin;
- Vector refp;
- SEdgeList *sel;
- } dogd;
-
- double GetDistance(Point2d mp);
- Vector GetLabelPos(void);
- Vector GetReferencePos(void);
- void Draw(void);
- void GetEdges(SEdgeList *sel);
- bool IsStylable();
- hStyle GetStyle() const;
- bool HasLabel();
-
- void LineDrawOrGetDistance(Vector a, Vector b);
bool IsVisible() const;
- void DrawOrGetDistance(Vector *labelPos);
- std::string Label(void);
- bool DoLineExtend(Vector p0, Vector p1, Vector pt, double salient);
- void DoArcForAngle(Vector a0, Vector da, Vector b0, Vector db,
- Vector offset, Vector *ref, bool trim);
- void DoArrow(Vector p, Vector dir, Vector n, double width, double angle, double da);
- void DoLineWithArrows(Vector ref, Vector a, Vector b, bool onlyOneExt);
- int DoLineTrimmedAgainstBox(Vector ref, Vector a, Vector b, bool extend, Vector gr, Vector gu, double swidth, double sheight);
- int DoLineTrimmedAgainstBox(Vector ref, Vector a, Vector b, bool extend = true);
- void DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu);
- void StippledLine(Vector a, Vector b);
- void DoProjectedPoint(Vector *p);
- void DoEqualLenTicks(Vector a, Vector b, Vector gn);
- void DoEqualRadiusTicks(hEntity he);
-
- std::string DescriptionString(void);
-
- static hConstraint AddConstraint(Constraint *c, bool rememberForUndo);
- static hConstraint AddConstraint(Constraint *c);
- static void MenuConstrain(int id);
- static void DeleteAllConstraintsFor(int type, hEntity entityA, hEntity ptA);
+ bool IsStylable() const;
+ hStyle GetStyle() const;
+ bool HasLabel() const;
+ std::string Label() const;
+
+ enum class DrawAs { DEFAULT, HOVERED, SELECTED };
+ void Draw(DrawAs how, Canvas *canvas);
+ Vector GetLabelPos(const Camera &camera);
+ void GetReferencePoints(const Camera &camera, std::vector<Vector> *refs);
+
+ void DoLayout(DrawAs how, Canvas *canvas,
+ Vector *labelPos, std::vector<Vector> *refs);
+ void DoLine(Canvas *canvas, Canvas::hStroke hcs, Vector a, Vector b);
+ void DoStippledLine(Canvas *canvas, Canvas::hStroke hcs, Vector a, Vector b);
+ bool DoLineExtend(Canvas *canvas, Canvas::hStroke hcs,
+ Vector p0, Vector p1, Vector pt, double salient);
+ void DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
+ Vector a0, Vector da, Vector b0, Vector db,
+ Vector offset, Vector *ref, bool trim);
+ void DoArrow(Canvas *canvas, Canvas::hStroke hcs,
+ Vector p, Vector dir, Vector n, double width, double angle, double da);
+ void DoLineWithArrows(Canvas *canvas, Canvas::hStroke hcs,
+ Vector ref, Vector a, Vector b, bool onlyOneExt);
+ int DoLineTrimmedAgainstBox(Canvas *canvas, Canvas::hStroke hcs,
+ Vector ref, Vector a, Vector b, bool extend,
+ Vector gr, Vector gu, double swidth, double sheight);
+ int DoLineTrimmedAgainstBox(Canvas *canvas, Canvas::hStroke hcs,
+ Vector ref, Vector a, Vector b, bool extend = true);
+ void DoLabel(Canvas *canvas, Canvas::hStroke hcs,
+ Vector ref, Vector *labelPos, Vector gr, Vector gu);
+ void DoProjectedPoint(Canvas *canvas, Canvas::hStroke hcs, Vector *p);
+ void DoProjectedPoint(Canvas *canvas, Canvas::hStroke hcs, Vector *p, Vector n, Vector o);
+
+ void DoEqualLenTicks(Canvas *canvas, Canvas::hStroke hcs,
+ Vector a, Vector b, Vector gn, Vector *refp);
+ void DoEqualRadiusTicks(Canvas *canvas, Canvas::hStroke hcs,
+ hEntity he, Vector *refp);
+
+ std::string DescriptionString() const;
+
+ static hConstraint AddConstraint(Constraint *c, bool rememberForUndo = true);
+ static void MenuConstrain(Command id);
+ static void DeleteAllConstraintsFor(Constraint::Type type, hEntity entityA, hEntity ptA);
static hConstraint ConstrainCoincident(hEntity ptA, hEntity ptB);
- static hConstraint Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA);
- static hConstraint Constrain(int type, hEntity ptA, hEntity ptB,
- hEntity entityA, hEntity entityB,
- bool other, bool other2);
+ static hConstraint Constrain(Constraint::Type type, hEntity ptA, hEntity ptB, hEntity entityA,
+ hEntity entityB = Entity::NO_ENTITY, bool other = false,
+ bool other2 = false);
+ static hConstraint TryConstrain(Constraint::Type type, hEntity ptA, hEntity ptB,
+ hEntity entityA, hEntity entityB = Entity::NO_ENTITY,
+ bool other = false, bool other2 = false);
};
class hEquation {
public:
uint32_t v;
- inline bool isFromConstraint(void);
- inline hConstraint constraint(void);
+ inline bool isFromConstraint() const;
+ inline hConstraint constraint() const;
};
+template<>
+struct IsHandleOracle<hEquation> : std::true_type {};
+
class Equation {
public:
int tag;
Expr *e;
- void Clear(void) {}
+ void Clear() {}
};
int tag;
hStyle h;
- enum {
- STIPPLE_CONTINUOUS = 0,
- STIPPLE_DASH = 1,
- STIPPLE_LONG_DASH = 2,
- STIPPLE_DASH_DOT = 3,
- STIPPLE_DASH_DOT_DOT = 4,
- STIPPLE_DOT = 5,
- STIPPLE_FREEHAND = 6,
- STIPPLE_ZIGZAG = 7,
-
- LAST_STIPPLE = STIPPLE_ZIGZAG
- };
-
enum {
// If an entity has no style, then it will be colored according to
// whether the group that it's in is active or not, whether it's
std::string name;
- enum {
- UNITS_AS_PIXELS = 0,
- UNITS_AS_MM = 1
+ enum class UnitsAs : uint32_t {
+ PIXELS = 0,
+ MM = 1
};
double width;
- int widthAs;
+ UnitsAs widthAs;
double textHeight;
- int textHeightAs;
- enum {
- ORIGIN_LEFT = 0x01,
- ORIGIN_RIGHT = 0x02,
- ORIGIN_BOT = 0x04,
- ORIGIN_TOP = 0x08
+ UnitsAs textHeightAs;
+ enum class TextOrigin : uint32_t {
+ NONE = 0x00,
+ LEFT = 0x01,
+ RIGHT = 0x02,
+ BOT = 0x04,
+ TOP = 0x08
};
- int textOrigin;
+ TextOrigin textOrigin;
double textAngle;
RgbaColor color;
bool filled;
RgbaColor fillColor;
bool visible;
bool exportable;
- int stippleType;
+ StipplePattern stippleType;
double stippleScale;
int zIndex;
static std::string CnfTextHeight(const std::string &prefix);
static std::string CnfPrefixToName(const std::string &prefix);
- static void CreateAllDefaultStyles(void);
+ static void CreateAllDefaultStyles();
static void CreateDefaultStyle(hStyle h);
static void FillDefaultStyle(Style *s, const Default *d = NULL, bool factory = false);
- static void FreezeDefaultStyles(void);
- static void LoadFactoryDefaults(void);
+ static void FreezeDefaultStyles(Platform::SettingsRef settings);
+ static void LoadFactoryDefaults();
static void AssignSelectionToStyle(uint32_t v);
static uint32_t CreateCustomStyle(bool rememberForUndo = true);
static Style *Get(hStyle hs);
static RgbaColor Color(hStyle hs, bool forExport=false);
- static RgbaColor FillColor(hStyle hs, bool forExport=false);
- static float Width(hStyle hs);
static RgbaColor Color(int hs, bool forExport=false);
- static float Width(int hs);
+ static RgbaColor FillColor(hStyle hs, bool forExport=false);
+ static double Width(hStyle hs);
+ static double Width(int hs);
static double WidthMm(int hs);
static double TextHeight(hStyle hs);
static double DefaultTextHeight();
+ static Canvas::Stroke Stroke(hStyle hs);
+ static Canvas::Stroke Stroke(int hs);
static bool Exportable(int hs);
static hStyle ForEntity(hEntity he);
- static int PatternType(hStyle hs);
+ static StipplePattern PatternType(hStyle hs);
static double StippleScaleMm(hStyle hs);
- std::string DescriptionString(void);
+ std::string DescriptionString() const;
- void Clear(void) {}
+ void Clear() {}
};
-inline hEntity hGroup::entity(int i)
+inline hEntity hGroup::entity(int i) const
{ hEntity r; r.v = 0x80000000 | (v << 16) | (uint32_t)i; return r; }
-inline hParam hGroup::param(int i)
+inline hParam hGroup::param(int i) const
{ hParam r; r.v = 0x80000000 | (v << 16) | (uint32_t)i; return r; }
-inline hEquation hGroup::equation(int i)
+inline hEquation hGroup::equation(int i) const
{ hEquation r; r.v = (v << 16) | 0x80000000 | (uint32_t)i; return r; }
-inline bool hRequest::IsFromReferences(void) {
- if(v == Request::HREQUEST_REFERENCE_XY.v) return true;
- if(v == Request::HREQUEST_REFERENCE_YZ.v) return true;
- if(v == Request::HREQUEST_REFERENCE_ZX.v) return true;
+inline bool hRequest::IsFromReferences() const {
+ if(*this == Request::HREQUEST_REFERENCE_XY) return true;
+ if(*this == Request::HREQUEST_REFERENCE_YZ) return true;
+ if(*this == Request::HREQUEST_REFERENCE_ZX) return true;
return false;
}
-inline hEntity hRequest::entity(int i)
+inline hEntity hRequest::entity(int i) const
{ hEntity r; r.v = (v << 16) | (uint32_t)i; return r; }
-inline hParam hRequest::param(int i)
+inline hParam hRequest::param(int i) const
{ hParam r; r.v = (v << 16) | (uint32_t)i; return r; }
-inline bool hEntity::isFromRequest(void)
+inline bool hEntity::isFromRequest() const
{ if(v & 0x80000000) return false; else return true; }
-inline hRequest hEntity::request(void)
+inline hRequest hEntity::request() const
{ hRequest r; r.v = (v >> 16); return r; }
-inline hGroup hEntity::group(void)
+inline hGroup hEntity::group() const
{ hGroup r; r.v = (v >> 16) & 0x3fff; return r; }
-inline hEquation hEntity::equation(int i)
- { if(i != 0) oops(); hEquation r; r.v = v | 0x40000000; return r; }
+inline hEquation hEntity::equation(int i) const
+ { hEquation r; r.v = v | 0x40000000 | (uint32_t)i; return r; }
-inline hRequest hParam::request(void)
+inline hRequest hParam::request() const
{ hRequest r; r.v = (v >> 16); return r; }
-inline hEquation hConstraint::equation(int i)
+inline hEquation hConstraint::equation(int i) const
{ hEquation r; r.v = (v << 16) | (uint32_t)i; return r; }
+inline hParam hConstraint::param(int i) const
+ { hParam r; r.v = v | 0x40000000 | (uint32_t)i; return r; }
-inline bool hEquation::isFromConstraint(void)
+inline bool hEquation::isFromConstraint() const
{ if(v & 0xc0000000) return false; else return true; }
-inline hConstraint hEquation::constraint(void)
+inline hConstraint hEquation::constraint() const
{ hConstraint r; r.v = (v >> 16); return r; }
// The format for entities stored on the clipboard.
class ClipboardRequest {
public:
- int type;
+ Request::Type type;
int extraPoints;
hStyle style;
std::string str;
std::string font;
+ Platform::Path file;
bool construction;
Vector point[MAX_POINTS_IN_ENTITY];
SolveSpaceUI SolveSpace::SS = {};
Sketch SolveSpace::SK = {};
-std::string SolveSpace::RecentFile[MAX_RECENT] = {};
-
void SolveSpaceUI::Init() {
+#if !defined(HEADLESS)
+ // Check that the resource system works.
+ dbp("%s", LoadString("banner.txt").data());
+#endif
+
+ Platform::SettingsRef settings = Platform::GetSettings();
+
SS.tangentArcRadius = 10.0;
// Then, load the registry settings.
- int i;
// Default list of colors for the model material
- modelColor[0] = CnfThawColor(RGBi(150, 150, 150), "ModelColor_0");
- modelColor[1] = CnfThawColor(RGBi(100, 100, 100), "ModelColor_1");
- modelColor[2] = CnfThawColor(RGBi( 30, 30, 30), "ModelColor_2");
- modelColor[3] = CnfThawColor(RGBi(150, 0, 0), "ModelColor_3");
- modelColor[4] = CnfThawColor(RGBi( 0, 100, 0), "ModelColor_4");
- modelColor[5] = CnfThawColor(RGBi( 0, 80, 80), "ModelColor_5");
- modelColor[6] = CnfThawColor(RGBi( 0, 0, 130), "ModelColor_6");
- modelColor[7] = CnfThawColor(RGBi( 80, 0, 80), "ModelColor_7");
+ modelColor[0] = settings->ThawColor("ModelColor_0", RGBi(150, 150, 150));
+ modelColor[1] = settings->ThawColor("ModelColor_1", RGBi(100, 100, 100));
+ modelColor[2] = settings->ThawColor("ModelColor_2", RGBi( 30, 30, 30));
+ modelColor[3] = settings->ThawColor("ModelColor_3", RGBi(150, 0, 0));
+ modelColor[4] = settings->ThawColor("ModelColor_4", RGBi( 0, 100, 0));
+ modelColor[5] = settings->ThawColor("ModelColor_5", RGBi( 0, 80, 80));
+ modelColor[6] = settings->ThawColor("ModelColor_6", RGBi( 0, 0, 130));
+ modelColor[7] = settings->ThawColor("ModelColor_7", RGBi( 80, 0, 80));
// Light intensities
- lightIntensity[0] = CnfThawFloat(1.0f, "LightIntensity_0");
- lightIntensity[1] = CnfThawFloat(0.5f, "LightIntensity_1");
- ambientIntensity = 0.3; // no setting for that yet
+ lightIntensity[0] = settings->ThawFloat("LightIntensity_0", 1.0);
+ lightIntensity[1] = settings->ThawFloat("LightIntensity_1", 0.5);
+ ambientIntensity = settings->ThawFloat("Light_Ambient", 0.3);
// Light positions
- lightDir[0].x = CnfThawFloat(-1.0f, "LightDir_0_Right" );
- lightDir[0].y = CnfThawFloat( 1.0f, "LightDir_0_Up" );
- lightDir[0].z = CnfThawFloat( 0.0f, "LightDir_0_Forward" );
- lightDir[1].x = CnfThawFloat( 1.0f, "LightDir_1_Right" );
- lightDir[1].y = CnfThawFloat( 0.0f, "LightDir_1_Up" );
- lightDir[1].z = CnfThawFloat( 0.0f, "LightDir_1_Forward" );
+ lightDir[0].x = settings->ThawFloat("LightDir_0_Right", -1.0);
+ lightDir[0].y = settings->ThawFloat("LightDir_0_Up", 1.0);
+ lightDir[0].z = settings->ThawFloat("LightDir_0_Forward", 0.0);
+ lightDir[1].x = settings->ThawFloat("LightDir_1_Right", 1.0);
+ lightDir[1].y = settings->ThawFloat("LightDir_1_Up", 0.0);
+ lightDir[1].z = settings->ThawFloat("LightDir_1_Forward", 0.0);
exportMode = false;
// Chord tolerance
- chordTol = CnfThawFloat(0.5f, "ChordTolerancePct");
+ chordTol = settings->ThawFloat("ChordTolerancePct", 0.1);
// Max pwl segments to generate
- maxSegments = CnfThawInt(10, "MaxSegments");
+ maxSegments = settings->ThawInt("MaxSegments", 20);
// Chord tolerance
- exportChordTol = CnfThawFloat(0.1f, "ExportChordTolerance");
+ exportChordTol = settings->ThawFloat("ExportChordTolerance", 0.1);
// Max pwl segments to generate
- exportMaxSegments = CnfThawInt(64, "ExportMaxSegments");
+ exportMaxSegments = settings->ThawInt("ExportMaxSegments", 64);
+ // Timeout value for finding redundant constrains (ms)
+ timeoutRedundantConstr = settings->ThawInt("TimeoutRedundantConstraints", 1000);
// View units
- viewUnits = (Unit)CnfThawInt((uint32_t)UNIT_MM, "ViewUnits");
+ viewUnits = (Unit)settings->ThawInt("ViewUnits", (uint32_t)Unit::MM);
// Number of digits after the decimal point
- afterDecimalMm = CnfThawInt(2, "AfterDecimalMm");
- afterDecimalInch = CnfThawInt(3, "AfterDecimalInch");
+ afterDecimalMm = settings->ThawInt("AfterDecimalMm", 2);
+ afterDecimalInch = settings->ThawInt("AfterDecimalInch", 3);
+ afterDecimalDegree = settings->ThawInt("AfterDecimalDegree", 2);
+ useSIPrefixes = settings->ThawBool("UseSIPrefixes", false);
// Camera tangent (determines perspective)
- cameraTangent = CnfThawFloat(0.3f/1e3f, "CameraTangent");
+ cameraTangent = settings->ThawFloat("CameraTangent", 0.3f/1e3);
// Grid spacing
- gridSpacing = CnfThawFloat(5.0f, "GridSpacing");
+ gridSpacing = settings->ThawFloat("GridSpacing", 5.0);
// Export scale factor
- exportScale = CnfThawFloat(1.0f, "ExportScale");
+ exportScale = settings->ThawFloat("ExportScale", 1.0);
// Export offset (cutter radius comp)
- exportOffset = CnfThawFloat(0.0f, "ExportOffset");
+ exportOffset = settings->ThawFloat("ExportOffset", 0.0);
// Rewrite exported colors close to white into black (assuming white bg)
- fixExportColors = CnfThawBool(true, "FixExportColors");
+ fixExportColors = settings->ThawBool("FixExportColors", true);
+ // Export background color
+ exportBackgroundColor = settings->ThawBool("ExportBackgroundColor", false);
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
- drawBackFaces = CnfThawBool(true, "DrawBackFaces");
+ drawBackFaces = settings->ThawBool("DrawBackFaces", true);
+ // Use turntable mouse navigation
+ turntableNav = settings->ThawBool("TurntableNav", false);
+ // Immediately edit dimension
+ immediatelyEditDimension = settings->ThawBool("ImmediatelyEditDimension", false);
// Check that contours are closed and not self-intersecting
- checkClosedContour = CnfThawBool(true, "CheckClosedContour");
+ checkClosedContour = settings->ThawBool("CheckClosedContour", true);
+ // Enable automatic constrains for lines
+ automaticLineConstraints = settings->ThawBool("AutomaticLineConstraints", true);
+ // Draw closed polygons areas
+ showContourAreas = settings->ThawBool("ShowContourAreas", false);
// Export shaded triangles in a 2d view
- exportShadedTriangles = CnfThawBool(true, "ExportShadedTriangles");
+ exportShadedTriangles = settings->ThawBool("ExportShadedTriangles", true);
// Export pwl curves (instead of exact) always
- exportPwlCurves = CnfThawBool(false, "ExportPwlCurves");
+ exportPwlCurves = settings->ThawBool("ExportPwlCurves", false);
// Background color on-screen
- backgroundColor = CnfThawColor(RGBi(0, 0, 0), "BackgroundColor");
+ backgroundColor = settings->ThawColor("BackgroundColor", RGBi(0, 0, 0));
// Whether export canvas size is fixed or derived from bbox
- exportCanvasSizeAuto = CnfThawBool(true, "ExportCanvasSizeAuto");
+ exportCanvasSizeAuto = settings->ThawBool("ExportCanvasSizeAuto", true);
// Margins for automatic canvas size
- exportMargin.left = CnfThawFloat(5.0f, "ExportMargin_Left");
- exportMargin.right = CnfThawFloat(5.0f, "ExportMargin_Right");
- exportMargin.bottom = CnfThawFloat(5.0f, "ExportMargin_Bottom");
- exportMargin.top = CnfThawFloat(5.0f, "ExportMargin_Top");
+ exportMargin.left = settings->ThawFloat("ExportMargin_Left", 5.0);
+ exportMargin.right = settings->ThawFloat("ExportMargin_Right", 5.0);
+ exportMargin.bottom = settings->ThawFloat("ExportMargin_Bottom", 5.0);
+ exportMargin.top = settings->ThawFloat("ExportMargin_Top", 5.0);
// Dimensions for fixed canvas size
- exportCanvas.width = CnfThawFloat(100.0f, "ExportCanvas_Width");
- exportCanvas.height = CnfThawFloat(100.0f, "ExportCanvas_Height");
- exportCanvas.dx = CnfThawFloat( 5.0f, "ExportCanvas_Dx");
- exportCanvas.dy = CnfThawFloat( 5.0f, "ExportCanvas_Dy");
+ exportCanvas.width = settings->ThawFloat("ExportCanvas_Width", 100.0);
+ exportCanvas.height = settings->ThawFloat("ExportCanvas_Height", 100.0);
+ exportCanvas.dx = settings->ThawFloat("ExportCanvas_Dx", 5.0);
+ exportCanvas.dy = settings->ThawFloat("ExportCanvas_Dy", 5.0);
// Extra parameters when exporting G code
- gCode.depth = CnfThawFloat(10.0f, "GCode_Depth");
- gCode.passes = CnfThawInt(1, "GCode_Passes");
- gCode.feed = CnfThawFloat(10.0f, "GCode_Feed");
- gCode.plungeFeed = CnfThawFloat(10.0f, "GCode_PlungeFeed");
+ gCode.depth = settings->ThawFloat("GCode_Depth", 10.0);
+ gCode.passes = settings->ThawInt("GCode_Passes", 1);
+ gCode.feed = settings->ThawFloat("GCode_Feed", 10.0);
+ gCode.plungeFeed = settings->ThawFloat("GCode_PlungeFeed", 10.0);
// Show toolbar in the graphics window
- showToolbar = CnfThawBool(true, "ShowToolbar");
+ showToolbar = settings->ThawBool("ShowToolbar", true);
// Recent files menus
- for(i = 0; i < MAX_RECENT; i++) {
- RecentFile[i] = CnfThawString("", "RecentFile_" + std::to_string(i));
+ for(size_t i = 0; i < MAX_RECENT; i++) {
+ std::string rawPath = settings->ThawString("RecentFile_" + std::to_string(i), "");
+ if(rawPath.empty()) continue;
+ recentFiles.push_back(Platform::Path::From(rawPath));
}
- RefreshRecentMenus();
// Autosave timer
- autosaveInterval = CnfThawInt(5, "AutosaveInterval");
+ autosaveInterval = settings->ThawInt("AutosaveInterval", 5);
+ // Locale
+ std::string locale = settings->ThawString("Locale", "");
+ if(!locale.empty()) {
+ SetLocale(locale);
+ }
+
+ generateAllTimer = Platform::CreateTimer();
+ generateAllTimer->onTimeout = std::bind(&SolveSpaceUI::GenerateAll, &SS, Generate::DIRTY,
+ /*andFindFree=*/false, /*genForBBox=*/false);
+
+ showTWTimer = Platform::CreateTimer();
+ showTWTimer->onTimeout = std::bind(&TextWindow::Show, &TW);
+
+ autosaveTimer = Platform::CreateTimer();
+ autosaveTimer->onTimeout = std::bind(&SolveSpaceUI::Autosave, &SS);
// The default styles (colors, line widths, etc.) are also stored in the
// configuration file, but we will automatically load those as we need
// them.
- SetAutosaveTimerFor(autosaveInterval);
+ ScheduleAutosave();
NewFile();
AfterNewFile();
+
+ if(TW.window && GW.window) {
+ TW.window->ThawPosition(settings, "TextWindow");
+ GW.window->ThawPosition(settings, "GraphicsWindow");
+ TW.window->SetVisible(true);
+ GW.window->SetVisible(true);
+ GW.window->Focus();
+
+ // Do this once the window is created.
+ Request3DConnexionEventsForWindow(GW.window);
+ }
}
-bool SolveSpaceUI::LoadAutosaveFor(const std::string &filename) {
- std::string autosaveFile = filename + AUTOSAVE_SUFFIX;
+bool SolveSpaceUI::LoadAutosaveFor(const Platform::Path &filename) {
+ Platform::Path autosaveFile = filename.WithExtension(BACKUP_EXT);
- FILE *f = ssfopen(autosaveFile, "rb");
+ FILE *f = OpenFile(autosaveFile, "rb");
if(!f)
return false;
fclose(f);
- if(LoadAutosaveYesNo() == DIALOG_YES) {
+ Platform::MessageDialogRef dialog = CreateMessageDialog(GW.window);
+
+ using Platform::MessageDialog;
+ dialog->SetType(MessageDialog::Type::QUESTION);
+ dialog->SetTitle(C_("title", "Autosave Available"));
+ dialog->SetMessage(C_("dialog", "An autosave file is available for this sketch."));
+ dialog->SetDescription(C_("dialog", "Do you want to load the autosave file instead?"));
+ dialog->AddButton(C_("button", "&Load autosave"), MessageDialog::Response::YES,
+ /*isDefault=*/true);
+ dialog->AddButton(C_("button", "Do&n't Load"), MessageDialog::Response::NO);
+
+ // FIXME(async): asyncify this call
+ if(dialog->RunModal() == MessageDialog::Response::YES) {
unsaved = true;
- return LoadFromFile(autosaveFile);
+ return LoadFromFile(autosaveFile, /*canCancel=*/true);
}
return false;
}
-bool SolveSpaceUI::OpenFile(const std::string &filename) {
+bool SolveSpaceUI::Load(const Platform::Path &filename) {
bool autosaveLoaded = LoadAutosaveFor(filename);
- bool fileLoaded = autosaveLoaded || LoadFromFile(filename);
- if(fileLoaded)
+ bool fileLoaded = autosaveLoaded || LoadFromFile(filename, /*canCancel=*/true);
+ if(fileLoaded) {
saveFile = filename;
- bool success = fileLoaded && ReloadAllImported(/*canCancel=*/true);
- if(success) {
AddToRecentList(filename);
} else {
- saveFile = "";
+ saveFile.Clear();
NewFile();
}
AfterNewFile();
unsaved = autosaveLoaded;
- return success;
+ return fileLoaded;
}
-void SolveSpaceUI::Exit(void) {
+void SolveSpaceUI::Exit() {
+ Platform::SettingsRef settings = Platform::GetSettings();
+
+ GW.window->FreezePosition(settings, "GraphicsWindow");
+ TW.window->FreezePosition(settings, "TextWindow");
+
// Recent files
- for(int i = 0; i < MAX_RECENT; i++)
- CnfFreezeString(RecentFile[i], "RecentFile_" + std::to_string(i));
+ for(size_t i = 0; i < MAX_RECENT; i++) {
+ std::string rawPath;
+ if(recentFiles.size() > i) {
+ rawPath = recentFiles[i].raw;
+ }
+ settings->FreezeString("RecentFile_" + std::to_string(i), rawPath);
+ }
// Model colors
- for(int i = 0; i < MODEL_COLORS; i++)
- CnfFreezeColor(modelColor[i], "ModelColor_" + std::to_string(i));
+ for(size_t i = 0; i < MODEL_COLORS; i++)
+ settings->FreezeColor("ModelColor_" + std::to_string(i), modelColor[i]);
// Light intensities
- CnfFreezeFloat((float)lightIntensity[0], "LightIntensity_0");
- CnfFreezeFloat((float)lightIntensity[1], "LightIntensity_1");
+ settings->FreezeFloat("LightIntensity_0", (float)lightIntensity[0]);
+ settings->FreezeFloat("LightIntensity_1", (float)lightIntensity[1]);
+ settings->FreezeFloat("Light_Ambient", (float)ambientIntensity);
// Light directions
- CnfFreezeFloat((float)lightDir[0].x, "LightDir_0_Right");
- CnfFreezeFloat((float)lightDir[0].y, "LightDir_0_Up");
- CnfFreezeFloat((float)lightDir[0].z, "LightDir_0_Forward");
- CnfFreezeFloat((float)lightDir[1].x, "LightDir_1_Right");
- CnfFreezeFloat((float)lightDir[1].y, "LightDir_1_Up");
- CnfFreezeFloat((float)lightDir[1].z, "LightDir_1_Forward");
+ settings->FreezeFloat("LightDir_0_Right", (float)lightDir[0].x);
+ settings->FreezeFloat("LightDir_0_Up", (float)lightDir[0].y);
+ settings->FreezeFloat("LightDir_0_Forward", (float)lightDir[0].z);
+ settings->FreezeFloat("LightDir_1_Right", (float)lightDir[1].x);
+ settings->FreezeFloat("LightDir_1_Up", (float)lightDir[1].y);
+ settings->FreezeFloat("LightDir_1_Forward", (float)lightDir[1].z);
// Chord tolerance
- CnfFreezeFloat((float)chordTol, "ChordTolerancePct");
+ settings->FreezeFloat("ChordTolerancePct", (float)chordTol);
// Max pwl segments to generate
- CnfFreezeInt((uint32_t)maxSegments, "MaxSegments");
+ settings->FreezeInt("MaxSegments", (uint32_t)maxSegments);
// Export Chord tolerance
- CnfFreezeFloat((float)exportChordTol, "ExportChordTolerance");
+ settings->FreezeFloat("ExportChordTolerance", (float)exportChordTol);
// Export Max pwl segments to generate
- CnfFreezeInt((uint32_t)exportMaxSegments, "ExportMaxSegments");
+ settings->FreezeInt("ExportMaxSegments", (uint32_t)exportMaxSegments);
+ // Timeout for finding which constraints to fix Jacobian
+ settings->FreezeInt("TimeoutRedundantConstraints", (uint32_t)timeoutRedundantConstr);
// View units
- CnfFreezeInt((uint32_t)viewUnits, "ViewUnits");
+ settings->FreezeInt("ViewUnits", (uint32_t)viewUnits);
// Number of digits after the decimal point
- CnfFreezeInt((uint32_t)afterDecimalMm, "AfterDecimalMm");
- CnfFreezeInt((uint32_t)afterDecimalInch, "AfterDecimalInch");
+ settings->FreezeInt("AfterDecimalMm", (uint32_t)afterDecimalMm);
+ settings->FreezeInt("AfterDecimalInch", (uint32_t)afterDecimalInch);
+ settings->FreezeInt("AfterDecimalDegree", (uint32_t)afterDecimalDegree);
+ settings->FreezeBool("UseSIPrefixes", useSIPrefixes);
// Camera tangent (determines perspective)
- CnfFreezeFloat((float)cameraTangent, "CameraTangent");
+ settings->FreezeFloat("CameraTangent", (float)cameraTangent);
// Grid spacing
- CnfFreezeFloat(gridSpacing, "GridSpacing");
+ settings->FreezeFloat("GridSpacing", gridSpacing);
// Export scale
- CnfFreezeFloat(exportScale, "ExportScale");
+ settings->FreezeFloat("ExportScale", exportScale);
// Export offset (cutter radius comp)
- CnfFreezeFloat(exportOffset, "ExportOffset");
+ settings->FreezeFloat("ExportOffset", exportOffset);
// Rewrite exported colors close to white into black (assuming white bg)
- CnfFreezeBool(fixExportColors, "FixExportColors");
+ settings->FreezeBool("FixExportColors", fixExportColors);
+ // Export background color
+ settings->FreezeBool("ExportBackgroundColor", exportBackgroundColor);
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
- CnfFreezeBool(drawBackFaces, "DrawBackFaces");
+ settings->FreezeBool("DrawBackFaces", drawBackFaces);
+ // Draw closed polygons areas
+ settings->FreezeBool("ShowContourAreas", showContourAreas);
// Check that contours are closed and not self-intersecting
- CnfFreezeBool(checkClosedContour, "CheckClosedContour");
+ settings->FreezeBool("CheckClosedContour", checkClosedContour);
+ // Use turntable mouse navigation
+ settings->FreezeBool("TurntableNav", turntableNav);
+ // Immediately edit dimensions
+ settings->FreezeBool("ImmediatelyEditDimension", immediatelyEditDimension);
+ // Enable automatic constrains for lines
+ settings->FreezeBool("AutomaticLineConstraints", automaticLineConstraints);
// Export shaded triangles in a 2d view
- CnfFreezeBool(exportShadedTriangles, "ExportShadedTriangles");
+ settings->FreezeBool("ExportShadedTriangles", exportShadedTriangles);
// Export pwl curves (instead of exact) always
- CnfFreezeBool(exportPwlCurves, "ExportPwlCurves");
+ settings->FreezeBool("ExportPwlCurves", exportPwlCurves);
// Background color on-screen
- CnfFreezeColor(backgroundColor, "BackgroundColor");
+ settings->FreezeColor("BackgroundColor", backgroundColor);
// Whether export canvas size is fixed or derived from bbox
- CnfFreezeBool(exportCanvasSizeAuto, "ExportCanvasSizeAuto");
+ settings->FreezeBool("ExportCanvasSizeAuto", exportCanvasSizeAuto);
// Margins for automatic canvas size
- CnfFreezeFloat(exportMargin.left, "ExportMargin_Left");
- CnfFreezeFloat(exportMargin.right, "ExportMargin_Right");
- CnfFreezeFloat(exportMargin.bottom, "ExportMargin_Bottom");
- CnfFreezeFloat(exportMargin.top, "ExportMargin_Top");
+ settings->FreezeFloat("ExportMargin_Left", exportMargin.left);
+ settings->FreezeFloat("ExportMargin_Right", exportMargin.right);
+ settings->FreezeFloat("ExportMargin_Bottom", exportMargin.bottom);
+ settings->FreezeFloat("ExportMargin_Top", exportMargin.top);
// Dimensions for fixed canvas size
- CnfFreezeFloat(exportCanvas.width, "ExportCanvas_Width");
- CnfFreezeFloat(exportCanvas.height, "ExportCanvas_Height");
- CnfFreezeFloat(exportCanvas.dx, "ExportCanvas_Dx");
- CnfFreezeFloat(exportCanvas.dy, "ExportCanvas_Dy");
+ settings->FreezeFloat("ExportCanvas_Width", exportCanvas.width);
+ settings->FreezeFloat("ExportCanvas_Height", exportCanvas.height);
+ settings->FreezeFloat("ExportCanvas_Dx", exportCanvas.dx);
+ settings->FreezeFloat("ExportCanvas_Dy", exportCanvas.dy);
// Extra parameters when exporting G code
- CnfFreezeFloat(gCode.depth, "GCode_Depth");
- CnfFreezeInt(gCode.passes, "GCode_Passes");
- CnfFreezeFloat(gCode.feed, "GCode_Feed");
- CnfFreezeFloat(gCode.plungeFeed, "GCode_PlungeFeed");
+ settings->FreezeFloat("GCode_Depth", gCode.depth);
+ settings->FreezeInt("GCode_Passes", gCode.passes);
+ settings->FreezeFloat("GCode_Feed", gCode.feed);
+ settings->FreezeFloat("GCode_PlungeFeed", gCode.plungeFeed);
// Show toolbar in the graphics window
- CnfFreezeBool(showToolbar, "ShowToolbar");
+ settings->FreezeBool("ShowToolbar", showToolbar);
// Autosave timer
- CnfFreezeInt(autosaveInterval, "AutosaveInterval");
+ settings->FreezeInt("AutosaveInterval", autosaveInterval);
// And the default styles, colors and line widths and such.
- Style::FreezeDefaultStyles();
+ Style::FreezeDefaultStyles(settings);
- ExitNow();
+ Platform::ExitGui();
}
void SolveSpaceUI::ScheduleGenerateAll() {
- if(!later.scheduled) ScheduleLater();
- later.scheduled = true;
- later.generateAll = true;
+ generateAllTimer->RunAfterProcessingEvents();
}
void SolveSpaceUI::ScheduleShowTW() {
- if(!later.scheduled) ScheduleLater();
- later.scheduled = true;
- later.showTW = true;
+ showTWTimer->RunAfterProcessingEvents();
}
-void SolveSpaceUI::DoLater(void) {
- if(later.generateAll) GenerateAll();
- if(later.showTW) TW.Show();
- later = {};
+void SolveSpaceUI::ScheduleAutosave() {
+ autosaveTimer->RunAfter(autosaveInterval * 60 * 1000);
}
-double SolveSpaceUI::MmPerUnit(void) {
- if(viewUnits == UNIT_INCHES) {
- return 25.4;
- } else {
- return 1.0;
+double SolveSpaceUI::MmPerUnit() {
+ switch(viewUnits) {
+ case Unit::INCHES: return 25.4;
+ case Unit::METERS: return 1000.0;
+ case Unit::MM: return 1.0;
}
+ return 1.0;
}
-const char *SolveSpaceUI::UnitName(void) {
- if(viewUnits == UNIT_INCHES) {
- return "inch";
- } else {
- return "mm";
+const char *SolveSpaceUI::UnitName() {
+ switch(viewUnits) {
+ case Unit::INCHES: return "in";
+ case Unit::METERS: return "m";
+ case Unit::MM: return "mm";
}
+ return "";
}
+
std::string SolveSpaceUI::MmToString(double v) {
- if(viewUnits == UNIT_INCHES) {
- return ssprintf("%.*f", afterDecimalInch, v/25.4);
+ v /= MmPerUnit();
+ return ssprintf("%.*f", UnitDigitsAfterDecimal(), v);
+}
+static const char *DimToString(int dim) {
+ switch(dim) {
+ case 3: return "³";
+ case 2: return "²";
+ case 1: return "";
+ default: ssassert(false, "Unexpected dimension");
+ }
+}
+static std::pair<int, std::string> SelectSIPrefixMm(int deg) {
+ if(deg >= 3) return { 3, "km" };
+ else if(deg >= 0) return { 0, "m" };
+ else if(deg >= -2) return { -2, "cm" };
+ else if(deg >= -3) return { -3, "mm" };
+ else if(deg >= -6) return { -6, "µm" };
+ else return { -9, "nm" };
+}
+static std::pair<int, std::string> SelectSIPrefixInch(int deg) {
+ if(deg >= 0) return { 0, "in" };
+ else if(deg >= -3) return { -3, "mil" };
+ else return { -6, "µin" };
+}
+std::string SolveSpaceUI::MmToStringSI(double v, int dim) {
+ bool compact = false;
+ if(dim == 0) {
+ if(!useSIPrefixes) return MmToString(v);
+ compact = true;
+ dim = 1;
+ }
+
+ v /= pow((viewUnits == Unit::INCHES) ? 25.4 : 1000, dim);
+ int vdeg = (int)((log10(fabs(v))) / dim);
+ std::string unit;
+ if(fabs(v) > 0.0) {
+ int sdeg = 0;
+ std::tie(sdeg, unit) =
+ (viewUnits == Unit::INCHES)
+ ? SelectSIPrefixInch(vdeg)
+ : SelectSIPrefixMm(vdeg);
+ v /= pow(10.0, sdeg * dim);
+ }
+ int pdeg = (int)ceil(log10(fabs(v) + 1e-10));
+ return ssprintf("%.*g%s%s%s", pdeg + UnitDigitsAfterDecimal(), v,
+ compact ? "" : " ", unit.c_str(), DimToString(dim));
+}
+std::string SolveSpaceUI::DegreeToString(double v) {
+ if(fabs(v - floor(v)) > 1e-10) {
+ return ssprintf("%.*f", afterDecimalDegree, v);
} else {
- return ssprintf("%.*f", afterDecimalMm, v);
+ return ssprintf("%.0f", v);
}
}
double SolveSpaceUI::ExprToMm(Expr *e) {
double SolveSpaceUI::StringToMm(const std::string &str) {
return std::stod(str) * MmPerUnit();
}
-double SolveSpaceUI::ChordTolMm(void) {
+double SolveSpaceUI::ChordTolMm() {
if(exportMode) return ExportChordTolMm();
return chordTolCalculated;
}
-double SolveSpaceUI::ExportChordTolMm(void) {
+double SolveSpaceUI::ExportChordTolMm() {
return exportChordTol / exportScale;
}
-int SolveSpaceUI::GetMaxSegments(void) {
+int SolveSpaceUI::GetMaxSegments() {
if(exportMode) return exportMaxSegments;
return maxSegments;
}
-int SolveSpaceUI::UnitDigitsAfterDecimal(void) {
- return (viewUnits == UNIT_INCHES) ? afterDecimalInch : afterDecimalMm;
+int SolveSpaceUI::UnitDigitsAfterDecimal() {
+ return (viewUnits == Unit::INCHES) ? afterDecimalInch : afterDecimalMm;
}
void SolveSpaceUI::SetUnitDigitsAfterDecimal(int v) {
- if(viewUnits == UNIT_INCHES) {
+ if(viewUnits == Unit::INCHES) {
afterDecimalInch = v;
} else {
afterDecimalMm = v;
}
}
-double SolveSpaceUI::CameraTangent(void) {
+double SolveSpaceUI::CameraTangent() {
if(!usePerspectiveProj) {
return 0;
} else {
}
}
-void SolveSpaceUI::AfterNewFile(void) {
+void SolveSpaceUI::AfterNewFile() {
// Clear out the traced point, which is no longer valid
traced.point = Entity::NO_ENTITY;
traced.path.l.Clear();
// Quit export mode
justExportedInfo.draw = false;
+ centerOfMass.draw = false;
exportMode = false;
// GenerateAll() expects the view to be valid, because it uses that to
SS.GW.projRight = Vector::From(1, 0, 0);
SS.GW.projUp = Vector::From(0, 1, 0);
- GenerateAll(GENERATE_REGEN);
+ GenerateAll(Generate::ALL);
- TW.Init();
GW.Init();
+ TW.Init();
unsaved = false;
- int w, h;
- GetGraphicsWindowSize(&w, &h);
- GW.width = w;
- GW.height = h;
-
- // The triangles haven't been generated yet, but zoom to fit the entities
- // roughly in the window, since that sets the mesh tolerance. Consider
- // invisible entities, so we still get something reasonable if the only
- // thing visible is the not-yet-generated surfaces.
- GW.ZoomToFit(true);
-
- GenerateAll(GENERATE_ALL);
- SS.ScheduleShowTW();
- // Then zoom to fit again, to fit the triangles
- GW.ZoomToFit(false);
+ GW.ZoomToFit();
// Create all the default styles; they'll get created on the fly anyways,
// but can't hurt to do it now.
Style::CreateAllDefaultStyles();
- UpdateWindowTitle();
+ UpdateWindowTitles();
}
-void SolveSpaceUI::RemoveFromRecentList(const std::string &filename) {
- int src, dest;
- dest = 0;
- for(src = 0; src < MAX_RECENT; src++) {
- if(filename != RecentFile[src]) {
- if(src != dest) RecentFile[dest] = RecentFile[src];
- dest++;
- }
+void SolveSpaceUI::AddToRecentList(const Platform::Path &filename) {
+ auto it = std::find_if(recentFiles.begin(), recentFiles.end(),
+ [&](const Platform::Path &p) { return p.Equals(filename); });
+ if(it != recentFiles.end()) {
+ recentFiles.erase(it);
}
- while(dest < MAX_RECENT) RecentFile[dest++].clear();
- RefreshRecentMenus();
-}
-void SolveSpaceUI::AddToRecentList(const std::string &filename) {
- RemoveFromRecentList(filename);
- int src;
- for(src = MAX_RECENT - 2; src >= 0; src--) {
- RecentFile[src+1] = RecentFile[src];
+ if(recentFiles.size() > MAX_RECENT) {
+ recentFiles.erase(recentFiles.begin() + MAX_RECENT);
}
- RecentFile[0] = filename;
- RefreshRecentMenus();
+
+ recentFiles.insert(recentFiles.begin(), filename);
+ GW.PopulateRecentFiles();
}
bool SolveSpaceUI::GetFilenameAndSave(bool saveAs) {
- std::string prevSaveFile = saveFile;
-
- if(saveAs || saveFile.empty()) {
- if(!GetSaveFile(&saveFile, "", SlvsFileFilter)) return false;
- // need to get new filename directly into saveFile, since that
- // determines linkFileRel path
+ Platform::SettingsRef settings = Platform::GetSettings();
+ Platform::Path newSaveFile = saveFile;
+
+ if(saveAs || saveFile.IsEmpty()) {
+ Platform::FileDialogRef dialog = Platform::CreateSaveFileDialog(GW.window);
+ dialog->AddFilter(C_("file-type", "SolveSpace models"), { SKETCH_EXT });
+ dialog->ThawChoices(settings, "Sketch");
+ if(!newSaveFile.IsEmpty()) {
+ dialog->SetFilename(newSaveFile);
+ }
+ if(dialog->RunModal()) {
+ dialog->FreezeChoices(settings, "Sketch");
+ newSaveFile = dialog->GetFilename();
+ } else {
+ return false;
+ }
}
- if(SaveToFile(saveFile)) {
- AddToRecentList(saveFile);
+ if(SaveToFile(newSaveFile)) {
+ AddToRecentList(newSaveFile);
RemoveAutosave();
+ saveFile = newSaveFile;
unsaved = false;
return true;
} else {
- // don't store an invalid save filename
- saveFile = prevSaveFile;
return false;
}
}
-bool SolveSpaceUI::Autosave()
+void SolveSpaceUI::Autosave()
{
- SetAutosaveTimerFor(autosaveInterval);
-
- if(!saveFile.empty() && unsaved)
- return SaveToFile(saveFile + AUTOSAVE_SUFFIX);
+ ScheduleAutosave();
- return false;
+ if(!saveFile.IsEmpty() && unsaved) {
+ SaveToFile(saveFile.WithExtension(BACKUP_EXT));
+ }
}
void SolveSpaceUI::RemoveAutosave()
{
- std::string autosaveFile = saveFile + AUTOSAVE_SUFFIX;
- ssremove(autosaveFile);
+ Platform::Path autosaveFile = saveFile.WithExtension(BACKUP_EXT);
+ RemoveFile(autosaveFile);
}
-bool SolveSpaceUI::OkayToStartNewFile(void) {
+bool SolveSpaceUI::OkayToStartNewFile() {
if(!unsaved) return true;
- switch(SaveFileYesNoCancel()) {
- case DIALOG_YES:
- return GetFilenameAndSave(false);
+ Platform::MessageDialogRef dialog = CreateMessageDialog(GW.window);
- case DIALOG_NO:
+ using Platform::MessageDialog;
+ dialog->SetType(MessageDialog::Type::QUESTION);
+ dialog->SetTitle(C_("title", "Modified File"));
+ if(!SolveSpace::SS.saveFile.IsEmpty()) {
+ dialog->SetMessage(ssprintf(C_("dialog", "Do you want to save the changes you made to "
+ "the sketch “%s”?"), saveFile.raw.c_str()));
+ } else {
+ dialog->SetMessage(C_("dialog", "Do you want to save the changes you made to "
+ "the new sketch?"));
+ }
+ dialog->SetDescription(C_("dialog", "Your changes will be lost if you don't save them."));
+ dialog->AddButton(C_("button", "&Save"), MessageDialog::Response::YES,
+ /*isDefault=*/true);
+ dialog->AddButton(C_("button", "Do&n't Save"), MessageDialog::Response::NO);
+ dialog->AddButton(C_("button", "&Cancel"), MessageDialog::Response::CANCEL);
+
+ // FIXME(async): asyncify this call
+ switch(dialog->RunModal()) {
+ case MessageDialog::Response::YES:
+ return GetFilenameAndSave(/*saveAs=*/false);
+
+ case MessageDialog::Response::NO:
RemoveAutosave();
return true;
- case DIALOG_CANCEL:
+ default:
return false;
-
- default: oops(); break;
}
}
-void SolveSpaceUI::UpdateWindowTitle(void) {
- SetCurrentFilename(saveFile);
-}
+void SolveSpaceUI::UpdateWindowTitles() {
+ if(!GW.window || !TW.window) return;
-static std::string Extension(const std::string &filename) {
- int dot = filename.rfind('.');
- if(dot >= 0) {
- std::string ext = filename.substr(dot + 1, filename.length());
- std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
- return ext;
+ if(saveFile.IsEmpty()) {
+ GW.window->SetTitle(C_("title", "(new sketch)"));
+ } else {
+ if(!GW.window->SetTitleForFilename(saveFile)) {
+ GW.window->SetTitle(saveFile.raw);
+ }
}
- return "";
+
+ TW.window->SetTitle(C_("title", "Property Browser"));
}
-void SolveSpaceUI::MenuFile(int id) {
- if(id >= RECENT_OPEN && id < (RECENT_OPEN+MAX_RECENT)) {
- if(!SS.OkayToStartNewFile()) return;
-
- std::string newFile = RecentFile[id - RECENT_OPEN];
- SS.OpenFile(newFile);
- return;
- }
+void SolveSpaceUI::MenuFile(Command id) {
+ Platform::SettingsRef settings = Platform::GetSettings();
switch(id) {
- case GraphicsWindow::MNU_NEW:
+ case Command::NEW:
if(!SS.OkayToStartNewFile()) break;
- SS.saveFile = "";
+ SS.saveFile.Clear();
SS.NewFile();
SS.AfterNewFile();
break;
- case GraphicsWindow::MNU_OPEN: {
+ case Command::OPEN: {
if(!SS.OkayToStartNewFile()) break;
- std::string newFile;
- if(GetOpenFile(&newFile, "", SlvsFileFilter)) {
- SS.OpenFile(newFile);
+ Platform::FileDialogRef dialog = Platform::CreateOpenFileDialog(SS.GW.window);
+ dialog->AddFilters(Platform::SolveSpaceModelFileFilters);
+ dialog->ThawChoices(settings, "Sketch");
+ if(dialog->RunModal()) {
+ dialog->FreezeChoices(settings, "Sketch");
+ SS.Load(dialog->GetFilename());
}
break;
}
- case GraphicsWindow::MNU_SAVE:
- SS.GetFilenameAndSave(false);
+ case Command::SAVE:
+ SS.GetFilenameAndSave(/*saveAs=*/false);
break;
- case GraphicsWindow::MNU_SAVE_AS:
- SS.GetFilenameAndSave(true);
+ case Command::SAVE_AS:
+ SS.GetFilenameAndSave(/*saveAs=*/true);
break;
- case GraphicsWindow::MNU_EXPORT_PNG: {
- std::string exportFile;
- if(!GetSaveFile(&exportFile, "", PngFileFilter)) break;
- SS.ExportAsPngTo(exportFile);
+ case Command::EXPORT_IMAGE: {
+ Platform::FileDialogRef dialog = Platform::CreateSaveFileDialog(SS.GW.window);
+ dialog->AddFilters(Platform::RasterFileFilters);
+ dialog->ThawChoices(settings, "ExportImage");
+ if(dialog->RunModal()) {
+ dialog->FreezeChoices(settings, "ExportImage");
+ SS.ExportAsPngTo(dialog->GetFilename());
+ }
break;
}
- case GraphicsWindow::MNU_EXPORT_VIEW: {
- std::string exportFile;
- if(!GetSaveFile(&exportFile, CnfThawString("", "ViewExportFormat"),
- VectorFileFilter)) break;
- CnfFreezeString(Extension(exportFile), "ViewExportFormat");
+ case Command::EXPORT_VIEW: {
+ Platform::FileDialogRef dialog = Platform::CreateSaveFileDialog(SS.GW.window);
+ dialog->AddFilters(Platform::VectorFileFilters);
+ dialog->ThawChoices(settings, "ExportView");
+ if(!dialog->RunModal()) break;
+ dialog->FreezeChoices(settings, "ExportView");
// If the user is exporting something where it would be
// inappropriate to include the constraints, then warn.
if(SS.GW.showConstraints &&
- (FilenameHasExtension(exportFile, ".txt") ||
+ (dialog->GetFilename().HasExtension("txt") ||
fabs(SS.exportOffset) > LENGTH_EPS))
{
- Message("Constraints are currently shown, and will be exported "
- "in the toolpath. This is probably not what you want; "
- "hide them by clicking the link at the top of the "
- "text window.");
+ Message(_("Constraints are currently shown, and will be exported "
+ "in the toolpath. This is probably not what you want; "
+ "hide them by clicking the link at the top of the "
+ "text window."));
}
- SS.ExportViewOrWireframeTo(exportFile, false);
+ SS.ExportViewOrWireframeTo(dialog->GetFilename(), /*exportWireframe=*/false);
break;
}
- case GraphicsWindow::MNU_EXPORT_WIREFRAME: {
- std::string exportFile;
- if(!GetSaveFile(&exportFile, CnfThawString("", "WireframeExportFormat"),
- Vector3dFileFilter)) break;
- CnfFreezeString(Extension(exportFile), "WireframeExportFormat");
+ case Command::EXPORT_WIREFRAME: {
+ Platform::FileDialogRef dialog = Platform::CreateSaveFileDialog(SS.GW.window);
+ dialog->AddFilters(Platform::Vector3dFileFilters);
+ dialog->ThawChoices(settings, "ExportWireframe");
+ if(!dialog->RunModal()) break;
+ dialog->FreezeChoices(settings, "ExportWireframe");
- SS.ExportViewOrWireframeTo(exportFile, true);
+ SS.ExportViewOrWireframeTo(dialog->GetFilename(), /*exportWireframe*/true);
break;
}
- case GraphicsWindow::MNU_EXPORT_SECTION: {
- std::string exportFile;
- if(!GetSaveFile(&exportFile, CnfThawString("", "SectionExportFormat"),
- VectorFileFilter)) break;
- CnfFreezeString(Extension(exportFile), "SectionExportFormat");
+ case Command::EXPORT_SECTION: {
+ Platform::FileDialogRef dialog = Platform::CreateSaveFileDialog(SS.GW.window);
+ dialog->AddFilters(Platform::VectorFileFilters);
+ dialog->ThawChoices(settings, "ExportSection");
+ if(!dialog->RunModal()) break;
+ dialog->FreezeChoices(settings, "ExportSection");
- SS.ExportSectionTo(exportFile);
+ SS.ExportSectionTo(dialog->GetFilename());
break;
}
- case GraphicsWindow::MNU_EXPORT_MESH: {
- std::string exportFile;
- if(!GetSaveFile(&exportFile, CnfThawString("", "MeshExportFormat"),
- MeshFileFilter)) break;
- CnfFreezeString(Extension(exportFile), "MeshExportFormat");
+ case Command::EXPORT_MESH: {
+ Platform::FileDialogRef dialog = Platform::CreateSaveFileDialog(SS.GW.window);
+ dialog->AddFilters(Platform::MeshFileFilters);
+ dialog->ThawChoices(settings, "ExportMesh");
+ if(!dialog->RunModal()) break;
+ dialog->FreezeChoices(settings, "ExportMesh");
- SS.ExportMeshTo(exportFile);
+ SS.ExportMeshTo(dialog->GetFilename());
break;
}
- case GraphicsWindow::MNU_EXPORT_SURFACES: {
- std::string exportFile;
- if(!GetSaveFile(&exportFile, CnfThawString("", "SurfacesExportFormat"),
- SurfaceFileFilter)) break;
- CnfFreezeString(Extension(exportFile), "SurfacesExportFormat");
+ case Command::EXPORT_SURFACES: {
+ Platform::FileDialogRef dialog = Platform::CreateSaveFileDialog(SS.GW.window);
+ dialog->AddFilters(Platform::SurfaceFileFilters);
+ dialog->ThawChoices(settings, "ExportSurfaces");
+ if(!dialog->RunModal()) break;
+ dialog->FreezeChoices(settings, "ExportSurfaces");
StepFileWriter sfw = {};
- sfw.ExportSurfacesTo(exportFile);
+ sfw.ExportSurfacesTo(dialog->GetFilename());
break;
}
- case GraphicsWindow::MNU_IMPORT: {
- std::string importFile;
- if(!GetOpenFile(&importFile, CnfThawString("", "ImportFormat"),
- ImportableFileFilter)) break;
- CnfFreezeString(Extension(importFile), "ImportFormat");
+ case Command::IMPORT: {
+ Platform::FileDialogRef dialog = Platform::CreateOpenFileDialog(SS.GW.window);
+ dialog->AddFilters(Platform::ImportFileFilters);
+ dialog->ThawChoices(settings, "Import");
+ if(!dialog->RunModal()) break;
+ dialog->FreezeChoices(settings, "Import");
- if(Extension(importFile) == "dxf") {
+ Platform::Path importFile = dialog->GetFilename();
+ if(importFile.HasExtension("dxf")) {
ImportDxf(importFile);
- } else if(Extension(importFile) == "dwg") {
+ } else if(importFile.HasExtension("dwg")) {
ImportDwg(importFile);
} else {
- Error("Can't identify file type from file extension of "
- "filename '%s'; try .dxf or .dwg.", importFile.c_str());
+ Error(_("Can't identify file type from file extension of "
+ "filename '%s'; try .dxf or .dwg."), importFile.raw.c_str());
+ break;
}
- SS.GenerateAll(SolveSpaceUI::GENERATE_UNTIL_ACTIVE);
+ SS.GenerateAll(SolveSpaceUI::Generate::UNTIL_ACTIVE);
SS.ScheduleShowTW();
break;
}
- case GraphicsWindow::MNU_EXIT:
+ case Command::EXIT:
if(!SS.OkayToStartNewFile()) break;
SS.Exit();
break;
- default: oops();
+ default: ssassert(false, "Unexpected menu ID");
}
- SS.UpdateWindowTitle();
+ SS.UpdateWindowTitles();
}
-void SolveSpaceUI::MenuAnalyze(int id) {
+void SolveSpaceUI::MenuAnalyze(Command id) {
+ Platform::SettingsRef settings = Platform::GetSettings();
+
SS.GW.GroupSelection();
-#define gs (SS.GW.gs)
+ auto const &gs = SS.GW.gs;
switch(id) {
- case GraphicsWindow::MNU_STEP_DIM:
+ case Command::STEP_DIM:
if(gs.constraints == 1 && gs.n == 0) {
Constraint *c = SK.GetConstraint(gs.constraint[0]);
if(c->HasLabel() && !c->reference) {
- SS.TW.shown.dimFinish = c->valA;
- SS.TW.shown.dimSteps = 10;
- SS.TW.shown.dimIsDistance =
- (c->type != Constraint::ANGLE) &&
- (c->type != Constraint::LENGTH_RATIO) &&
- (c->type != Constraint::LENGTH_DIFFERENCE);
+ SS.TW.stepDim.finish = c->valA;
+ SS.TW.stepDim.steps = 10;
+ SS.TW.stepDim.isDistance =
+ (c->type != Constraint::Type::ANGLE) &&
+ (c->type != Constraint::Type::LENGTH_RATIO) &&
+ (c->type != Constraint::Type::LENGTH_DIFFERENCE);
SS.TW.shown.constraint = c->h;
- SS.TW.shown.screen = TextWindow::SCREEN_STEP_DIMENSION;
+ SS.TW.shown.screen = TextWindow::Screen::STEP_DIMENSION;
// The step params are specified in the text window,
// so force that to be shown.
SS.ScheduleShowTW();
SS.GW.ClearSelection();
} else {
- Error("Constraint must have a label, and must not be "
- "a reference dimension.");
+ Error(_("Constraint must have a label, and must not be "
+ "a reference dimension."));
}
} else {
- Error("Bad selection for step dimension; select a constraint.");
+ Error(_("Bad selection for step dimension; select a constraint."));
}
break;
- case GraphicsWindow::MNU_NAKED_EDGES: {
- SS.nakedEdges.Clear();
-
- Group *g = SK.GetGroup(SS.GW.activeGroup);
- SMesh *m = &(g->displayMesh);
- SKdNode *root = SKdNode::From(m);
- bool inters, leaks;
- root->MakeCertainEdgesInto(&(SS.nakedEdges),
- SKdNode::NAKED_OR_SELF_INTER_EDGES, true, &inters, &leaks);
-
- InvalidateGraphics();
-
- const char *intersMsg = inters ?
- "The mesh is self-intersecting (NOT okay, invalid)." :
- "The mesh is not self-intersecting (okay, valid).";
- const char *leaksMsg = leaks ?
- "The mesh has naked edges (NOT okay, invalid)." :
- "The mesh is watertight (okay, valid).";
-
- std::string cntMsg = ssprintf("\n\nThe model contains %d triangles, from "
- "%d surfaces.", g->displayMesh.l.n, g->runningShell.surface.n);
-
- if(SS.nakedEdges.l.n == 0) {
- Message("%s\n\n%s\n\nZero problematic edges, good.%s",
- intersMsg, leaksMsg, cntMsg.c_str());
- } else {
- Error("%s\n\n%s\n\n%d problematic edges, bad.%s",
- intersMsg, leaksMsg, SS.nakedEdges.l.n, cntMsg.c_str());
- }
+ case Command::NAKED_EDGES: {
+ ShowNakedEdges(/*reportOnlyWhenNotOkay=*/false);
break;
}
- case GraphicsWindow::MNU_INTERFERENCE: {
+ case Command::INTERFERENCE: {
SS.nakedEdges.Clear();
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
SKdNode *root = SKdNode::From(m);
bool inters, leaks;
root->MakeCertainEdgesInto(&(SS.nakedEdges),
- SKdNode::SELF_INTER_EDGES, false, &inters, &leaks);
+ EdgeKind::SELF_INTER, /*coplanarIsInter=*/false, &inters, &leaks);
- InvalidateGraphics();
+ SS.GW.Invalidate();
if(inters) {
Error("%d edges interfere with other triangles, bad.",
SS.nakedEdges.l.n);
} else {
- Message("The assembly does not interfere, good.");
+ Message(_("The assembly does not interfere, good."));
}
break;
}
- case GraphicsWindow::MNU_VOLUME: {
- SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
+ case Command::CENTER_OF_MASS: {
+ SS.UpdateCenterOfMass();
+ SS.centerOfMass.draw = true;
+ SS.GW.Invalidate();
+ break;
+ }
- double vol = 0;
- int i;
- for(i = 0; i < m->l.n; i++) {
- STriangle tr = m->l.elem[i];
-
- // Translate to place vertex A at (x, y, 0)
- Vector trans = Vector::From(tr.a.x, tr.a.y, 0);
- tr.a = (tr.a).Minus(trans);
- tr.b = (tr.b).Minus(trans);
- tr.c = (tr.c).Minus(trans);
-
- // Rotate to place vertex B on the y-axis. Depending on
- // whether the triangle is CW or CCW, C is either to the
- // right or to the left of the y-axis. This handles the
- // sign of our normal.
- Vector u = Vector::From(-tr.b.y, tr.b.x, 0);
- u = u.WithMagnitude(1);
- Vector v = Vector::From(tr.b.x, tr.b.y, 0);
- v = v.WithMagnitude(1);
- Vector n = Vector::From(0, 0, 1);
-
- tr.a = (tr.a).DotInToCsys(u, v, n);
- tr.b = (tr.b).DotInToCsys(u, v, n);
- tr.c = (tr.c).DotInToCsys(u, v, n);
-
- n = tr.Normal().WithMagnitude(1);
-
- // Triangles on edge don't contribute
- if(fabs(n.z) < LENGTH_EPS) continue;
-
- // The plane has equation p dot n = a dot n
- double d = (tr.a).Dot(n);
- // nx*x + ny*y + nz*z = d
- // nz*z = d - nx*x - ny*y
- double A = -n.x/n.z, B = -n.y/n.z, C = d/n.z;
-
- double mac = tr.c.y/tr.c.x, mbc = (tr.c.y - tr.b.y)/tr.c.x;
- double xc = tr.c.x, yb = tr.b.y;
-
- // I asked Maple for
- // int(int(A*x + B*y +C, y=mac*x..(mbc*x + yb)), x=0..xc);
- double integral =
- (1.0/3)*(
- A*(mbc-mac)+
- (1.0/2)*B*(mbc*mbc-mac*mac)
- )*(xc*xc*xc)+
- (1.0/2)*(A*yb+B*yb*mbc+C*(mbc-mac))*xc*xc+
- C*yb*xc+
- (1.0/2)*B*yb*yb*xc;
-
- vol += integral;
+ case Command::VOLUME: {
+ Group *g = SK.GetGroup(SS.GW.activeGroup);
+ double totalVol = g->displayMesh.CalculateVolume();
+ std::string msg = ssprintf(
+ _("The volume of the solid model is:\n\n"
+ " %s"),
+ SS.MmToStringSI(totalVol, /*dim=*/3).c_str());
+
+ SMesh curMesh = {};
+ g->thisShell.TriangulateInto(&curMesh);
+ double curVol = curMesh.CalculateVolume();
+ if(curVol > 0.0) {
+ msg += ssprintf(
+ _("\nThe volume of current group mesh is:\n\n"
+ " %s"),
+ SS.MmToStringSI(curVol, /*dim=*/3).c_str());
}
- std::string msg = ssprintf("The volume of the solid model is:\n\n"" %.3f %s^3",
- vol / pow(SS.MmPerUnit(), 3),
- SS.UnitName());
-
- if(SS.viewUnits == SolveSpaceUI::UNIT_MM) {
- msg += ssprintf("\n %.2f mL", vol/(10*10*10));
- }
- msg += "\n\nCurved surfaces have been approximated as triangles.\n"
- "This introduces error, typically of around 1%.";
+ msg += _("\n\nCurved surfaces have been approximated as triangles.\n"
+ "This introduces error, typically of around 1%.");
Message("%s", msg.c_str());
break;
}
- case GraphicsWindow::MNU_AREA: {
+ case Command::AREA: {
Group *g = SK.GetGroup(SS.GW.activeGroup);
- if(g->polyError.how != Group::POLY_GOOD) {
- Error("This group does not contain a correctly-formed "
- "2d closed area. It is open, not coplanar, or self-"
- "intersecting.");
+ SS.GW.GroupSelection();
+
+ if(gs.faces > 0) {
+ std::vector<uint32_t> faces;
+ faces.push_back(gs.face[0].v);
+ if(gs.faces > 1) faces.push_back(gs.face[1].v);
+ double area = g->displayMesh.CalculateSurfaceArea(faces);
+ Message(_("The surface area of the selected faces is:\n\n"
+ " %s\n\n"
+ "Curves have been approximated as piecewise linear.\n"
+ "This introduces error, typically of around 1%%."),
+ SS.MmToStringSI(area, /*dim=*/2).c_str());
+ break;
+ }
+
+ if(g->polyError.how != PolyError::GOOD) {
+ Error(_("This group does not contain a correctly-formed "
+ "2d closed area. It is open, not coplanar, or self-"
+ "intersecting."));
break;
}
SEdgeList sel = {};
g->polyLoops.MakeEdgesInto(&sel);
SPolygon sp = {};
- sel.AssemblePolygon(&sp, NULL, true);
+ sel.AssemblePolygon(&sp, NULL, /*keepDir=*/true);
sp.normal = sp.ComputeNormal();
sp.FixContourDirections();
double area = sp.SignedArea();
- double scale = SS.MmPerUnit();
- Message("The area of the region sketched in this group is:\n\n"
- " %.3f %s^2\n\n"
- "Curves have been approximated as piecewise linear.\n"
- "This introduces error, typically of around 1%%.",
- area / (scale*scale),
- SS.UnitName());
+ Message(_("The area of the region sketched in this group is:\n\n"
+ " %s\n\n"
+ "Curves have been approximated as piecewise linear.\n"
+ "This introduces error, typically of around 1%%."),
+ SS.MmToStringSI(area, /*dim=*/2).c_str());
sel.Clear();
sp.Clear();
break;
}
- case GraphicsWindow::MNU_SHOW_DOF:
+ case Command::PERIMETER: {
+ if(gs.n > 0 && gs.n == gs.entities) {
+ double perimeter = 0.0;
+ for(int i = 0; i < gs.entities; i++) {
+ Entity *en = SK.entity.FindById(gs.entity[i]);
+ SEdgeList *el = en->GetOrGenerateEdges();
+ for(const SEdge &e : el->l) {
+ perimeter += e.b.Minus(e.a).Magnitude();
+ }
+ }
+ Message(_("The total length of the selected entities is:\n\n"
+ " %s\n\n"
+ "Curves have been approximated as piecewise linear.\n"
+ "This introduces error, typically of around 1%%."),
+ SS.MmToStringSI(perimeter, /*dim=*/1).c_str());
+ } else {
+ Error(_("Bad selection for perimeter; select line segments, arcs, and curves."));
+ }
+ break;
+ }
+
+ case Command::SHOW_DOF:
// This works like a normal solve, except that it calculates
// which variables are free/bound at the same time.
- SS.GenerateAll(SolveSpaceUI::GENERATE_ALL, true);
+ SS.GenerateAll(SolveSpaceUI::Generate::ALL, /*andFindFree=*/true);
break;
- case GraphicsWindow::MNU_TRACE_PT:
+ case Command::TRACE_PT:
if(gs.points == 1 && gs.n == 1) {
SS.traced.point = gs.point[0];
SS.GW.ClearSelection();
} else {
- Error("Bad selection for trace; select a single point.");
+ Error(_("Bad selection for trace; select a single point."));
}
break;
- case GraphicsWindow::MNU_STOP_TRACING: {
- std::string exportFile;
- if(GetSaveFile(&exportFile, "", CsvFileFilter)) {
- FILE *f = ssfopen(exportFile, "wb");
+ case Command::STOP_TRACING: {
+ Platform::FileDialogRef dialog = Platform::CreateSaveFileDialog(SS.GW.window);
+ dialog->AddFilters(Platform::CsvFileFilters);
+ dialog->ThawChoices(settings, "Trace");
+ if(dialog->RunModal()) {
+ dialog->FreezeChoices(settings, "Trace");
+
+ FILE *f = OpenFile(dialog->GetFilename(), "wb");
if(f) {
int i;
SContour *sc = &(SS.traced.path);
for(i = 0; i < sc->l.n; i++) {
- Vector p = sc->l.elem[i].p;
+ Vector p = sc->l[i].p;
double s = SS.exportScale;
fprintf(f, "%.10f, %.10f, %.10f\r\n",
p.x/s, p.y/s, p.z/s);
}
fclose(f);
} else {
- Error("Couldn't write to '%s'", exportFile.c_str());
+ Error(_("Couldn't write to '%s'"), dialog->GetFilename().raw.c_str());
}
}
// Clear the trace, and stop tracing
SS.traced.point = Entity::NO_ENTITY;
SS.traced.path.l.Clear();
- InvalidateGraphics();
+ SS.GW.Invalidate();
break;
}
- default: oops();
+ default: ssassert(false, "Unexpected menu ID");
+ }
+}
+
+void SolveSpaceUI::ShowNakedEdges(bool reportOnlyWhenNotOkay) {
+ SS.nakedEdges.Clear();
+
+ Group *g = SK.GetGroup(SS.GW.activeGroup);
+ SMesh *m = &(g->displayMesh);
+ SKdNode *root = SKdNode::From(m);
+ bool inters, leaks;
+ root->MakeCertainEdgesInto(&(SS.nakedEdges),
+ EdgeKind::NAKED_OR_SELF_INTER, /*coplanarIsInter=*/true, &inters, &leaks);
+
+ if(reportOnlyWhenNotOkay && !inters && !leaks && SS.nakedEdges.l.IsEmpty()) {
+ return;
+ }
+ SS.GW.Invalidate();
+
+ const char *intersMsg = inters ?
+ _("The mesh is self-intersecting (NOT okay, invalid).") :
+ _("The mesh is not self-intersecting (okay, valid).");
+ const char *leaksMsg = leaks ?
+ _("The mesh has naked edges (NOT okay, invalid).") :
+ _("The mesh is watertight (okay, valid).");
+
+ std::string cntMsg = ssprintf(
+ _("\n\nThe model contains %d triangles, from %d surfaces."),
+ g->displayMesh.l.n, g->runningShell.surface.n);
+
+ if(SS.nakedEdges.l.IsEmpty()) {
+ Message(_("%s\n\n%s\n\nZero problematic edges, good.%s"),
+ intersMsg, leaksMsg, cntMsg.c_str());
+ } else {
+ Error(_("%s\n\n%s\n\n%d problematic edges, bad.%s"),
+ intersMsg, leaksMsg, SS.nakedEdges.l.n, cntMsg.c_str());
}
}
-void SolveSpaceUI::MenuHelp(int id) {
+void SolveSpaceUI::MenuHelp(Command id) {
switch(id) {
- case GraphicsWindow::MNU_WEBSITE:
- OpenWebsite("http://solvespace.com/helpmenu");
+ case Command::WEBSITE:
+ Platform::OpenInBrowser("http://solvespace.com/helpmenu");
break;
- case GraphicsWindow::MNU_ABOUT:
- Message(
-"This is SolveSpace version " PACKAGE_VERSION ".\n"
+ case Command::ABOUT:
+ Message(_(
+"This is SolveSpace version %s.\n"
"\n"
"For more information, see http://solvespace.com/\n"
"\n"
"There is NO WARRANTY, to the extent permitted by\n"
"law. For details, visit http://gnu.org/licenses/\n"
"\n"
-"© 2008-2016 Jonathan Westhues and other authors.\n"
-);
+"© 2008-%d Jonathan Westhues and other authors.\n"),
+PACKAGE_VERSION, 2020);
break;
- default: oops();
+ default: ssassert(false, "Unexpected menu ID");
}
}
-void SolveSpaceUI::Clear(void) {
+void SolveSpaceUI::Clear() {
sys.Clear();
for(int i = 0; i < MAX_UNDO; i++) {
if(i < undo.cnt) undo.d[i].Clear();
if(i < redo.cnt) redo.d[i].Clear();
}
+ TW.window = NULL;
+ GW.openRecentMenu = NULL;
+ GW.linkRecentMenu = NULL;
+ GW.showGridMenuItem = NULL;
+ GW.perspectiveProjMenuItem = NULL;
+ GW.showToolbarMenuItem = NULL;
+ GW.showTextWndMenuItem = NULL;
+ GW.fullScreenMenuItem = NULL;
+ GW.unitsMmMenuItem = NULL;
+ GW.unitsMetersMenuItem = NULL;
+ GW.unitsInchesMenuItem = NULL;
+ GW.inWorkplaneMenuItem = NULL;
+ GW.in3dMenuItem = NULL;
+ GW.undoMenuItem = NULL;
+ GW.redoMenuItem = NULL;
+ GW.window = NULL;
}
-void Sketch::Clear(void) {
+void Sketch::Clear() {
group.Clear();
groupOrder.Clear();
constraint.Clear();
BBox Sketch::CalculateEntityBBox(bool includingInvisible) {
BBox box = {};
bool first = true;
- for(int i = 0; i < entity.n; i++) {
- Entity *e = (Entity *)&entity.elem[i];
- if(!(e->IsVisible() || includingInvisible)) continue;
-
- Vector point;
- double r = 0.0;
- if(e->IsPoint()) {
- point = e->PointGetNum();
- } else {
- switch(e->type) {
- case Entity::ARC_OF_CIRCLE:
- case Entity::CIRCLE:
- r = e->CircleGetRadiusNum();
- point = GetEntity(e->point[0])->PointGetNum();
- break;
- default: continue;
- }
- }
+ auto includePoint = [&](const Vector &point) {
if(first) {
box.minp = point;
box.maxp = point;
- box.Include(point, r);
first = false;
} else {
- box.Include(point, r);
+ box.Include(point);
+ }
+ };
+
+ for(const Entity &e : entity) {
+ if(e.construction) continue;
+ if(!(includingInvisible || e.IsVisible())) continue;
+
+ // arc center point shouldn't be included in bounding box calculation
+ if(e.IsPoint() && e.h.isFromRequest()) {
+ Request *r = SK.GetRequest(e.h.request());
+ if(r->type == Request::Type::ARC_OF_CIRCLE && e.h == r->h.entity(1)) {
+ continue;
+ }
+ }
+
+ if(e.IsPoint()) {
+ includePoint(e.PointGetNum());
+ continue;
+ }
+
+ switch(e.type) {
+ // Circles and arcs are special cases. We calculate their bounds
+ // based on Bezier curve bounds. This is not exact for arcs,
+ // but the implementation is rather simple.
+ case Entity::Type::CIRCLE:
+ case Entity::Type::ARC_OF_CIRCLE: {
+ SBezierList sbl = {};
+ e.GenerateBezierCurves(&sbl);
+
+ for(const SBezier &sb : sbl.l) {
+ for(int j = 0; j <= sb.deg; j++) {
+ includePoint(sb.ctrl[j]);
+ }
+ }
+ sbl.Clear();
+ continue;
+ }
+
+ default:
+ continue;
}
}
+
return box;
}
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
-#ifndef __SOLVESPACE_H
-#define __SOLVESPACE_H
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <stdarg.h>
-#include <math.h>
-#include <limits.h>
+#ifndef SOLVESPACE_H
+#define SOLVESPACE_H
+
+#include <cctype>
+#include <climits>
+#include <cmath>
+#include <csetjmp>
+#include <cstdarg>
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
#include <algorithm>
-#include <memory>
-#include <string>
+#include <chrono>
+#include <functional>
#include <locale>
-#include <vector>
-#include <unordered_map>
#include <map>
+#include <memory>
#include <set>
#include <sstream>
-#ifdef WIN32
-# include <windows.h> // required by GL headers
-#endif
-#ifdef __APPLE__
-# include <strings.h> // for strcasecmp in file.cpp
-# include <OpenGL/gl.h>
-# include <OpenGL/glu.h>
-#else
-# include <GL/gl.h>
-# include <GL/glu.h>
-#endif
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
// We declare these in advance instead of simply using FT_Library
// (defined as typedef FT_LibraryRec_* FT_Library) because including
struct FT_LibraryRec_;
struct FT_FaceRec_;
+typedef struct _cairo cairo_t;
+typedef struct _cairo_surface cairo_surface_t;
+
// The few floating-point equality comparisons in SolveSpace have been
// carefully considered, so we disable the -Wfloat-equal warning for them
#ifdef __clang__
#endif
// Debugging functions
-#ifdef NDEBUG
-#define oops() do { dbp("oops at line %d, file %s\n", __LINE__, __FILE__); \
- exit(-1); } while(0)
+#if defined(__GNUC__)
+#define ssassert(condition, message) \
+ do { \
+ if(__builtin_expect((condition), true) == false) { \
+ SolveSpace::AssertFailure(__FILE__, __LINE__, __func__, #condition, message); \
+ __builtin_unreachable(); \
+ } \
+ } while(0)
#else
-#define oops() do { dbp("oops at line %d, file %s\n", __LINE__, __FILE__); \
- abort(); } while(0)
+#define ssassert(condition, message) \
+ do { \
+ if((condition) == false) { \
+ SolveSpace::AssertFailure(__FILE__, __LINE__, __func__, #condition, message); \
+ abort(); \
+ } \
+ } while(0)
#endif
-#ifndef isnan
-# define isnan(x) (((x) != (x)) || (x > 1e11) || (x < -1e11))
-#endif
+#define dbp SolveSpace::Platform::DebugPrint
+#define DBPTRI(tri) \
+ dbp("tri: (%.3f %.3f %.3f) (%.3f %.3f %.3f) (%.3f %.3f %.3f)", \
+ CO((tri).a), CO((tri).b), CO((tri).c))
namespace SolveSpace {
using std::min;
using std::max;
using std::swap;
+using std::fabs;
+
+[[noreturn]]
+void AssertFailure(const char *file, unsigned line, const char *function,
+ const char *condition, const char *message);
#if defined(__GNUC__)
__attribute__((__format__ (__printf__, 1, 2)))
#endif
std::string ssprintf(const char *fmt, ...);
+inline bool IsReasonable(double x) {
+ return std::isnan(x) || x > 1e11 || x < -1e11;
+}
+
inline int WRAP(int v, int n) {
// Clamp it to the range [0, n)
while(v >= n) v -= n;
return v;
}
-// Why is this faster than the library function?
-inline double ffabs(double v) { return (v > 0) ? v : (-v); }
-
#define CO(v) (v).x, (v).y, (v).z
-#define ANGLE_COS_EPS (1e-6)
-#define LENGTH_EPS (1e-6)
-#define VERY_POSITIVE (1e10)
-#define VERY_NEGATIVE (-1e10)
+static constexpr double ANGLE_COS_EPS = 1e-6;
+static constexpr double LENGTH_EPS = 1e-6;
+static constexpr double VERY_POSITIVE = 1e10;
+static constexpr double VERY_NEGATIVE = -1e10;
-#define isforname(c) (isalnum(c) || (c) == '_' || (c) == '-' || (c) == '#')
+#include "platform/platform.h"
+#include "platform/gui.h"
+#include "resource.h"
-#if defined(WIN32)
-std::string Narrow(const wchar_t *s);
-std::wstring Widen(const char *s);
-std::string Narrow(const std::wstring &s);
-std::wstring Widen(const std::string &s);
-#endif
-
-inline double Random(double vmax) {
- return (vmax*rand()) / RAND_MAX;
-}
+using Platform::AllocTemporary;
+using Platform::FreeAllTemporary;
class Expr;
class ExprVector;
class ExprQuaternion;
class RgbaColor;
+enum class Command : uint32_t;
-//================
-// From the platform-specific code.
-#if defined(WIN32)
-#define PATH_SEP "\\"
-#else
-#define PATH_SEP "/"
-#endif
-
-FILE *ssfopen(const std::string &filename, const char *mode);
-std::fstream ssfstream(const std::string &filename, std::ios_base::openmode mode);
-void ssremove(const std::string &filename);
-
-#define MAX_RECENT 8
-#define RECENT_OPEN (0xf000)
-#define RECENT_LINK (0xf100)
-extern std::string RecentFile[MAX_RECENT];
-void RefreshRecentMenus(void);
-
-enum DialogChoice { DIALOG_YES = 1, DIALOG_NO = -1, DIALOG_CANCEL = 0 };
-DialogChoice SaveFileYesNoCancel(void);
-DialogChoice LoadAutosaveYesNo(void);
-DialogChoice LocateImportedFileYesNoCancel(const std::string &filename,
- bool canCancel);
-
-#define AUTOSAVE_SUFFIX "~"
-
-struct FileFilter {
- const char *name;
- const char *patterns[3];
+enum class Unit : uint32_t {
+ MM = 0,
+ INCHES,
+ METERS
};
-// SolveSpace native file format
-const FileFilter SlvsFileFilter[] = {
- { "SolveSpace models", { "slvs" } },
- { NULL, {} }
-};
-// PNG format bitmap
-const FileFilter PngFileFilter[] = {
- { "PNG", { "png" } },
- { NULL, {} }
-};
-// Triangle mesh
-const FileFilter MeshFileFilter[] = {
- { "STL mesh", { "stl" } },
- { "Wavefront OBJ mesh", { "obj" } },
- { "Three.js-compatible mesh, with viewer", { "html" } },
- { "Three.js-compatible mesh, mesh only", { "js" } },
- { NULL, {} }
-};
-// NURBS surfaces
-const FileFilter SurfaceFileFilter[] = {
- { "STEP file", { "step", "stp" } },
- { NULL, {} }
-};
-// 2d vector (lines and curves) format
-const FileFilter VectorFileFilter[] = {
- { "PDF file", { "pdf" } },
- { "Encapsulated PostScript", { "eps", "ps" } },
- { "Scalable Vector Graphics", { "svg" } },
- { "STEP file", { "step", "stp" } },
- { "DXF file (AutoCAD 2007)", { "dxf" } },
- { "HPGL file", { "plt", "hpgl" } },
- { "G Code", { "ngc", "txt" } },
- { NULL, {} }
-};
-// 3d vector (wireframe lines and curves) format
-const FileFilter Vector3dFileFilter[] = {
- { "STEP file", { "step", "stp" } },
- { "DXF file (AutoCAD 2007)", { "dxf" } },
- { NULL, {} }
-};
-// All Importable formats
-const FileFilter ImportableFileFilter[] = {
- { "AutoCAD DXF and DWG files", { "dxf", "dwg" } },
- { NULL, {} }
-};
-// Comma-separated value, like a spreadsheet would use
-const FileFilter CsvFileFilter[] = {
- { "CSV", { "csv" } },
- { NULL, {} }
-};
-
-bool GetSaveFile(std::string *filename, const std::string &defExtension,
- const FileFilter filters[]);
-bool GetOpenFile(std::string *filename, const std::string &defExtension,
- const FileFilter filters[]);
-std::vector<std::string> GetFontFiles();
-
-void OpenWebsite(const char *url);
-
-void CheckMenuById(int id, bool checked);
-void RadioMenuById(int id, bool selected);
-void EnableMenuById(int id, bool enabled);
-
-void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars,
- const std::string &str);
-void HideGraphicsEditControl(void);
-bool GraphicsEditControlIsVisible(void);
-void ShowTextEditControl(int x, int y, const std::string &str);
-void HideTextEditControl(void);
-bool TextEditControlIsVisible(void);
-void MoveTextScrollbarTo(int pos, int maxPos, int page);
-
-#define CONTEXT_SUBMENU (-1)
-#define CONTEXT_SEPARATOR (-2)
-void AddContextMenuItem(const char *legend, int id);
-void CreateContextSubmenu(void);
-int ShowContextMenu(void);
-
-void ToggleMenuBar(void);
-bool MenuBarIsVisible(void);
-void ShowTextWindow(bool visible);
-void InvalidateText(void);
-void InvalidateGraphics(void);
-void PaintGraphics(void);
-void ToggleFullScreen(void);
-bool FullScreenIsActive(void);
-void GetGraphicsWindowSize(int *w, int *h);
-void GetTextWindowSize(int *w, int *h);
-int64_t GetMilliseconds(void);
-int64_t GetUnixTime(void);
-
-void dbp(const char *str, ...);
-#define DBPTRI(tri) \
- dbp("tri: (%.3f %.3f %.3f) (%.3f %.3f %.3f) (%.3f %.3f %.3f)", \
- CO((tri).a), CO((tri).b), CO((tri).c))
-
-void SetCurrentFilename(const std::string &filename);
-void SetMousePointerToHand(bool yes);
-void DoMessageBox(const char *str, int rows, int cols, bool error);
-void SetTimerFor(int milliseconds);
-void SetAutosaveTimerFor(int minutes);
-void ScheduleLater();
-void ExitNow(void);
-
-void CnfFreezeInt(uint32_t val, const std::string &name);
-void CnfFreezeFloat(float val, const std::string &name);
-void CnfFreezeString(const std::string &val, const std::string &name);
-std::string CnfThawString(const std::string &val, const std::string &name);
-uint32_t CnfThawInt(uint32_t val, const std::string &name);
-float CnfThawFloat(float val, const std::string &name);
-
-void *AllocTemporary(size_t n);
-void FreeTemporary(void *p);
-void FreeAllTemporary(void);
-void *MemAlloc(size_t n);
-void MemFree(void *p);
-void InitHeaps(void);
-void vl(void); // debug function to validate heaps
-
-// End of platform-specific functions
-//================
+template<class Key, class T>
+using handle_map = std::map<Key, T>;
class Group;
class SSurface;
#include "dsc.h"
#include "polygon.h"
#include "srf/surface.h"
+#include "render/render.h"
class Entity;
class hEntity;
typedef IdList<Entity,hEntity> EntityList;
typedef IdList<Param,hParam> ParamList;
+enum class SolveResult : uint32_t {
+ OKAY = 0,
+ DIDNT_CONVERGE = 10,
+ REDUNDANT_OKAY = 11,
+ REDUNDANT_DIDNT_CONVERGE = 12,
+ TOO_MANY_UNKNOWNS = 20
+};
+
+
#include "sketch.h"
#include "ui.h"
#include "expr.h"
utf8_iterator& operator++() { **this; p=n; n=NULL; return *this; }
utf8_iterator operator++(int) { utf8_iterator t(*this); operator++(); return t; }
char32_t operator*();
+ const char* ptr() const { return p; }
};
class ReadUTF8 {
const std::string &str;
public:
ReadUTF8(const std::string &str) : str(str) {}
utf8_iterator begin() const { return utf8_iterator(&str[0]); }
- utf8_iterator end() const { return utf8_iterator(&str[str.length()]); }
+ utf8_iterator end() const { return utf8_iterator(&str[0] + str.length()); }
};
-void ssglLineWidth(GLfloat width);
-void ssglVertex3v(Vector u);
-void ssglAxisAlignedQuad(double l, double r, double t, double b, bool lone = true);
-void ssglAxisAlignedLineLoop(double l, double r, double t, double b);
-#ifdef WIN32
-# define SSGL_CALLBACK __stdcall
-#else
-# define SSGL_CALLBACK
-#endif
-extern "C" { typedef void SSGL_CALLBACK ssglCallbackFptr(void); }
-void ssglTesselatePolygon(GLUtesselator *gt, SPolygon *p);
-void ssglFillPolygon(SPolygon *p);
-void ssglFillMesh(bool useSpecColor, RgbaColor color,
- SMesh *m, uint32_t h, uint32_t s1, uint32_t s2);
-void ssglDebugPolygon(SPolygon *p);
-void ssglDrawEdges(SEdgeList *l, bool endpointsToo, hStyle hs);
-void ssglDrawOutlines(SOutlineList *l, Vector projDir, hStyle hs);
-void ssglDebugMesh(SMesh *m);
-void ssglMarkPolygonNormal(SPolygon *p);
-typedef void ssglLineFn(void *data, Vector a, Vector b);
-void ssglWriteText(const std::string &str, double h, Vector t, Vector u, Vector v,
- ssglLineFn *fn, void *fndata);
-void ssglWriteTextRefCenter(const std::string &str, double h, Vector t, Vector u, Vector v,
- ssglLineFn *fn, void *fndata);
-double ssglStrCapHeight(double h);
-double ssglStrFontSize(double h);
-double ssglStrWidth(const std::string &str, double h);
-void ssglLockColorTo(RgbaColor rgb);
-void ssglStippledLine(Vector a, Vector b, double width,
- int stippleType, double stippleScale, bool maybeFat);
-void ssglStippledLine(Vector a, Vector b, double width,
- const char *stipplePattern, double stippleScale, bool maybeFat);
-void ssglFatLine(Vector a, Vector b, double width);
-void ssglUnlockColor(void);
-void ssglColorRGB(RgbaColor rgb);
-void ssglColorRGBa(RgbaColor rgb, double a);
-void ssglDepthRangeOffset(int units);
-void ssglDepthRangeLockToFront(bool yes);
-void ssglDrawPixelsWithTexture(uint8_t *data, int w, int h);
-void ssglInitializeBitmapFont();
-void ssglBitmapText(const std::string &str, Vector p);
-void ssglBitmapCharQuad(char32_t chr, double x, double y);
-int ssglBitmapCharWidth(char32_t chr);
-#define TEXTURE_BACKGROUND_IMG 10
-#define TEXTURE_DRAW_PIXELS 20
-#define TEXTURE_COLOR_PICKER_2D 30
-#define TEXTURE_COLOR_PICKER_1D 40
-#define TEXTURE_BITMAP_FONT 50
-
#define arraylen(x) (sizeof((x))/sizeof((x)[0]))
#define PI (3.1415926535897931)
double a21, double a22, double a23, double a24,
double a31, double a32, double a33, double a34,
double a41, double a42, double a43, double a44);
-std::string MakeAcceleratorLabel(int accel);
-bool FilenameHasExtension(const std::string &str, const char *ext);
-bool ReadFile(const std::string &filename, std::string *data);
-bool WriteFile(const std::string &filename, const std::string &data);
-void Message(const char *str, ...);
-void Error(const char *str, ...);
-void CnfFreezeBool(bool v, const std::string &name);
-void CnfFreezeColor(RgbaColor v, const std::string &name);
-bool CnfThawBool(bool v, const std::string &name);
-RgbaColor CnfThawColor(RgbaColor v, const std::string &name);
+void MultMatrix(double *mata, double *matb, double *matr);
+
+int64_t GetMilliseconds();
+void Message(const char *fmt, ...);
+void MessageAndRun(std::function<void()> onDismiss, const char *fmt, ...);
+void Error(const char *fmt, ...);
class System {
public:
} mat;
static const double RANK_MAG_TOLERANCE, CONVERGE_TOLERANCE;
- int CalculateRank(void);
- bool TestRank(void);
+ int CalculateRank();
+ bool TestRank(int *rank = NULL);
static bool SolveLinearSystem(double X[], double A[][MAX_UNKNOWNS],
double B[], int N);
- bool SolveLeastSquares(void);
+ bool SolveLeastSquares();
bool WriteJacobian(int tag);
- void EvalJacobian(void);
+ void EvalJacobian();
void WriteEquationsExceptFor(hConstraint hc, Group *g);
- void FindWhichToRemoveToFixJacobian(Group *g, List<hConstraint> *bad);
- void SolveBySubstitution(void);
+ void FindWhichToRemoveToFixJacobian(Group *g, List<hConstraint> *bad,
+ bool forceDofCheck);
+ void SolveBySubstitution();
bool IsDragged(hParam p);
bool NewtonSolve(int tag);
- enum {
- SOLVED_OKAY = 0,
- DIDNT_CONVERGE = 10,
- REDUNDANT_OKAY = 11,
- REDUNDANT_DIDNT_CONVERGE = 12,
- TOO_MANY_UNKNOWNS = 20
- };
- int Solve(Group *g, int *dof, List<hConstraint> *bad,
- bool andFindBad, bool andFindFree);
+ void MarkParamsFree(bool findFree);
+ int CalculateDof();
+
+ SolveResult Solve(Group *g, int *rank = NULL, int *dof = NULL,
+ List<hConstraint> *bad = NULL,
+ bool andFindBad = false, bool andFindFree = false,
+ bool forceDofCheck = false);
- void Clear(void);
+ SolveResult SolveRank(Group *g, int *rank = NULL, int *dof = NULL,
+ List<hConstraint> *bad = NULL,
+ bool andFindBad = false, bool andFindFree = false);
+
+ void Clear();
};
#include "ttf.h"
class StepFileWriter {
public:
- void ExportSurfacesTo(const std::string &filename);
- void WriteHeader(void);
- void WriteProductHeader(void);
+ void ExportSurfacesTo(const Platform::Path &filename);
+ void WriteHeader();
+ void WriteProductHeader();
int ExportCurve(SBezier *sb);
int ExportCurveLoop(SBezierLoop *loop, bool inner);
void ExportSurface(SSurface *ss, SBezierList *sbl);
- void WriteWireframe(void);
- void WriteFooter(void);
+ void WriteWireframe();
+ void WriteFooter();
List<int> curves;
List<int> advancedFaces;
public:
FILE *f;
- std::string filename;
+ Platform::Path filename;
Vector ptMin, ptMax;
static double MmToPts(double mm);
- static VectorFileWriter *ForFile(const std::string &filename);
-
- ~VectorFileWriter();
+ static VectorFileWriter *ForFile(const Platform::Path &filename);
void SetModelviewProjection(const Vector &u, const Vector &v, const Vector &n,
const Vector &origin, double cameraTan, double scale);
void BezierAsPwl(SBezier *sb);
void BezierAsNonrationalCubic(SBezier *sb, int depth=0);
- virtual bool OutputConstraints(IdList<Constraint,hConstraint> *) { return false; }
- virtual bool CanOutputMesh() const { return false; }
-
- virtual void StartPath( RgbaColor strokeRgb, double lineWidth,
+ virtual void StartPath(RgbaColor strokeRgb, double lineWidth,
bool filled, RgbaColor fillRgb, hStyle hs) = 0;
virtual void FinishPath(RgbaColor strokeRgb, double lineWidth,
bool filled, RgbaColor fillRgb, hStyle hs) = 0;
virtual void Bezier(SBezier *sb) = 0;
virtual void Triangle(STriangle *tr) = 0;
- virtual void StartFile(void) = 0;
- virtual void FinishAndCloseFile(void) = 0;
- virtual bool HasCanvasSize(void) = 0;
+ virtual bool OutputConstraints(IdList<Constraint,hConstraint> *) { return false; }
+ virtual void Background(RgbaColor color) = 0;
+ virtual void StartFile() = 0;
+ virtual void FinishAndCloseFile() = 0;
+ virtual bool HasCanvasSize() const = 0;
+ virtual bool CanOutputMesh() const = 0;
};
class DxfFileWriter : public VectorFileWriter {
public:
std::vector<BezierPath> paths;
IdList<Constraint,hConstraint> *constraint;
- bool OutputConstraints(IdList<Constraint,hConstraint> *constraint);
+ static const char *lineTypeName(StipplePattern stippleType);
+
+ bool OutputConstraints(IdList<Constraint,hConstraint> *constraint) override;
void StartPath( RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
void FinishPath(RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
- void Triangle(STriangle *tr);
- void Bezier(SBezier *sb);
- void StartFile(void);
- void FinishAndCloseFile(void);
- bool HasCanvasSize(void) { return false; }
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
+ void Triangle(STriangle *tr) override;
+ void Bezier(SBezier *sb) override;
+ void Background(RgbaColor color) override;
+ void StartFile() override;
+ void FinishAndCloseFile() override;
+ bool HasCanvasSize() const override { return false; }
+ bool CanOutputMesh() const override { return false; }
bool NeedToOutput(Constraint *c);
-
- static const char *lineTypeName(int stippleType);
-
};
class EpsFileWriter : public VectorFileWriter {
public:
void MaybeMoveTo(Vector s, Vector f);
void StartPath( RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
void FinishPath(RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
- void Triangle(STriangle *tr);
- void Bezier(SBezier *sb);
- void StartFile(void);
- void FinishAndCloseFile(void);
- bool HasCanvasSize(void) { return true; }
- bool CanOutputMesh() const { return true; }
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
+ void Triangle(STriangle *tr) override;
+ void Bezier(SBezier *sb) override;
+ void Background(RgbaColor color) override;
+ void StartFile() override;
+ void FinishAndCloseFile() override;
+ bool HasCanvasSize() const override { return true; }
+ bool CanOutputMesh() const override { return true; }
};
class PdfFileWriter : public VectorFileWriter {
public:
void MaybeMoveTo(Vector s, Vector f);
void StartPath( RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
void FinishPath(RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
- void Triangle(STriangle *tr);
- void Bezier(SBezier *sb);
- void StartFile(void);
- void FinishAndCloseFile(void);
- bool HasCanvasSize(void) { return true; }
- bool CanOutputMesh() const { return true; }
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
+ void Triangle(STriangle *tr) override;
+ void Bezier(SBezier *sb) override;
+ void Background(RgbaColor color) override;
+ void StartFile() override;
+ void FinishAndCloseFile() override;
+ bool HasCanvasSize() const override { return true; }
+ bool CanOutputMesh() const override { return true; }
};
class SvgFileWriter : public VectorFileWriter {
public:
void MaybeMoveTo(Vector s, Vector f);
void StartPath( RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
void FinishPath(RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
- void Triangle(STriangle *tr);
- void Bezier(SBezier *sb);
- void StartFile(void);
- void FinishAndCloseFile(void);
- bool HasCanvasSize(void) { return true; }
- bool CanOutputMesh() const { return true; }
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
+ void Triangle(STriangle *tr) override;
+ void Bezier(SBezier *sb) override;
+ void Background(RgbaColor color) override;
+ void StartFile() override;
+ void FinishAndCloseFile() override;
+ bool HasCanvasSize() const override { return true; }
+ bool CanOutputMesh() const override { return true; }
};
class HpglFileWriter : public VectorFileWriter {
public:
static double MmToHpglUnits(double mm);
void StartPath( RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
void FinishPath(RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
- void Triangle(STriangle *tr);
- void Bezier(SBezier *sb);
- void StartFile(void);
- void FinishAndCloseFile(void);
- bool HasCanvasSize(void) { return false; }
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
+ void Triangle(STriangle *tr) override;
+ void Bezier(SBezier *sb) override;
+ void Background(RgbaColor color) override;
+ void StartFile() override;
+ void FinishAndCloseFile() override;
+ bool HasCanvasSize() const override { return false; }
+ bool CanOutputMesh() const override { return false; }
};
class Step2dFileWriter : public VectorFileWriter {
StepFileWriter sfw;
void StartPath( RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
void FinishPath(RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
- void Triangle(STriangle *tr);
- void Bezier(SBezier *sb);
- void StartFile(void);
- void FinishAndCloseFile(void);
- bool HasCanvasSize(void) { return false; }
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
+ void Triangle(STriangle *tr) override;
+ void Bezier(SBezier *sb) override;
+ void Background(RgbaColor color) override;
+ void StartFile() override;
+ void FinishAndCloseFile() override;
+ bool HasCanvasSize() const override { return false; }
+ bool CanOutputMesh() const override { return false; }
};
class GCodeFileWriter : public VectorFileWriter {
public:
SEdgeList sel;
void StartPath( RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
void FinishPath(RgbaColor strokeRgb, double lineWidth,
- bool filled, RgbaColor fillRgb, hStyle hs);
- void Triangle(STriangle *tr);
- void Bezier(SBezier *sb);
- void StartFile(void);
- void FinishAndCloseFile(void);
- bool HasCanvasSize(void) { return false; }
+ bool filled, RgbaColor fillRgb, hStyle hs) override;
+ void Triangle(STriangle *tr) override;
+ void Bezier(SBezier *sb) override;
+ void Background(RgbaColor color) override;
+ void StartFile() override;
+ void FinishAndCloseFile() override;
+ bool HasCanvasSize() const override { return false; }
+ bool CanOutputMesh() const override { return false; }
};
#ifdef LIBRARY
inline Group *GetGroup (hGroup h) { return group. FindById(h); }
// Styles are handled a bit differently.
- void Clear(void);
+ void Clear();
BBox CalculateEntityBBox(bool includingInvisible);
Group *GetRunningMeshGroupFor(hGroup h);
GraphicsWindow GW;
// The state for undo/redo
- typedef struct {
+ typedef struct UndoState {
IdList<Group,hGroup> group;
List<hGroup> groupOrder;
IdList<Request,hRequest> request;
IdList<Style,hStyle> style;
hGroup activeGroup;
- void Clear(void) {
+ void Clear() {
group.Clear();
request.Clear();
constraint.Clear();
style.Clear();
}
} UndoState;
- enum { MAX_UNDO = 16 };
+ enum { MAX_UNDO = 100 };
typedef struct {
UndoState d[MAX_UNDO];
int cnt;
} UndoStack;
UndoStack undo;
UndoStack redo;
- void UndoEnableMenus(void);
- void UndoRemember(void);
- void UndoUndo(void);
- void UndoRedo(void);
+
+ std::map<Platform::Path, std::shared_ptr<Pixmap>, Platform::PathLess> images;
+ bool ReloadLinkedImage(const Platform::Path &saveFile, Platform::Path *filename,
+ bool canCancel);
+
+ void UndoEnableMenus();
+ void UndoRemember();
+ void UndoUndo();
+ void UndoRedo();
void PushFromCurrentOnto(UndoStack *uk);
void PopOntoCurrentFrom(UndoStack *uk);
void UndoClearState(UndoState *ut);
int maxSegments;
double exportChordTol;
int exportMaxSegments;
+ int timeoutRedundantConstr; //milliseconds
double cameraTangent;
- float gridSpacing;
- float exportScale;
- float exportOffset;
+ double gridSpacing;
+ double exportScale;
+ double exportOffset;
bool fixExportColors;
+ bool exportBackgroundColor;
bool drawBackFaces;
+ bool showContourAreas;
bool checkClosedContour;
+ bool turntableNav;
+ bool immediatelyEditDimension;
+ bool automaticLineConstraints;
bool showToolbar;
+ Platform::Path screenshotFile;
RgbaColor backgroundColor;
bool exportShadedTriangles;
bool exportPwlCurves;
bool exportCanvasSizeAuto;
bool exportMode;
struct {
- float left;
- float right;
- float bottom;
- float top;
+ double left;
+ double right;
+ double bottom;
+ double top;
} exportMargin;
struct {
- float width;
- float height;
- float dx;
- float dy;
+ double width;
+ double height;
+ double dx;
+ double dy;
} exportCanvas;
struct {
- float depth;
+ double depth;
int passes;
- float feed;
- float plungeFeed;
+ double feed;
+ double plungeFeed;
} gCode;
- typedef enum {
- UNIT_MM = 0,
- UNIT_INCHES
- } Unit;
Unit viewUnits;
int afterDecimalMm;
int afterDecimalInch;
+ int afterDecimalDegree;
+ bool useSIPrefixes;
int autosaveInterval; // in minutes
std::string MmToString(double v);
+ std::string MmToStringSI(double v, int dim = 0);
+ std::string DegreeToString(double v);
double ExprToMm(Expr *e);
double StringToMm(const std::string &s);
- const char *UnitName(void);
- double MmPerUnit(void);
- int UnitDigitsAfterDecimal(void);
+ const char *UnitName();
+ double MmPerUnit();
+ int UnitDigitsAfterDecimal();
void SetUnitDigitsAfterDecimal(int v);
- double ChordTolMm(void);
- double ExportChordTolMm(void);
- int GetMaxSegments(void);
+ double ChordTolMm();
+ double ExportChordTolMm();
+ int GetMaxSegments();
bool usePerspectiveProj;
- double CameraTangent(void);
+ double CameraTangent();
// Some stuff relating to the tangent arcs created non-parametrically
// as special requests.
double tangentArcRadius;
bool tangentArcManual;
- bool tangentArcDeleteOld;
+ bool tangentArcModify;
// The platform-dependent code calls this before entering the msg loop
- void Init(void);
- bool OpenFile(const std::string &filename);
- void Exit(void);
+ void Init();
+ void Exit();
// File load/save routines, including the additional files that get
// loaded when we have link groups.
FILE *fh;
- void AfterNewFile(void);
- static void RemoveFromRecentList(const std::string &filename);
- static void AddToRecentList(const std::string &filename);
- std::string saveFile;
+ void AfterNewFile();
+ void AddToRecentList(const Platform::Path &filename);
+ Platform::Path saveFile;
bool fileLoadError;
bool unsaved;
typedef struct {
void *ptr;
} SaveTable;
static const SaveTable SAVED[];
- void SaveUsingTable(int type);
- void LoadUsingTable(char *key, char *val);
+ void SaveUsingTable(const Platform::Path &filename, int type);
+ void LoadUsingTable(const Platform::Path &filename, char *key, char *val);
struct {
Group g;
Request r;
Constraint c;
Style s;
} sv;
- static void MenuFile(int id);
- bool Autosave();
+ static void MenuFile(Command id);
+ void Autosave();
void RemoveAutosave();
+ static constexpr size_t MAX_RECENT = 8;
+ static constexpr const char *SKETCH_EXT = "slvs";
+ static constexpr const char *BACKUP_EXT = "slvs~";
+ std::vector<Platform::Path> recentFiles;
+ bool Load(const Platform::Path &filename);
bool GetFilenameAndSave(bool saveAs);
- bool OkayToStartNewFile(void);
- hGroup CreateDefaultDrawingGroup(void);
- void UpdateWindowTitle(void);
- void ClearExisting(void);
- void NewFile(void);
- bool SaveToFile(const std::string &filename);
- bool LoadAutosaveFor(const std::string &filename);
- bool LoadFromFile(const std::string &filename);
- bool LoadEntitiesFromFile(const std::string &filename, EntityList *le,
+ bool OkayToStartNewFile();
+ hGroup CreateDefaultDrawingGroup();
+ void UpdateWindowTitles();
+ void ClearExisting();
+ void NewFile();
+ bool SaveToFile(const Platform::Path &filename);
+ bool LoadAutosaveFor(const Platform::Path &filename);
+ bool LoadFromFile(const Platform::Path &filename, bool canCancel = false);
+ void UpgradeLegacyData();
+ bool LoadEntitiesFromFile(const Platform::Path &filename, EntityList *le,
SMesh *m, SShell *sh);
- bool ReloadAllImported(bool canCancel=false);
+ bool LoadEntitiesFromSlvs(const Platform::Path &filename, EntityList *le,
+ SMesh *m, SShell *sh);
+ bool ReloadAllLinked(const Platform::Path &filename, bool canCancel = false);
// And the various export options
- void ExportAsPngTo(const std::string &filename);
- void ExportMeshTo(const std::string &filename);
+ void ExportAsPngTo(const Platform::Path &filename);
+ void ExportMeshTo(const Platform::Path &filename);
void ExportMeshAsStlTo(FILE *f, SMesh *sm);
- void ExportMeshAsObjTo(FILE *f, SMesh *sm);
- void ExportMeshAsThreeJsTo(FILE *f, const std::string &filename,
- SMesh *sm, SEdgeList *sel);
- void ExportViewOrWireframeTo(const std::string &filename, bool wireframe);
- void ExportSectionTo(const std::string &filename);
+ void ExportMeshAsQ3doTo(FILE *f, SMesh *sm);
+ void ExportMeshAsObjTo(FILE *fObj, FILE *fMtl, SMesh *sm);
+ void ExportMeshAsThreeJsTo(FILE *f, const Platform::Path &filename,
+ SMesh *sm, SOutlineList *sol);
+ void ExportMeshAsVrmlTo(FILE *f, const Platform::Path &filename, SMesh *sm);
+ void ExportViewOrWireframeTo(const Platform::Path &filename, bool exportWireframe);
+ void ExportSectionTo(const Platform::Path &filename);
void ExportWireframeCurves(SEdgeList *sel, SBezierList *sbl,
VectorFileWriter *out);
void ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm,
- Vector u, Vector v, Vector n, Vector origin,
- double cameraTan,
+ Vector u, Vector v,
+ Vector n, Vector origin,
+ double cameraTan,
VectorFileWriter *out);
- static void MenuAnalyze(int id);
+ static void MenuAnalyze(Command id);
// Additional display stuff
struct {
Vector ptA;
Vector ptB;
} extraLine;
- struct {
- uint8_t *fromFile;
- int w, h;
- int rw, rh;
- double scale; // pixels per mm
- Vector origin;
- } bgImage;
struct {
bool draw, showOrigin;
Vector pt, u, v;
} justExportedInfo;
+ struct {
+ bool draw;
+ bool dirty;
+ Vector position;
+ } centerOfMass;
class Clipboard {
public:
List<ClipboardRequest> r;
List<Constraint> c;
- void Clear(void);
+ void Clear();
bool ContainsEntity(hEntity old);
hEntity NewEntityFor(hEntity old);
};
Clipboard clipboard;
- void MarkGroupDirty(hGroup hg);
+ void MarkGroupDirty(hGroup hg, bool onlyThis = false);
void MarkGroupDirtyByEntity(hEntity he);
// Consistency checking on the sketch: stuff with missing dependencies
int nonTrivialConstraints;
} deleted;
bool GroupExists(hGroup hg);
- bool PruneOrphans(void);
+ bool PruneOrphans();
bool EntityExists(hEntity he);
bool GroupsInOrder(hGroup before, hGroup after);
bool PruneGroups(hGroup hg);
bool PruneRequests(hGroup hg);
bool PruneConstraints(hGroup hg);
+ static void ShowNakedEdges(bool reportOnlyWhenNotOkay);
- enum GenerateType {
- GENERATE_DIRTY,
- GENERATE_ALL,
- GENERATE_REGEN,
- GENERATE_UNTIL_ACTIVE,
+ enum class Generate : uint32_t {
+ DIRTY,
+ ALL,
+ REGEN,
+ UNTIL_ACTIVE,
};
- void GenerateAll(GenerateType type = GENERATE_DIRTY, bool andFindFree = false,
+ void GenerateAll(Generate type = Generate::DIRTY, bool andFindFree = false,
bool genForBBox = false);
void SolveGroup(hGroup hg, bool andFindFree);
void SolveGroupAndReport(hGroup hg, bool andFindFree);
- void MarkDraggedParams(void);
- void ForceReferences(void);
+ SolveResult TestRankForGroup(hGroup hg, int *rank = NULL);
+ void WriteEqSystemForGroup(hGroup hg);
+ void MarkDraggedParams();
+ void ForceReferences();
+ void UpdateCenterOfMass();
- bool ActiveGroupsOkay(void);
+ bool ActiveGroupsOkay();
// The system to be solved.
System *pSys;
// the sketch!
bool allConsistent;
- struct {
- bool scheduled;
- bool showTW;
- bool generateAll;
- } later;
+ Platform::TimerRef showTWTimer;
+ Platform::TimerRef generateAllTimer;
+ Platform::TimerRef autosaveTimer;
void ScheduleShowTW();
void ScheduleGenerateAll();
- void DoLater(void);
+ void ScheduleAutosave();
- static void MenuHelp(int id);
+ static void MenuHelp(Command id);
- void Clear(void);
+ void Clear();
// We allocate TW and sys on the heap to work around an MSVC problem
// where it puts zero-initialized global data in the binary (~30M of zeroes)
// in release builds.
SolveSpaceUI()
- : pTW(new TextWindow({})), TW(*pTW),
- pSys(new System({})), sys(*pSys) {}
+ : pTW(new TextWindow()), TW(*pTW),
+ pSys(new System()), sys(*pSys) {}
~SolveSpaceUI() {
delete pTW;
}
};
-void ImportDxf(const std::string &file);
-void ImportDwg(const std::string &file);
+void ImportDxf(const Platform::Path &file);
+void ImportDwg(const Platform::Path &file);
+bool LinkIDF(const Platform::Path &filename, EntityList *le, SMesh *m, SShell *sh);
extern SolveSpaceUI SS;
extern Sketch SK;
-};
+}
#ifndef __OBJC__
using namespace SolveSpace;
//-----------------------------------------------------------------------------
-// Top-level functions to compute the Boolean union or difference between
-// two shells of rational polynomial surfaces.
+// Top-level functions to compute the Boolean union, difference or intersection
+// between two shells of rational polynomial surfaces.
//
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
static int I;
void SShell::MakeFromUnionOf(SShell *a, SShell *b) {
- MakeFromBoolean(a, b, AS_UNION);
+ MakeFromBoolean(a, b, SSurface::CombineAs::UNION);
}
void SShell::MakeFromDifferenceOf(SShell *a, SShell *b) {
- MakeFromBoolean(a, b, AS_DIFFERENCE);
+ MakeFromBoolean(a, b, SSurface::CombineAs::DIFFERENCE);
+}
+
+void SShell::MakeFromIntersectionOf(SShell *a, SShell *b) {
+ MakeFromBoolean(a, b, SSurface::CombineAs::INTERSECTION);
+}
+
+void SCurve::GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin) const {
+ *ptMax = {VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE};
+ *ptMin = {VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE};
+
+ for(int i = 0; i <= exact.deg; i++) {
+ exact.ctrl[i].MakeMaxMin(ptMax, ptMin);
+ }
+}
+
+// We will be inserting other curve verticies into our curves to split them.
+// This is helpful when curved surfaces become tangent along a trim and the
+// usual tests for curve-surface intersection don't split the curve at a vertex.
+// This is faster than the previous version that split at surface corners and
+// handles more buggy cases. It's not clear this is the best way but it works ok.
+static void FindVertsOnCurve(List<SInter> *l, const SCurve *curve, SShell *sh) {
+
+ Vector amax, amin;
+ curve->GetAxisAlignedBounding(&amax, &amin);
+
+ for(auto sc : sh->curve) {
+ if(!sc.isExact) continue;
+
+ Vector cmax, cmin;
+ sc.GetAxisAlignedBounding(&cmax, &cmin);
+
+ if(Vector::BoundingBoxesDisjoint(amax, amin, cmax, cmin)) {
+ // They cannot possibly intersect, no curves to generate
+ continue;
+ }
+
+ for(int i=0; i<2; i++) {
+ Vector pt = sc.exact.ctrl[ i==0 ? 0 : sc.exact.deg ];
+ double t;
+ curve->exact.ClosestPointTo(pt, &t, /*must converge=*/ false);
+ double d = pt.Minus(curve->exact.PointAt(t)).Magnitude();
+ if((t>LENGTH_EPS) && (t<(1.0-LENGTH_EPS)) && (d < LENGTH_EPS)) {
+ SInter inter;
+ inter.p = pt;
+ l->Add(&inter);
+ }
+ }
+ }
}
//-----------------------------------------------------------------------------
// the intersection of srfA and srfB.) Return a new pwl curve with everything
// split.
//-----------------------------------------------------------------------------
-static Vector LineStart, LineDirection;
-static int ByTAlongLine(const void *av, const void *bv)
-{
- SInter *a = (SInter *)av,
- *b = (SInter *)bv;
-
- double ta = (a->p.Minus(LineStart)).DivPivoting(LineDirection),
- tb = (b->p.Minus(LineStart)).DivPivoting(LineDirection);
-
- return (ta > tb) ? 1 : -1;
-}
SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
- SSurface *srfA, SSurface *srfB)
+ SSurface *srfA, SSurface *srfB) const
{
SCurve ret;
ret = *this;
ret.pts = {};
- SCurvePt *p = pts.First();
- if(!p) oops();
+ // First find any vertex that lies on our curve.
+ List<SInter> vertpts = {};
+ if(isExact) {
+ if(agnstA)
+ FindVertsOnCurve(&vertpts, this, agnstA);
+ if(agnstB)
+ FindVertsOnCurve(&vertpts, this, agnstB);
+ }
+
+ const SCurvePt *p = pts.First();
+ ssassert(p != NULL, "Cannot split an empty curve");
SCurvePt prev = *p;
ret.pts.Add(p);
p = pts.NextAfter(p);
-
+
for(; p; p = pts.NextAfter(p)) {
List<SInter> il = {};
// Find all the intersections with the two passed shells
if(agnstA)
- agnstA->AllPointsIntersecting(prev.p, p->p, &il, true, false, true);
+ agnstA->AllPointsIntersecting(prev.p, p->p, &il,
+ /*asSegment=*/true, /*trimmed=*/false, /*inclTangent=*/true);
if(agnstB)
- agnstB->AllPointsIntersecting(prev.p, p->p, &il, true, false, true);
+ agnstB->AllPointsIntersecting(prev.p, p->p, &il,
+ /*asSegment=*/true, /*trimmed=*/false, /*inclTangent=*/true);
- if(il.n > 0) {
+ if(!il.IsEmpty()) {
// The intersections were generated by intersecting the pwl
// edge against a surface; so they must be refined to lie
// exactly on the original curve.
}
Point2d puv;
- (pi->srf)->ClosestPointTo(pi->p, &puv, false);
+ (pi->srf)->ClosestPointTo(pi->p, &puv, /*mustConverge=*/false);
// Split the edge if the intersection lies within the surface's
// trim curves, or within the chord tol of the trim curve; want
// some slop if points are close to edge and pwl is too coarse,
// and it doesn't hurt to split unnecessarily.
Point2d dummy = { 0, 0 };
- int c = (pi->srf->bsp) ? pi->srf->bsp->ClassifyPoint(puv, dummy, pi->srf) : SBspUv::OUTSIDE;
- if(c == SBspUv::OUTSIDE) {
+ SBspUv::Class c = (pi->srf->bsp) ? pi->srf->bsp->ClassifyPoint(puv, dummy, pi->srf) : SBspUv::Class::OUTSIDE;
+ if(c == SBspUv::Class::OUTSIDE) {
double d = VERY_POSITIVE;
if(pi->srf->bsp) d = pi->srf->bsp->MinimumDistanceToEdge(puv, pi->srf);
if(d > SS.ChordTolMm()) {
}
}
- // We're keeping the intersection, so actually refine it.
- (pi->srf)->PointOnSurfaces(srfA, srfB, &(puv.x), &(puv.y));
+ // We're keeping the intersection, so actually refine it. Finding the intersection
+ // to within EPS is important to match the ends of different chopped trim curves.
+ // The general 3-surface intersection fails to refine for trims where surfaces
+ // are tangent at the curve, but those trims are usually exact, so…
+ if(isExact) {
+ (pi->srf)->PointOnCurve(&exact, &(puv.x), &(puv.y));
+ } else {
+ (pi->srf)->PointOnSurfaces(srfA, srfB, &(puv.x), &(puv.y));
+ }
pi->p = (pi->srf)->PointAt(puv);
}
il.RemoveTagged();
+ }
+ // Now add any vertex that is on this segment
+ const Vector lineStart = prev.p;
+ const Vector lineDirection = (p->p).Minus(prev.p);
+ for(auto vtx : vertpts) {
+ double t = (vtx.p.Minus(lineStart)).DivProjected(lineDirection);
+ if((0.0 < t) && (t < 1.0)) {
+ il.Add(&vtx);
+ }
+ }
+ if(!il.IsEmpty()) {
+ SInter *pi;
// And now sort them in order along the line. Note that we must
// do that after refining, in case the refining would make two
// points switch places.
- LineStart = prev.p;
- LineDirection = (p->p).Minus(prev.p);
- qsort(il.elem, il.n, sizeof(il.elem[0]), ByTAlongLine);
+ std::sort(il.begin(), il.end(), [&](const SInter &a, const SInter &b) {
+ double ta = (a.p.Minus(lineStart)).DivProjected(lineDirection);
+ double tb = (b.p.Minus(lineStart)).DivProjected(lineDirection);
+
+ return (ta < tb);
+ });
// And now uses the intersections to generate our split pwl edge(s)
Vector prev = Vector::From(VERY_POSITIVE, 0, 0);
ret.pts.Add(p);
prev = *p;
}
+ vertpts.Clear();
return ret;
}
void SShell::CopyCurvesSplitAgainst(bool opA, SShell *agnst, SShell *into) {
- SCurve *sc;
- for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
+#pragma omp parallel for
+ for(int i=0; i<curve.n; i++) {
+ SCurve *sc = &curve[i];
SCurve scn = sc->MakeCopySplitAgainst(agnst, NULL,
surface.FindById(sc->surfA),
surface.FindById(sc->surfB));
- scn.source = opA ? SCurve::FROM_A : SCurve::FROM_B;
-
- hSCurve hsc = into->curve.AddAndAssignId(&scn);
- // And note the new ID so that we can rewrite the trims appropriately
- sc->newH = hsc;
+ scn.source = opA ? SCurve::Source::A : SCurve::Source::B;
+#pragma omp critical
+ {
+ hSCurve hsc = into->curve.AddAndAssignId(&scn);
+ // And note the new ID so that we can rewrite the trims appropriately
+ sc->newH = hsc;
+ }
}
}
}
}
-static bool KeepRegion(int type, bool opA, int shell, int orig)
+static bool KeepRegion(SSurface::CombineAs type, bool opA, SShell::Class shell, SShell::Class orig)
{
- bool inShell = (shell == SShell::INSIDE),
- inSame = (shell == SShell::COINC_SAME),
- inOpp = (shell == SShell::COINC_OPP),
- inOrig = (orig == SShell::INSIDE);
+ bool inShell = (shell == SShell::Class::INSIDE),
+ outSide = (shell == SShell::Class::OUTSIDE),
+ inSame = (shell == SShell::Class::COINC_SAME),
+ inOrig = (orig == SShell::Class::INSIDE);
- bool inFace = inSame || inOpp;
-
- // If these are correct, then they should be independent of inShell
- // if inFace is true.
if(!inOrig) return false;
switch(type) {
- case SShell::AS_UNION:
+ case SSurface::CombineAs::UNION:
if(opA) {
- return (!inShell && !inFace);
+ return outSide;
} else {
- return (!inShell && !inFace) || inSame;
+ return outSide || inSame;
}
- case SShell::AS_DIFFERENCE:
+ case SSurface::CombineAs::DIFFERENCE:
if(opA) {
- return (!inShell && !inFace);
+ return outSide;
} else {
- return (inShell && !inFace) || inSame;
+ return inShell || inSame;
}
- default: oops();
+ case SSurface::CombineAs::INTERSECTION:
+ if(opA) {
+ return inShell;
+ } else {
+ return inShell || inSame;
+ }
+
+ default: ssassert(false, "Unexpected combine type");
}
}
-static bool KeepEdge(int type, bool opA,
- int indir_shell, int outdir_shell,
- int indir_orig, int outdir_orig)
+static bool KeepEdge(SSurface::CombineAs type, bool opA,
+ SShell::Class indir_shell, SShell::Class outdir_shell,
+ SShell::Class indir_orig, SShell::Class outdir_orig)
{
bool keepIn = KeepRegion(type, opA, indir_shell, indir_orig),
keepOut = KeepRegion(type, opA, outdir_shell, outdir_orig);
return false;
}
-static void TagByClassifiedEdge(int bspclass, int *indir, int *outdir)
+static void TagByClassifiedEdge(SBspUv::Class bspclass, SShell::Class *indir, SShell::Class *outdir)
{
switch(bspclass) {
- case SBspUv::INSIDE:
- *indir = SShell::INSIDE;
- *outdir = SShell::INSIDE;
+ case SBspUv::Class::INSIDE:
+ *indir = SShell::Class::INSIDE;
+ *outdir = SShell::Class::INSIDE;
break;
- case SBspUv::OUTSIDE:
- *indir = SShell::OUTSIDE;
- *outdir = SShell::OUTSIDE;
+ case SBspUv::Class::OUTSIDE:
+ *indir = SShell::Class::OUTSIDE;
+ *outdir = SShell::Class::OUTSIDE;
break;
- case SBspUv::EDGE_PARALLEL:
- *indir = SShell::INSIDE;
- *outdir = SShell::OUTSIDE;
+ case SBspUv::Class::EDGE_PARALLEL:
+ *indir = SShell::Class::INSIDE;
+ *outdir = SShell::Class::OUTSIDE;
break;
- case SBspUv::EDGE_ANTIPARALLEL:
- *indir = SShell::OUTSIDE;
- *outdir = SShell::INSIDE;
+ case SBspUv::Class::EDGE_ANTIPARALLEL:
+ *indir = SShell::Class::OUTSIDE;
+ *outdir = SShell::Class::INSIDE;
break;
default:
dbp("TagByClassifiedEdge: fail!");
- *indir = SShell::OUTSIDE;
- *outdir = SShell::OUTSIDE;
+ *indir = SShell::Class::OUTSIDE;
+ *outdir = SShell::Class::OUTSIDE;
break;
}
}
void SSurface::FindChainAvoiding(SEdgeList *src, SEdgeList *dest,
SPointList *avoid)
{
- if(src->l.n < 1) oops();
+ ssassert(!src->l.IsEmpty(), "Need at least one edge");
// Start with an arbitrary edge.
- dest->l.Add(&(src->l.elem[0]));
+ dest->l.Add(src->l.First());
src->l.ClearTags();
- src->l.elem[0].tag = 1;
+ src->l.First()->tag = 1;
bool added;
do {
added = false;
// The start and finish of the current edge chain
- Vector s = dest->l.elem[0].a,
- f = dest->l.elem[dest->l.n - 1].b;
+ Vector s = dest->l.First()->a,
+ f = dest->l.Last()->b;
// We can attach a new edge at the start or finish, as long as that
// start or finish point isn't in the list of points to avoid.
SCurve *sc = shell->curve.FindById(hc);
if(sc->isExact && sc->exact.deg != 1) {
double t;
- sc->exact.ClosestPointTo(*pt, &t, false);
+ sc->exact.ClosestPointTo(*pt, &t, /*mustConverge=*/false);
*pt = sc->exact.PointAt(t);
ClosestPointTo(*pt, &muv);
} else if(!sc->isExact) {
SSurface *trimmedA = sc->GetSurfaceA(sha, shb),
- *trimmedB = sc->GetSurfaceB(sha, shb);
+ *trimmedB = sc->GetSurfaceB(sha, shb);
*pt = trimmedA->ClosestPointOnThisAndSurface(trimmedB, *pt);
ClosestPointTo(*pt, &muv);
}
enxyz = (ab.Cross(*surfn)).WithMagnitude(SS.ChordTolMm());
// And based on that, compute the edge's inner normal in uv space. This
// vector is perpendicular to the edge in xyz, but not necessarily in uv.
- Vector tu, tv;
+ Vector tu, tv, tx, ty;
TangentsAt(muv.x, muv.y, &tu, &tv);
+ Vector n = tu.Cross(tv);
+ // since tu and tv may not be orthogonal, use y in place of v, x in place of u.
+ // |y| = |v|sin(theta) where theta is the angle between tu and tv.
+ ty = n.Cross(tu).ScaledBy(1.0/tu.MagSquared());
+ tx = tv.Cross(n).ScaledBy(1.0/tv.MagSquared());
+
Point2d enuv;
- enuv.x = enxyz.Dot(tu) / tu.MagSquared();
- enuv.y = enxyz.Dot(tv) / tv.MagSquared();
+ enuv.x = enxyz.Dot(tx) / tx.MagSquared();
+ enuv.y = enxyz.Dot(ty) / ty.MagSquared();
// Compute the inner and outer normals of this edge (within the srf),
// in xyz space. These are not necessarily antiparallel, if the
SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
SShell *sha, SShell *shb,
SShell *into,
- int type)
+ SSurface::CombineAs type,
+ int dbg_index)
{
bool opA = (parent == sha);
SShell *agnst = opA ? shb : sha;
ret.trim.Add(&stn);
}
- if(type == SShell::AS_DIFFERENCE && !opA) {
+ if(type == SSurface::CombineAs::DIFFERENCE && !opA) {
// The second operand of a Boolean difference gets turned inside out
ret.Reverse();
}
// be changed if we just flipped the surface normal, and we are using
// the split curves (not the original curves).
SEdgeList orig = {};
- ret.MakeEdgesInto(into, &orig, AS_UV);
+ ret.MakeEdgesInto(into, &orig, MakeAs::UV);
ret.trim.Clear();
// which means that we can't necessarily use the old BSP...
SBspUv *origBsp = SBspUv::From(&orig, &ret);
SEdgeList inter = {};
SSurface *ss;
- for(ss = agnst->surface.First(); ss; ss = agnst->surface.NextAfter(ss)) {
- SCurve *sc;
- for(sc = into->curve.First(); sc; sc = into->curve.NextAfter(sc)) {
- if(sc->source != SCurve::FROM_INTERSECTION) continue;
- if(opA) {
- if(sc->surfA.v != h.v || sc->surfB.v != ss->h.v) continue;
- } else {
- if(sc->surfB.v != h.v || sc->surfA.v != ss->h.v) continue;
- }
-
- int i;
- for(i = 1; i < sc->pts.n; i++) {
- Vector a = sc->pts.elem[i-1].p,
- b = sc->pts.elem[i].p;
-
- Point2d auv, buv;
- ss->ClosestPointTo(a, &(auv.x), &(auv.y));
- ss->ClosestPointTo(b, &(buv.x), &(buv.y));
-
- int c = (ss->bsp) ? ss->bsp->ClassifyEdge(auv, buv, ss) : SBspUv::OUTSIDE;
- if(c != SBspUv::OUTSIDE) {
- Vector ta = Vector::From(0, 0, 0);
- Vector tb = Vector::From(0, 0, 0);
- ret.ClosestPointTo(a, &(ta.x), &(ta.y));
- ret.ClosestPointTo(b, &(tb.x), &(tb.y));
-
- Vector tn = ret.NormalAt(ta.x, ta.y);
- Vector sn = ss->NormalAt(auv.x, auv.y);
-
- // We are subtracting the portion of our surface that
- // lies in the shell, so the in-plane edge normal should
- // point opposite to the surface normal.
- bool bkwds = true;
- if((tn.Cross(b.Minus(a))).Dot(sn) < 0) bkwds = !bkwds;
- if(type == SShell::AS_DIFFERENCE && !opA) bkwds = !bkwds;
- if(bkwds) {
- inter.AddEdge(tb, ta, sc->h.v, 1);
- } else {
- inter.AddEdge(ta, tb, sc->h.v, 0);
- }
+ SCurve *sc;
+ for(sc = into->curve.First(); sc; sc = into->curve.NextAfter(sc)) {
+ if(sc->source != SCurve::Source::INTERSECTION) continue;
+ if(opA) {
+ if(sc->surfA != h) continue;
+ ss = shb->surface.FindById(sc->surfB);
+ } else {
+ if(sc->surfB != h) continue;
+ ss = sha->surface.FindById(sc->surfA);
+ }
+ int i;
+ for(i = 1; i < sc->pts.n; i++) {
+ Vector a = sc->pts[i-1].p,
+ b = sc->pts[i].p;
+
+ Point2d auv, buv;
+ ss->ClosestPointTo(a, &(auv.x), &(auv.y));
+ ss->ClosestPointTo(b, &(buv.x), &(buv.y));
+
+ SBspUv::Class c = (ss->bsp) ? ss->bsp->ClassifyEdge(auv, buv, ss) : SBspUv::Class::OUTSIDE;
+ if(c != SBspUv::Class::OUTSIDE) {
+ Vector ta = Vector::From(0, 0, 0);
+ Vector tb = Vector::From(0, 0, 0);
+ ret.ClosestPointTo(a, &(ta.x), &(ta.y));
+ ret.ClosestPointTo(b, &(tb.x), &(tb.y));
+
+ Vector tn = ret.NormalAt(ta.x, ta.y);
+ Vector sn = ss->NormalAt(auv.x, auv.y);
+
+ // We are subtracting the portion of our surface that
+ // lies in the shell, so the in-plane edge normal should
+ // point opposite to the surface normal.
+ bool bkwds = true;
+ if((tn.Cross(b.Minus(a))).Dot(sn) < 0) bkwds = !bkwds;
+ if((type == SSurface::CombineAs::DIFFERENCE && !opA) ||
+ (type == SSurface::CombineAs::INTERSECTION)) { // Invert all newly created edges for intersection
+ bkwds = !bkwds;
+ }
+ if(bkwds) {
+ inter.AddEdge(tb, ta, sc->h.v, 1);
+ } else {
+ inter.AddEdge(ta, tb, sc->h.v, 0);
}
}
}
// our original and intersecting edge lists.
SEdgeList final = {};
- while(orig.l.n > 0) {
+ while(!orig.l.IsEmpty()) {
SEdgeList chain = {};
FindChainAvoiding(&orig, &chain, &choosing);
// Arbitrarily choose an edge within the chain to classify; they
// should all be the same, though.
- se = &(chain.l.elem[chain.l.n/2]);
+ se = &(chain.l[chain.l.n/2]);
Point2d auv = (se->a).ProjectXy(),
buv = (se->b).ProjectXy();
ret.EdgeNormalsWithinSurface(auv, buv, &pt, &enin, &enout, &surfn,
se->auxA, into, sha, shb);
- int indir_shell, outdir_shell, indir_orig, outdir_orig;
+ SShell::Class indir_shell, outdir_shell, indir_orig, outdir_orig;
- indir_orig = SShell::INSIDE;
- outdir_orig = SShell::OUTSIDE;
+ indir_orig = SShell::Class::INSIDE;
+ outdir_orig = SShell::Class::OUTSIDE;
agnst->ClassifyEdge(&indir_shell, &outdir_shell,
ret.PointAt(auv), ret.PointAt(buv), pt,
chain.Clear();
}
- while(inter.l.n > 0) {
+ while(!inter.l.IsEmpty()) {
SEdgeList chain = {};
FindChainAvoiding(&inter, &chain, &choosing);
// Any edge in the chain, same as above.
- se = &(chain.l.elem[chain.l.n/2]);
+ se = &(chain.l[chain.l.n/2]);
Point2d auv = (se->a).ProjectXy(),
buv = (se->b).ProjectXy();
ret.EdgeNormalsWithinSurface(auv, buv, &pt, &enin, &enout, &surfn,
se->auxA, into, sha, shb);
- int indir_shell, outdir_shell, indir_orig, outdir_orig;
+ SShell::Class indir_shell, outdir_shell, indir_orig, outdir_orig;
- int c_this = (origBsp) ? origBsp->ClassifyEdge(auv, buv, &ret) : SBspUv::OUTSIDE;
+ SBspUv::Class c_this = (origBsp) ? origBsp->ClassifyEdge(auv, buv, &ret) : SBspUv::Class::OUTSIDE;
TagByClassifiedEdge(c_this, &indir_orig, &outdir_orig);
agnst->ClassifyEdge(&indir_shell, &outdir_shell,
// we can get duplicate edges if our surface intersects the other shell
// at an edge, so that both surfaces intersect coincident (and both
// generate an intersection edge).
- final.CullExtraneousEdges();
+ final.CullExtraneousEdges(/*both=*/true);
// Use our reassembled edges to trim the new surface.
- ret.TrimFromEdgeList(&final, true);
+ ret.TrimFromEdgeList(&final, /*asUv=*/true);
SPolygon poly = {};
final.l.ClearTags();
- if(!final.AssemblePolygon(&poly, NULL, true)) {
+ if(!final.AssemblePolygon(&poly, NULL, /*keepDir=*/true))
+#pragma omp critical
+ {
into->booleanFailed = true;
- dbp("failed: I=%d, avoid=%d", I, choosing.l.n);
+ dbp("failed: I=%d, avoid=%d", I+dbg_index, choosing.l.n);
DEBUGEDGELIST(&final, &ret);
}
poly.Clear();
return ret;
}
-void SShell::CopySurfacesTrimAgainst(SShell *sha, SShell *shb, SShell *into,
- int type)
-{
- SSurface *ss;
- for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
- SSurface ssn;
- ssn = ss->MakeCopyTrimAgainst(this, sha, shb, into, type);
- ss->newH = into->surface.AddAndAssignId(&ssn);
- I++;
+void SShell::CopySurfacesTrimAgainst(SShell *sha, SShell *shb, SShell *into, SSurface::CombineAs type) {
+ std::vector <SSurface> ssn(surface.n);
+#pragma omp parallel for
+ for (int i = 0; i < surface.n; i++)
+ {
+ SSurface *ss = &surface[i];
+ ssn[i] = ss->MakeCopyTrimAgainst(this, sha, shb, into, type, i);
+ }
+
+ for (int i = 0; i < surface.n; i++)
+ {
+ surface[i].newH = into->surface.AddAndAssignId(&ssn[i]);
}
+ I += surface.n;
}
void SShell::MakeIntersectionCurvesAgainst(SShell *agnst, SShell *into) {
- SSurface *sa;
- for(sa = surface.First(); sa; sa = surface.NextAfter(sa)) {
+#pragma omp parallel for
+ for(int i = 0; i< surface.n; i++) {
+ SSurface *sa = &surface[i];
+
SSurface *sb;
for(sb = agnst->surface.First(); sb; sb = agnst->surface.NextAfter(sb)){
// Intersect every surface from our shell against every surface
}
}
-void SShell::CleanupAfterBoolean(void) {
+void SShell::CleanupAfterBoolean() {
SSurface *ss;
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
ss->edges.Clear();
// First, copy over all the curves. Note which shell (a or b) each curve
// came from, but assign it a new ID.
+ curve.ReserveMore(a->curve.n + b->curve.n);
SCurve *c, cn;
for(i = 0; i < 2; i++) {
ab = (i == 0) ? a : b;
for(c = ab->curve.First(); c; c = ab->curve.NextAfter(c)) {
cn = SCurve::FromTransformationOf(c, t, q, 1.0);
- cn.source = (i == 0) ? SCurve::FROM_A : SCurve::FROM_B;
+ cn.source = (i == 0) ? SCurve::Source::A : SCurve::Source::B;
// surfA and surfB are wrong now, and we can't fix them until
// we've assigned IDs to the surfaces. So we'll get that later.
c->newH = curve.AddAndAssignId(&cn);
}
// Likewise copy over all the surfaces.
+ surface.ReserveMore(a->surface.n + b->surface.n);
SSurface *s, sn;
for(i = 0; i < 2; i++) {
ab = (i == 0) ? a : b;
for(s = ab->surface.First(); s; s = ab->surface.NextAfter(s)) {
- sn = SSurface::FromTransformationOf(s, t, q, 1.0, true);
+ sn = SSurface::FromTransformationOf(s, t, q, 1.0, /*includingTrims=*/true);
// All the trim curve IDs get rewritten; we know the new handles
// to the curves since we recorded them in the previous step.
STrimBy *stb;
RewriteSurfaceHandlesForCurves(a, b);
}
-void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) {
+void SShell::MakeFromBoolean(SShell *a, SShell *b, SSurface::CombineAs type) {
booleanFailed = false;
a->MakeClassifyingBsps(NULL);
// Copy over all the original curves, splitting them so that a
// piecwise linear segment never crosses a surface from the other
// shell.
- a->CopyCurvesSplitAgainst(true, b, this);
- b->CopyCurvesSplitAgainst(false, a, this);
+ a->CopyCurvesSplitAgainst(/*opA=*/true, b, this);
+ b->CopyCurvesSplitAgainst(/*opA=*/false, a, this);
// Generate the intersection curves for each surface in A against all
// the surfaces in B (which is all of the intersection curves).
SCurve *sc;
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
SSurface *srfA = sc->GetSurfaceA(a, b),
- *srfB = sc->GetSurfaceB(a, b);
+ *srfB = sc->GetSurfaceB(a, b);
sc->RemoveShortSegments(srfA, srfB);
}
a->MakeClassifyingBsps(this);
b->MakeClassifyingBsps(this);
- if(b->surface.n == 0 || a->surface.n == 0) {
+ if(b->surface.IsEmpty() || a->surface.IsEmpty()) {
I = 1000000;
} else {
I = 0;
// All of the BSP routines that we use to perform and accelerate polygon ops.
//-----------------------------------------------------------------------------
void SShell::MakeClassifyingBsps(SShell *useCurvesFrom) {
- SSurface *ss;
- for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
- ss->MakeClassifyingBsp(this, useCurvesFrom);
+#pragma omp parallel for
+ for(int i = 0; i<surface.n; i++) {
+ surface[i].MakeClassifyingBsp(this, useCurvesFrom);
}
}
void SSurface::MakeClassifyingBsp(SShell *shell, SShell *useCurvesFrom) {
SEdgeList el = {};
- MakeEdgesInto(shell, &el, AS_UV, useCurvesFrom);
+ MakeEdgesInto(shell, &el, MakeAs::UV, useCurvesFrom);
bsp = SBspUv::From(&el, this);
el.Clear();
edges = {};
- MakeEdgesInto(shell, &edges, AS_XYZ, useCurvesFrom);
+ MakeEdgesInto(shell, &edges, MakeAs::XYZ, useCurvesFrom);
}
-SBspUv *SBspUv::Alloc(void) {
+SBspUv *SBspUv::Alloc() {
return (SBspUv *)AllocTemporary(sizeof(SBspUv));
}
-static int ByLength(const void *av, const void *bv)
-{
- SEdge *a = (SEdge *)av,
- *b = (SEdge *)bv;
-
- double la = (a->a).Minus(a->b).Magnitude(),
- lb = (b->a).Minus(b->b).Magnitude();
-
- // Sort in descending order, longest first. This improves numerical
- // stability for the normals.
- return (la < lb) ? 1 : -1;
-}
SBspUv *SBspUv::From(SEdgeList *el, SSurface *srf) {
SEdgeList work = {};
for(se = el->l.First(); se; se = el->l.NextAfter(se)) {
work.AddEdge(se->a, se->b, se->auxA, se->auxB);
}
- qsort(work.l.elem, work.l.n, sizeof(work.l.elem[0]), ByLength);
-
+ std::sort(work.l.begin(), work.l.end(), [](SEdge const &a, SEdge const &b) {
+ double la = (a.a).Minus(a.b).Magnitude(), lb = (b.a).Minus(b.b).Magnitude();
+ // Sort in descending order, longest first. This improves numerical
+ // stability for the normals.
+ return la > lb;
+ });
SBspUv *bsp = NULL;
for(se = work.l.First(); se; se = work.l.NextAfter(se)) {
bsp = InsertOrCreateEdge(bsp, (se->a).ProjectXy(), (se->b).ProjectXy(), srf);
// time we care about exact correctness is when we're very close to the line,
// which is when the linearization is accurate.
//-----------------------------------------------------------------------------
-void SBspUv::ScalePoints(Point2d *pt, Point2d *a, Point2d *b, SSurface *srf) {
+
+void SBspUv::ScalePoints(Point2d *pt, Point2d *a, Point2d *b, SSurface *srf) const {
Vector tu, tv;
srf->TangentsAt(pt->x, pt->y, &tu, &tv);
double mu = tu.Magnitude(), mv = tv.Magnitude();
a ->x *= mu; a ->y *= mv;
b ->x *= mu; b ->y *= mv;
}
+
double SBspUv::ScaledSignedDistanceToLine(Point2d pt, Point2d a, Point2d b,
- SSurface *srf)
+ SSurface *srf) const
{
ScalePoints(&pt, &a, &b, srf);
return pt.Dot(n) - d;
}
-double SBspUv::ScaledDistanceToLine(Point2d pt, Point2d a, Point2d b, bool seg,
- SSurface *srf)
+
+double SBspUv::ScaledDistanceToLine(Point2d pt, Point2d a, Point2d b, bool asSegment,
+ SSurface *srf) const
{
ScalePoints(&pt, &a, &b, srf);
- return pt.DistanceToLine(a, b, seg);
+ return pt.DistanceToLine(a, b, asSegment);
}
-SBspUv *SBspUv::InsertOrCreateEdge(SBspUv *where, const Point2d &ea, const Point2d &eb, SSurface *srf) {
+SBspUv *SBspUv::InsertOrCreateEdge(SBspUv *where, Point2d ea, Point2d eb, SSurface *srf) {
if(where == NULL) {
SBspUv *ret = Alloc();
ret->a = ea;
m->more = more;
more = m;
} else if(fabs(dea) < LENGTH_EPS) {
- // Point A lies on this lie, but point B does not
+ // Point A lies on this line, but point B does not
if(deb > 0) {
pos = InsertOrCreateEdge(pos, ea, eb, srf);
} else {
neg = InsertOrCreateEdge(neg, ea, eb, srf);
}
} else if(fabs(deb) < LENGTH_EPS) {
- // Point B lies on this lie, but point A does not
+ // Point B lies on this line, but point A does not
if(dea > 0) {
pos = InsertOrCreateEdge(pos, ea, eb, srf);
} else {
return;
}
-int SBspUv::ClassifyPoint(Point2d p, Point2d eb, SSurface *srf) {
-
+SBspUv::Class SBspUv::ClassifyPoint(Point2d p, Point2d eb, SSurface *srf) const {
double dp = ScaledSignedDistanceToLine(p, a, b, srf);
if(fabs(dp) < LENGTH_EPS) {
- SBspUv *f = this;
+ const SBspUv *f = this;
while(f) {
Point2d ba = (f->b).Minus(f->a);
- if(ScaledDistanceToLine(p, f->a, ba, true, srf) < LENGTH_EPS) {
- if(ScaledDistanceToLine(eb, f->a, ba, false, srf) < LENGTH_EPS){
+ if(ScaledDistanceToLine(p, f->a, ba, /*asSegment=*/true, srf) < LENGTH_EPS) {
+ if(ScaledDistanceToLine(eb, f->a, ba, /*asSegment=*/false, srf) < LENGTH_EPS){
if(ba.Dot(eb.Minus(p)) > 0) {
- return EDGE_PARALLEL;
+ return Class::EDGE_PARALLEL;
} else {
- return EDGE_ANTIPARALLEL;
+ return Class::EDGE_ANTIPARALLEL;
}
} else {
- return EDGE_OTHER;
+ return Class::EDGE_OTHER;
}
}
f = f->more;
}
// Pick arbitrarily which side to send it down, doesn't matter
- int c1 = neg ? neg->ClassifyPoint(p, eb, srf) : OUTSIDE;
- int c2 = pos ? pos->ClassifyPoint(p, eb, srf) : INSIDE;
+ Class c1 = neg ? neg->ClassifyPoint(p, eb, srf) : Class::OUTSIDE;
+ Class c2 = pos ? pos->ClassifyPoint(p, eb, srf) : Class::INSIDE;
if(c1 != c2) {
dbp("MISMATCH: %d %d %08x %08x", c1, c2, neg, pos);
}
return c1;
} else if(dp > 0) {
- return pos ? pos->ClassifyPoint(p, eb, srf) : INSIDE;
+ return pos ? pos->ClassifyPoint(p, eb, srf) : Class::INSIDE;
} else {
- return neg ? neg->ClassifyPoint(p, eb, srf) : OUTSIDE;
+ return neg ? neg->ClassifyPoint(p, eb, srf) : Class::OUTSIDE;
}
}
-int SBspUv::ClassifyEdge(Point2d ea, Point2d eb, SSurface *srf) {
- int ret = ClassifyPoint((ea.Plus(eb)).ScaledBy(0.5), eb, srf);
- if(ret == EDGE_OTHER) {
+SBspUv::Class SBspUv::ClassifyEdge(Point2d ea, Point2d eb, SSurface *srf) const {
+ SBspUv::Class ret = ClassifyPoint((ea.Plus(eb)).ScaledBy(0.5), eb, srf);
+ if(ret == Class::EDGE_OTHER) {
// Perhaps the edge is tangent at its midpoint (and we screwed up
// somewhere earlier and failed to split it); try a different
// point on the edge.
return ret;
}
-double SBspUv::MinimumDistanceToEdge(Point2d p, SSurface *srf) {
+double SBspUv::MinimumDistanceToEdge(Point2d p, SSurface *srf) const {
double dn = (neg) ? neg->MinimumDistanceToEdge(p, srf) : VERY_POSITIVE;
double dp = (pos) ? pos->MinimumDistanceToEdge(p, srf) : VERY_POSITIVE;
Point2d as = a, bs = b;
ScalePoints(&p, &as, &bs, srf);
- double d = p.DistanceToLine(as, bs.Minus(as), true);
+ double d = p.DistanceToLine(as, bs.Minus(as), /*asSegment=*/true);
return min(d, min(dn, dp));
}
p3.Project4d());
}
-Vector SBezier::Start(void) {
+Vector SBezier::Start() const {
return ctrl[0];
}
-Vector SBezier::Finish(void) {
+Vector SBezier::Finish() const {
return ctrl[deg];
}
-void SBezier::Reverse(void) {
+void SBezier::Reverse() {
int i;
for(i = 0; i < (deg+1)/2; i++) {
swap(ctrl[i], ctrl[deg-i]);
}
void SBezier::GetBoundingProjd(Vector u, Vector orig,
- double *umin, double *umax)
+ double *umin, double *umax) const
{
int i;
for(i = 0; i <= deg; i++) {
}
}
-SBezier SBezier::TransformedBy(Vector t, Quaternion q, double scale) {
+SBezier SBezier::TransformedBy(Vector t, Quaternion q, double scale) const {
SBezier ret = *this;
int i;
for(i = 0; i <= deg; i++) {
// Does this curve lie entirely within the specified plane? It does if all
// the control points lie in that plane.
//-----------------------------------------------------------------------------
-bool SBezier::IsInPlane(Vector n, double d) {
+bool SBezier::IsInPlane(Vector n, double d) const {
int i;
for(i = 0; i <= deg; i++) {
if(fabs((ctrl[i]).Dot(n) - d) > LENGTH_EPS) {
// Is this Bezier exactly the arc of a circle, projected along the specified
// axis? If yes, return that circle's center and radius.
//-----------------------------------------------------------------------------
-bool SBezier::IsCircle(Vector axis, Vector *center, double *r) {
+bool SBezier::IsCircle(Vector axis, Vector *center, double *r) const {
if(deg != 2) return false;
if(ctrl[1].DistanceToLine(ctrl[0], ctrl[2].Minus(ctrl[0])) < LENGTH_EPS) {
return true;
}
-bool SBezier::IsRational(void) {
+bool SBezier::IsRational() const {
int i;
for(i = 0; i <= deg; i++) {
if(fabs(weight[i] - 1) > LENGTH_EPS) return true;
// the new weights as required.
//-----------------------------------------------------------------------------
SBezier SBezier::InPerspective(Vector u, Vector v, Vector n,
- Vector origin, double cameraTan)
+ Vector origin, double cameraTan) const
{
Quaternion q = Quaternion::From(u, v);
q = q.Inverse();
return ret;
}
-bool SBezier::Equals(SBezier *b) {
+bool SBezier::Equals(SBezier *b) const {
// We just test of identical degree and control points, even though two
// curves could still be coincident (even sharing endpoints).
if(deg != b->deg) return false;
return true;
}
-void SBezierList::Clear(void) {
+void SBezierList::Clear() {
l.Clear();
}
//-----------------------------------------------------------------------------
// If our list contains multiple identical Beziers (in either forward or
-// reverse order), then cull them.
+// reverse order), then cull them. If both is true, both beziers are removed.
+// Otherwise only one of them is removed.
//-----------------------------------------------------------------------------
-void SBezierList::CullIdenticalBeziers(void) {
+void SBezierList::CullIdenticalBeziers(bool both) {
int i, j;
l.ClearTags();
for(i = 0; i < l.n; i++) {
- SBezier *bi = &(l.elem[i]), bir;
+ SBezier *bi = &(l[i]), bir;
bir = *bi;
bir.Reverse();
for(j = i + 1; j < l.n; j++) {
- SBezier *bj = &(l.elem[j]);
+ SBezier *bj = &(l[j]);
if(bj->Equals(bi) ||
bj->Equals(&bir))
{
- bi->tag = 1;
+ if (both) bi->tag = 1;
bj->tag = 1;
}
}
// curves. So this will screw up on tangencies and stuff, but otherwise should
// be fine.
//-----------------------------------------------------------------------------
-void SBezierList::AllIntersectionsWith(SBezierList *sblb, SPointList *spl) {
- SBezier *sba, *sbb;
- for(sba = l.First(); sba; sba = l.NextAfter(sba)) {
- for(sbb = sblb->l.First(); sbb; sbb = sblb->l.NextAfter(sbb)) {
+void SBezierList::AllIntersectionsWith(SBezierList *sblb, SPointList *spl) const {
+ for(const SBezier *sba = l.First(); sba; sba = l.NextAfter(sba)) {
+ for(const SBezier *sbb = sblb->l.First(); sbb; sbb = sblb->l.NextAfter(sbb)) {
sbb->AllIntersectionsWith(sba, spl);
}
}
}
-void SBezier::AllIntersectionsWith(SBezier *sbb, SPointList *spl) {
+void SBezier::AllIntersectionsWith(const SBezier *sbb, SPointList *spl) const {
SPointList splRaw = {};
SEdgeList sea, seb;
sea = {};
// Returns true if all the curves are coplanar, otherwise false.
//-----------------------------------------------------------------------------
bool SBezierList::GetPlaneContainingBeziers(Vector *p, Vector *u, Vector *v,
- Vector *notCoplanarAt)
+ Vector *notCoplanarAt) const
{
Vector pt, ptFar, ptOffLine, dp, n;
double farMax, offLineMax;
int i;
- SBezier *sb;
// Get any point on any Bezier; or an arbitrary point if list is empty.
- if(l.n > 0) {
- pt = l.elem[0].Start();
+ if(!l.IsEmpty()) {
+ pt = l[0].Start();
} else {
pt = Vector::From(0, 0, 0);
}
// Get the point farthest from our arbitrary point.
farMax = VERY_NEGATIVE;
- for(sb = l.First(); sb; sb = l.NextAfter(sb)) {
+ for(const SBezier *sb = l.First(); sb; sb = l.NextAfter(sb)) {
for(i = 0; i <= sb->deg; i++) {
double m = (pt.Minus(sb->ctrl[i])).Magnitude();
if(m > farMax) {
// Get the point farthest from the line between pt and ptFar
dp = ptFar.Minus(pt);
offLineMax = VERY_NEGATIVE;
- for(sb = l.First(); sb; sb = l.NextAfter(sb)) {
+ for(const SBezier *sb = l.First(); sb; sb = l.NextAfter(sb)) {
for(i = 0; i <= sb->deg; i++) {
double m = (sb->ctrl[i]).DistanceToLine(pt, dp);
if(m > offLineMax) {
n = u->Cross(*v);
n = n.WithMagnitude(1);
double d = p->Dot(n);
- for(sb = l.First(); sb; sb = l.NextAfter(sb)) {
+ for(const SBezier *sb = l.First(); sb; sb = l.NextAfter(sb)) {
for(i = 0; i <= sb->deg; i++) {
if(fabs(n.Dot(sb->ctrl[i]) - d) > LENGTH_EPS) {
if(notCoplanarAt) *notCoplanarAt = sb->ctrl[i];
if(sbl->l.n < 1) return loop;
sbl->l.ClearTags();
- SBezier *first = &(sbl->l.elem[0]);
+ SBezier *first = &(sbl->l[0]);
first->tag = 1;
loop.l.Add(first);
Vector start = first->Start();
sbl->l.RemoveTagged();
- while(sbl->l.n > 0 && !hanging.Equals(start)) {
+ while(!sbl->l.IsEmpty() && !hanging.Equals(start)) {
int i;
bool foundNext = false;
for(i = 0; i < sbl->l.n; i++) {
- SBezier *test = &(sbl->l.elem[i]);
+ SBezier *test = &(sbl->l[i]);
if((test->Finish()).Equals(hanging) && test->auxA == auxA) {
test->Reverse();
return loop;
}
-void SBezierLoop::Reverse(void) {
+void SBezierLoop::Reverse() {
l.Reverse();
SBezier *sb;
for(sb = l.First(); sb; sb = l.NextAfter(sb)) {
}
void SBezierLoop::GetBoundingProjd(Vector u, Vector orig,
- double *umin, double *umax)
+ double *umin, double *umax) const
{
- SBezier *sb;
- for(sb = l.First(); sb; sb = l.NextAfter(sb)) {
+ for(const SBezier *sb = l.First(); sb; sb = l.NextAfter(sb)) {
sb->GetBoundingProjd(u, orig, umin, umax);
}
}
-void SBezierLoop::MakePwlInto(SContour *sc, double chordTol) {
- SBezier *sb;
- for(sb = l.First(); sb; sb = l.NextAfter(sb)) {
+void SBezierLoop::MakePwlInto(SContour *sc, double chordTol) const {
+ for(const SBezier *sb = l.First(); sb; sb = l.NextAfter(sb)) {
sb->MakePwlInto(sc, chordTol);
// Avoid double points at join between Beziers; except that
// first and last points should be identical.
}
}
// Ensure that it's exactly closed, not just within a numerical tolerance.
- if((sc->l.elem[sc->l.n - 1].p).Equals(sc->l.elem[0].p)) {
- sc->l.elem[sc->l.n - 1] = sc->l.elem[0];
+ if((sc->l.Last()->p).Equals(sc->l.First()->p)) {
+ *sc->l.Last() = *sc->l.First();
}
}
-bool SBezierLoop::IsClosed(void) {
- if(l.n < 1) return false;
- Vector s = l.elem[0].Start(),
- f = l.elem[l.n-1].Finish();
+bool SBezierLoop::IsClosed() const {
+ if(l.IsEmpty()) return false;
+ Vector s = l.First()->Start(),
+ f = l.Last()->Finish();
return s.Equals(f);
}
SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly,
double chordTol,
bool *allClosed, SEdge *errorAt,
- SBezierList *openContours)
+ SBezierLoopSet *openContours)
{
SBezierLoopSet ret = {};
*allClosed = true;
- while(sbl->l.n > 0) {
+ while(!sbl->l.IsEmpty()) {
bool thisClosed;
SBezierLoop loop;
loop = SBezierLoop::FromCurves(sbl, &thisClosed, errorAt);
// Record open loops in a separate list, if requested.
*allClosed = false;
if(openContours) {
- SBezier *sb;
- for(sb = loop.l.First(); sb; sb = loop.l.NextAfter(sb)) {
- openContours->l.Add(sb);
- }
+ openContours->l.Add(&loop);
+ } else {
+ loop.Clear();
}
- loop.Clear();
} else {
ret.l.Add(&loop);
poly->AddEmptyContour();
- loop.MakePwlInto(&(poly->l.elem[poly->l.n-1]), chordTol);
+ loop.MakePwlInto(poly->l.Last(), chordTol);
}
}
}
void SBezierLoopSet::GetBoundingProjd(Vector u, Vector orig,
- double *umin, double *umax)
+ double *umin, double *umax) const
{
- SBezierLoop *sbl;
- for(sbl = l.First(); sbl; sbl = l.NextAfter(sbl)) {
+ for(const SBezierLoop *sbl = l.First(); sbl; sbl = l.NextAfter(sbl)) {
sbl->GetBoundingProjd(u, orig, umin, umax);
}
}
+double SBezierLoopSet::SignedArea() {
+ if(EXACT(area == 0.0)) {
+ SPolygon sp = {};
+ MakePwlInto(&sp);
+ sp.normal = sp.ComputeNormal();
+ area = sp.SignedArea();
+ sp.Clear();
+ }
+ return area;
+}
+
//-----------------------------------------------------------------------------
// Convert all the Beziers into piecewise linear form, and assemble that into
// a polygon, one contour per loop.
//-----------------------------------------------------------------------------
-void SBezierLoopSet::MakePwlInto(SPolygon *sp) {
- SBezierLoop *sbl;
- for(sbl = l.First(); sbl; sbl = l.NextAfter(sbl)) {
+void SBezierLoopSet::MakePwlInto(SPolygon *sp) const {
+ for(const SBezierLoop *sbl = l.First(); sbl; sbl = l.NextAfter(sbl)) {
sp->AddEmptyContour();
- sbl->MakePwlInto(&(sp->l.elem[sp->l.n - 1]));
+ sbl->MakePwlInto(sp->l.Last());
}
}
-void SBezierLoopSet::Clear(void) {
+void SBezierLoopSet::Clear() {
int i;
for(i = 0; i < l.n; i++) {
- (l.elem[i]).Clear();
+ (l[i]).Clear();
}
l.Clear();
}
double chordTol,
bool *allClosed, SEdge *notClosedAt,
bool *allCoplanar, Vector *notCoplanarAt,
- SBezierList *openContours)
+ SBezierLoopSet *openContours)
{
SSurface srfPlane;
if(!srfuv) {
if(openContours) {
SBezier *sb;
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
- openContours->l.Add(sb);
+ SBezierLoop sbl={};
+ sbl.l.Add(sb);
+ openContours->l.Add(&sbl);
}
}
return;
for(pt = sc->l.First(); pt; pt = sc->l.NextAfter(pt)) {
double u, v;
srfuv->ClosestPointTo(pt->p, &u, &v);
- spuv.l.elem[spuv.l.n - 1].AddPoint(Vector::From(u, v, 0));
+ spuv.l.Last()->AddPoint(Vector::From(u, v, 0));
}
}
spuv.normal = Vector::From(0, 0, 1); // must be, since it's in xy plane now
// works for curved surfaces too (important for STEP export).
spuv.FixContourDirections();
for(i = 0; i < spuv.l.n; i++) {
- SContour *contour = &(spuv.l.elem[i]);
- SBezierLoop *bl = &(sbls.l.elem[i]);
+ SContour *contour = &(spuv.l[i]);
+ SBezierLoop *bl = &(sbls.l[i]);
if(contour->tag) {
// This contour got reversed in the polygon to make the directions
// consistent, so the same must be necessary for the Bezier loop.
while(loopsRemaining) {
loopsRemaining = false;
for(i = 0; i < sbls.l.n; i++) {
- SBezierLoop *loop = &(sbls.l.elem[i]);
+ SBezierLoop *loop = &(sbls.l[i]);
if(loop->tag != OUTER_LOOP) continue;
// Check if this contour contains any outer loops; if it does, then
// will steal their holes, since their holes also lie inside this
// contour.
for(j = 0; j < sbls.l.n; j++) {
- SBezierLoop *outer = &(sbls.l.elem[j]);
+ SBezierLoop *outer = &(sbls.l[j]);
if(i == j) continue;
if(outer->tag != OUTER_LOOP) continue;
- Vector p = spuv.l.elem[j].AnyEdgeMidpoint();
- if(spuv.l.elem[i].ContainsPointProjdToNormal(spuv.normal, p)) {
+ Vector p = spuv.l[j].AnyEdgeMidpoint();
+ if(spuv.l[i].ContainsPointProjdToNormal(spuv.normal, p)) {
break;
}
}
loop->tag = USED_LOOP;
outerAndInners.l.Add(loop);
int auxA = 0;
- if(loop->l.n > 0) auxA = loop->l.elem[0].auxA;
+ if(loop->l.n > 0) auxA = loop->l[0].auxA;
for(j = 0; j < sbls.l.n; j++) {
- SBezierLoop *inner = &(sbls.l.elem[j]);
+ SBezierLoop *inner = &(sbls.l[j]);
if(inner->tag != INNER_LOOP) continue;
if(inner->l.n < 1) continue;
- if(inner->l.elem[0].auxA != auxA) continue;
+ if(inner->l[0].auxA != auxA) continue;
- Vector p = spuv.l.elem[j].AnyEdgeMidpoint();
- if(spuv.l.elem[i].ContainsPointProjdToNormal(spuv.normal, p)) {
+ Vector p = spuv.l[j].AnyEdgeMidpoint();
+ if(spuv.l[i].ContainsPointProjdToNormal(spuv.normal, p)) {
outerAndInners.l.Add(inner);
inner->tag = USED_LOOP;
}
// to screw up on that stuff. So just add them onto the open curve list.
// Very ugly, but better than losing curves.
for(i = 0; i < sbls.l.n; i++) {
- SBezierLoop *loop = &(sbls.l.elem[i]);
+ SBezierLoop *loop = &(sbls.l[i]);
if(loop->tag == USED_LOOP) continue;
if(openContours) {
- SBezier *sb;
- for(sb = loop->l.First(); sb; sb = loop->l.NextAfter(sb)) {
- openContours->l.Add(sb);
- }
+ openContours->l.Add(loop);
+ } else {
+ loop->Clear();
}
- loop->Clear();
// but don't free the used loops, since we shallow-copied them to
// ourself
}
l.Add(&sbls);
}
-void SBezierLoopSetSet::Clear(void) {
+void SBezierLoopSetSet::Clear() {
SBezierLoopSet *sbls;
for(sbls = l.First(); sbls; sbls = l.NextAfter(sbls)) {
sbls->Clear();
l.Clear();
}
-SCurve SCurve::FromTransformationOf(SCurve *a,
- Vector t, Quaternion q, double scale)
+SCurve SCurve::FromTransformationOf(SCurve *a, Vector t,
+ Quaternion q, double scale)
{
- SCurve ret = {};
+ bool needRotate = !EXACT(q.vx == 0.0 && q.vy == 0.0 && q.vz == 0.0 && q.w == 1.0);
+ bool needTranslate = !EXACT(t.x == 0.0 && t.y == 0.0 && t.z == 0.0);
+ bool needScale = !EXACT(scale == 1.0);
+ SCurve ret = {};
ret.h = a->h;
ret.isExact = a->isExact;
ret.exact = (a->exact).TransformedBy(t, q, scale);
ret.surfB = a->surfB;
SCurvePt *p;
+ ret.pts.ReserveMore(a->pts.n);
for(p = a->pts.First(); p; p = a->pts.NextAfter(p)) {
SCurvePt pp = *p;
- pp.p = (pp.p).ScaledBy(scale);
- pp.p = (q.Rotate(pp.p)).Plus(t);
+ if(needScale) {
+ pp.p = (pp.p).ScaledBy(scale);
+ }
+ if(needRotate) {
+ pp.p = q.Rotate(pp.p);
+ }
+ if(needTranslate) {
+ pp.p = pp.p.Plus(t);
+ }
ret.pts.Add(&pp);
}
return ret;
}
-void SCurve::Clear(void) {
+void SCurve::Clear() {
pts.Clear();
}
-SSurface *SCurve::GetSurfaceA(SShell *a, SShell *b) {
- if(source == FROM_A) {
+SSurface *SCurve::GetSurfaceA(SShell *a, SShell *b) const {
+ if(source == Source::A) {
return a->surface.FindById(surfA);
- } else if(source == FROM_B) {
+ } else if(source == Source::B) {
return b->surface.FindById(surfA);
- } else if(source == FROM_INTERSECTION) {
+ } else if(source == Source::INTERSECTION) {
return a->surface.FindById(surfA);
- } else oops();
+ } else ssassert(false, "Unexpected curve source");
}
-SSurface *SCurve::GetSurfaceB(SShell *a, SShell *b) {
- if(source == FROM_A) {
+SSurface *SCurve::GetSurfaceB(SShell *a, SShell *b) const {
+ if(source == Source::A) {
return a->surface.FindById(surfB);
- } else if(source == FROM_B) {
+ } else if(source == Source::B) {
return b->surface.FindById(surfB);
- } else if(source == FROM_INTERSECTION) {
+ } else if(source == Source::INTERSECTION) {
return b->surface.FindById(surfB);
- } else oops();
+ } else ssassert(false, "Unexpected curve source");
}
//-----------------------------------------------------------------------------
// stuff in the Booleans. So remove them.
//-----------------------------------------------------------------------------
void SCurve::RemoveShortSegments(SSurface *srfA, SSurface *srfB) {
- // Three, not two; curves are pwl'd to at least two edges (three points)
- // even if not necessary, to avoid square holes.
- if(pts.n <= 3) return;
+ if(pts.n <= 2) return;
pts.ClearTags();
- Vector prev = pts.elem[0].p;
+ Vector prev = pts[0].p;
+ double tprev = 0;
+ double t = 0;
+ double tnext = 0;
+
int i, a;
for(i = 1; i < pts.n - 1; i++) {
- SCurvePt *sct = &(pts.elem[i]),
- *scn = &(pts.elem[i+1]);
+ SCurvePt *sct = &(pts[i]),
+ *scn = &(pts[i+1]);
+
if(sct->vertex) {
prev = sct->p;
continue;
}
+
+ // if the curve is exact and points are >0.05 appart wrt t, point is there
+ // deliberately regardless of chord tolerance (ex: small circles)
+ tprev = t = tnext = 0;
+ if (isExact) {
+ exact.ClosestPointTo(prev, &tprev, /*mustconverge=*/ true);
+ exact.ClosestPointTo(sct->p, &t, /*mustconverge=*/ true);
+ exact.ClosestPointTo(scn->p, &tnext, /*mustconverge=*/ true);
+ }
+ if ( (t - tprev > 0.05) && (tnext - t > 0.05) ) {
+ prev = sct->p;
+ continue;
+ }
+
bool mustKeep = false;
// We must check against both surfaces; the piecewise linear edge
srf->ClosestPointTo(prev, &(puv.x), &(puv.y));
srf->ClosestPointTo(scn->p, &(nuv.x), &(nuv.y));
- if(srf->ChordToleranceForEdge(nuv, puv) > SS.ChordTolMm()) {
+ if(srf->ChordToleranceForEdge(nuv, puv) > SS.ChordTolMm() ) {
mustKeep = true;
}
}
SCurve *sc = shell->curve.FindById(hsc);
if(backwards) {
- stb.finish = sc->pts.elem[0].p;
- stb.start = sc->pts.elem[sc->pts.n - 1].p;
+ stb.finish = sc->pts[0].p;
+ stb.start = sc->pts.Last()->p;
stb.backwards = true;
} else {
- stb.start = sc->pts.elem[0].p;
- stb.finish = sc->pts.elem[sc->pts.n - 1].p;
+ stb.start = sc->pts[0].p;
+ stb.finish = sc->pts.Last()->p;
stb.backwards = false;
}
//-----------------------------------------------------------------------------
#include "../solvespace.h"
-void SShell::MergeCoincidentSurfaces(void) {
+void SShell::MergeCoincidentSurfaces() {
surface.ClearTags();
int i, j;
SSurface *si, *sj;
for(i = 0; i < surface.n; i++) {
- si = &(surface.elem[i]);
+ si = &(surface[i]);
if(si->tag) continue;
// Let someone else clean up the empty surfaces; we can certainly merge
// them, but we don't know how to calculate a reasonable bounding box.
- if(si->trim.n == 0) continue;
+ if(si->trim.IsEmpty())
+ continue;
// And for now we handle only coincident planes, so no sense wasting
// time on other surfaces.
if(si->degm != 1 || si->degn != 1) continue;
SEdgeList sel = {};
- si->MakeEdgesInto(this, &sel, SSurface::AS_XYZ);
+ si->MakeEdgesInto(this, &sel, SSurface::MakeAs::XYZ);
bool mergedThisTime, merged = false;
do {
mergedThisTime = false;
for(j = i + 1; j < surface.n; j++) {
- sj = &(surface.elem[j]);
+ sj = &(surface[j]);
if(sj->tag) continue;
- if(!sj->CoincidentWith(si, true)) continue;
+ if(!sj->CoincidentWith(si, /*sameNormal=*/true)) continue;
if(!sj->color.Equals(si->color)) continue;
// But we do merge surfaces with different face entities, since
// otherwise we'd hardly ever merge anything.
// the bounding box tests less effective, and possibly things
// less robust.
SEdgeList tel = {};
- sj->MakeEdgesInto(this, &tel, SSurface::AS_XYZ);
+ sj->MakeEdgesInto(this, &tel, SSurface::MakeAs::XYZ);
if(!sel.ContainsEdgeFrom(&tel)) {
tel.Clear();
continue;
sj->tag = 1;
merged = true;
mergedThisTime = true;
- sj->MakeEdgesInto(this, &sel, SSurface::AS_XYZ);
+ sj->MakeEdgesInto(this, &sel, SSurface::MakeAs::XYZ);
sj->trim.Clear();
// All the references to this surface get replaced with the
// new srf
SCurve *sc;
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
- if(sc->surfA.v == sj->h.v) sc->surfA = si->h;
- if(sc->surfB.v == sj->h.v) sc->surfB = si->h;
+ if(sc->surfA == sj->h) sc->surfA = si->h;
+ if(sc->surfB == sj->h) sc->surfB = si->h;
}
}
if(merged) {
sel.CullExtraneousEdges();
si->trim.Clear();
- si->TrimFromEdgeList(&sel, false);
+ si->TrimFromEdgeList(&sel, /*asUv=*/false);
// And we must choose control points such that all the trims lie
// with u and v in [0, 1], so that the bbox tests work.
// and convergence should be fast by now.
#define RATPOLY_EPS (LENGTH_EPS/(1e2))
-double SolveSpace::Bernstein(int k, int deg, double t)
-{
- if(k > deg || k < 0) return 0;
-
- switch(deg) {
- case 0:
- return 1;
-
- case 1:
- if(k == 0) {
- return (1 - t);
- } else if(k == 1) {
- return t;
- }
- break;
-
- case 2:
- if(k == 0) {
- return (1 - t)*(1 - t);
- } else if(k == 1) {
- return 2*(1 - t)*t;
- } else if(k == 2) {
- return t*t;
- }
- break;
-
- case 3:
- if(k == 0) {
- return (1 - t)*(1 - t)*(1 - t);
- } else if(k == 1) {
- return 3*(1 - t)*(1 - t)*t;
- } else if(k == 2) {
- return 3*(1 - t)*t*t;
- } else if(k == 3) {
- return t*t*t;
- }
- break;
- }
- oops();
+static inline double Bernstein(int k, int deg, double t) {
+// indexed by [degree][k][exponent]
+ static const double bernstein_coeff[4][4][4] = {
+ { { 1.0,0.0,0.0,0.0 }, { 1.0,0.0,0.0,0.0 }, { 1.0,0.0,0.0,0.0 }, { 1.0,0.0,0.0,0.0 } },
+ { { 1.0,-1.0,0.0,0.0 }, { 0.0,1.0,0.0,0.0 }, { 0.0,0.0,0.0,0.0 }, { 0.0,0.0,0.0,0.0 } },
+ { { 1.0,-2.0,1.0,0.0 }, { 0.0,2.0,-2.0,0.0 },{ 0.0,0.0,1.0,0.0 }, { 0.0,0.0,0.0,0.0 } },
+ { { 1.0,-3.0,3.0,-1.0 },{ 0.0,3.0,-6.0,3.0 },{ 0.0,0.0,3.0,-3.0}, { 0.0,0.0,0.0,1.0 } } };
+
+ const double *c = bernstein_coeff[deg][k];
+ return (((c[3]*t+c[2])*t)+c[1])*t+c[0];
}
-double SolveSpace::BernsteinDerivative(int k, int deg, double t)
-{
- switch(deg) {
- case 0:
- return 0;
-
- case 1:
- if(k == 0) {
- return -1;
- } else if(k == 1) {
- return 1;
- }
- break;
+static inline double BernsteinDerivative(int k, int deg, double t) {
+ static const double bernstein_derivative_coeff[4][4][3] = {
+ { { 0.0,0.0,0.0 }, { 0.0,0.0,0.0 }, { 0.0,0.0,0.0 }, { 0.0,0.0,0.0 } },
+ { { -1.0,0.0,0.0 }, { 1.0,0.0,0.0 }, { 0.0,0.0,0.0 }, { 0.0,0.0,0.0 } },
+ { { -2.0,2.0,0.0 }, { 2.0,-4.0,0.0 },{ 0.0,2.0,0.0 }, { 0.0,0.0,0.0 } },
+ { { -3.0,6.0,-3.0 },{ 3.0,-12.0,9.0 },{ 0.0,6.0,-9.0}, { 0.0,0.0,3.0 } } };
- case 2:
- if(k == 0) {
- return -2 + 2*t;
- } else if(k == 1) {
- return 2 - 4*t;
- } else if(k == 2) {
- return 2*t;
- }
- break;
-
- case 3:
- if(k == 0) {
- return -3 + 6*t - 3*t*t;
- } else if(k == 1) {
- return 3 - 12*t + 9*t*t;
- } else if(k == 2) {
- return 6*t - 9*t*t;
- } else if(k == 3) {
- return 3*t*t;
- }
- break;
- }
- oops();
+ const double *c = bernstein_derivative_coeff[deg][k];
+ return ((c[2]*t)+c[1])*t+c[0];
}
-Vector SBezier::PointAt(double t) {
+Vector SBezier::PointAt(double t) const {
Vector pt = Vector::From(0, 0, 0);
double d = 0;
return pt;
}
-Vector SBezier::TangentAt(double t) {
+Vector SBezier::TangentAt(double t) const {
Vector pt = Vector::From(0, 0, 0), pt_p = Vector::From(0, 0, 0);
double d = 0, d_p = 0;
return ret;
}
-void SBezier::ClosestPointTo(Vector p, double *t, bool converge) {
+void SBezier::ClosestPointTo(Vector p, double *t, bool mustConverge) const {
int i;
double minDist = VERY_POSITIVE;
*t = 0;
}
Vector p0;
- for(i = 0; i < (converge ? 15 : 5); i++) {
+ for(i = 0; i < (mustConverge ? 15 : 5); i++) {
p0 = PointAt(*t);
if(p0.Equals(p, RATPOLY_EPS)) {
return;
Vector dp = TangentAt(*t);
Vector pc = p.ClosestPointOnLine(p0, dp);
- *t += (pc.Minus(p0)).DivPivoting(dp);
+ *t += (pc.Minus(p0)).DivProjected(dp);
}
- if(converge) {
+ if(mustConverge) {
dbp("didn't converge (closest point on bezier curve)");
}
}
-bool SBezier::PointOnThisAndCurve(SBezier *sbb, Vector *p) {
+bool SBezier::PointOnThisAndCurve(const SBezier *sbb, Vector *p) const {
double ta, tb;
- this->ClosestPointTo(*p, &ta, false);
- sbb ->ClosestPointTo(*p, &tb, false);
+ this->ClosestPointTo(*p, &ta, /*mustConverge=*/false);
+ sbb ->ClosestPointTo(*p, &tb, /*mustConverge=*/false);
int i;
for(i = 0; i < 20; i++) {
return false;
}
-void SBezier::SplitAt(double t, SBezier *bef, SBezier *aft) {
+void SBezier::SplitAt(double t, SBezier *bef, SBezier *aft) const {
Vector4 ct[4];
int i;
for(i = 0; i <= deg; i++) {
*aft = SBezier::From(cts, ct12_23, ct23, ct[3]);
break;
}
- default: oops();
+ default: ssassert(false, "Unexpected degree of spline");
}
}
-void SBezier::MakePwlInto(SEdgeList *sel, double chordTol) {
+void SBezier::MakePwlInto(SEdgeList *sel, double chordTol, double max_dt) const {
List<Vector> lv = {};
- MakePwlInto(&lv, chordTol);
+ MakePwlInto(&lv, chordTol, max_dt);
int i;
for(i = 1; i < lv.n; i++) {
- sel->AddEdge(lv.elem[i-1], lv.elem[i]);
+ sel->AddEdge(lv[i-1], lv[i]);
}
lv.Clear();
}
-void SBezier::MakePwlInto(List<SCurvePt> *l, double chordTol) {
+void SBezier::MakePwlInto(List<SCurvePt> *l, double chordTol, double max_dt) const {
List<Vector> lv = {};
- MakePwlInto(&lv, chordTol);
+ MakePwlInto(&lv, chordTol, max_dt);
int i;
for(i = 0; i < lv.n; i++) {
SCurvePt scpt;
scpt.tag = 0;
- scpt.p = lv.elem[i];
+ scpt.p = lv[i];
scpt.vertex = (i == 0) || (i == (lv.n - 1));
l->Add(&scpt);
}
lv.Clear();
}
-void SBezier::MakePwlInto(SContour *sc, double chordTol) {
+void SBezier::MakePwlInto(SContour *sc, double chordTol, double max_dt) const {
List<Vector> lv = {};
- MakePwlInto(&lv, chordTol);
+ MakePwlInto(&lv, chordTol, max_dt);
int i;
for(i = 0; i < lv.n; i++) {
- sc->AddPoint(lv.elem[i]);
+ sc->AddPoint(lv[i]);
}
lv.Clear();
}
-void SBezier::MakePwlInto(List<Vector> *l, double chordTol) {
+//--------------------------------------------------------------------------------------
+// all variants of MakePwlInto come here. Split a rational Bezier into Piecewise Linear
+// segments that don't deviate from the actual curve by more than the chordTol distance.
+// max_dt allows to force curves to be split into spans of no more than a certain
+// length based on t-parameter. RemoveShortSegments() may delete points when dt <= 0.1
+//--------------------------------------------------------------------------------------
+void SBezier::MakePwlInto(List<Vector> *l, double chordTol, double max_dt) const {
if(EXACT(chordTol == 0)) {
// Use the default chord tolerance.
chordTol = SS.ChordTolMm();
}
+ // Never do fewer than three intermediate points for curves; people seem to get
+ // unhappy when their circles turn into squares, but maybe less
+ // unhappy with octagons. Now 16-gons.
+ if (EXACT(max_dt == 0.0)) {
+ max_dt = (deg == 1) ? 1.0 : 0.25;
+ }
l->Add(&(ctrl[0]));
- if(deg == 1) {
+ // don't split first degee (lines) unless asked to by the caller via max_dt
+ if((deg == 1) && (max_dt >= 1.0)) {
l->Add(&(ctrl[1]));
} else {
- // Never do fewer than one intermediate point; people seem to get
- // unhappy when their circles turn into squares, but maybe less
- // unhappy with octagons.
- MakePwlInitialWorker(l, 0.0, 0.5, chordTol);
- MakePwlInitialWorker(l, 0.5, 1.0, chordTol);
+ MakePwlInitialWorker(l, 0.0, 0.5, chordTol, max_dt);
+ MakePwlInitialWorker(l, 0.5, 1.0, chordTol, max_dt);
}
}
-void SBezier::MakePwlWorker(List<Vector> *l, double ta, double tb, double chordTol)
+void SBezier::MakePwlWorker(List<Vector> *l, double ta, double tb, double chordTol, double max_dt) const
{
Vector pa = PointAt(ta);
Vector pb = PointAt(tb);
double d = pm.DistanceToLine(pa, pb.Minus(pa));
double step = 1.0/SS.GetMaxSegments();
- if((tb - ta) < step || d < chordTol) {
+ if(((tb - ta) < step || d < chordTol) && ((tb-ta) <= max_dt) ) {
// A previous call has already added the beginning of our interval.
l->Add(&pb);
} else {
double tm = (ta + tb) / 2;
- MakePwlWorker(l, ta, tm, chordTol);
- MakePwlWorker(l, tm, tb, chordTol);
+ MakePwlWorker(l, ta, tm, chordTol, max_dt);
+ MakePwlWorker(l, tm, tb, chordTol, max_dt);
}
}
-void SBezier::MakePwlInitialWorker(List<Vector> *l, double ta, double tb, double chordTol)
+void SBezier::MakePwlInitialWorker(List<Vector> *l, double ta, double tb, double chordTol, double max_dt) const
{
Vector pa = PointAt(ta);
Vector pb = PointAt(tb);
double d = max({
pm1.DistanceToLine(pa, dir),
pm2.DistanceToLine(pa, dir),
- pm3.DistanceToLine(pa, dir)
+ pm3.DistanceToLine(pa, dir)
});
double step = 1.0/SS.GetMaxSegments();
- if((tb - ta) < step || d < chordTol) {
+ if( ((tb - ta) < step || d < chordTol) && ((tb-ta) <= max_dt) ) {
// A previous call has already added the beginning of our interval.
l->Add(&pb);
} else {
double tm = (ta + tb) / 2;
- MakePwlWorker(l, ta, tm, chordTol);
- MakePwlWorker(l, tm, tb, chordTol);
+ MakePwlWorker(l, ta, tm, chordTol, max_dt);
+ MakePwlWorker(l, tm, tb, chordTol, max_dt);
+ }
+}
+
+void SBezier::MakeNonrationalCubicInto(SBezierList *bl, double tolerance, int depth) const {
+ Vector t0 = TangentAt(0), t1 = TangentAt(1);
+ // The curve is correct, and the first derivatives are correct, at the
+ // endpoints.
+ SBezier bnr = SBezier::From(
+ Start(),
+ Start().Plus(t0.ScaledBy(1.0/3)),
+ Finish().Minus(t1.ScaledBy(1.0/3)),
+ Finish());
+
+ bool closeEnough = true;
+ int i;
+ for(i = 1; i <= 3; i++) {
+ double t = i/4.0;
+ Vector p0 = PointAt(t),
+ pn = bnr.PointAt(t);
+ double d = (p0.Minus(pn)).Magnitude();
+ if(d > tolerance) {
+ closeEnough = false;
+ }
+ }
+
+ if(closeEnough || depth > 3) {
+ bl->l.Add(this);
+ } else {
+ SBezier bef, aft;
+ SplitAt(0.5, &bef, &aft);
+ bef.MakeNonrationalCubicInto(bl, tolerance, depth+1);
+ aft.MakeNonrationalCubicInto(bl, tolerance, depth+1);
}
}
-Vector SSurface::PointAt(Point2d puv) {
+Vector SSurface::PointAt(Point2d puv) const {
return PointAt(puv.x, puv.y);
}
-Vector SSurface::PointAt(double u, double v) {
+Vector SSurface::PointAt(double u, double v) const {
Vector num = Vector::From(0, 0, 0);
double den = 0;
return num;
}
-void SSurface::TangentsAt(double u, double v, Vector *tu, Vector *tv) {
+void SSurface::TangentsAt(double u, double v, Vector *tu, Vector *tv, bool retry) const {
Vector num = Vector::From(0, 0, 0),
num_u = Vector::From(0, 0, 0),
num_v = Vector::From(0, 0, 0);
*tv = ((num_v.ScaledBy(den)).Minus(num.ScaledBy(den_v)));
*tv = tv->ScaledBy(1.0/(den*den));
+
+ // Tangent is zero at sungularities like the north pole. Move away a bit and retry.
+ if(tv->Equals(Vector::From(0,0,0)) && retry)
+ TangentsAt(u+(0.5-u)*0.00001, v, tu, tv, false);
+ if(tu->Equals(Vector::From(0,0,0)) && retry)
+ TangentsAt(u, v+(0.5-v)*0.00001, tu, tv, false);
}
-Vector SSurface::NormalAt(Point2d puv) {
+Vector SSurface::NormalAt(Point2d puv) const {
return NormalAt(puv.x, puv.y);
}
-Vector SSurface::NormalAt(double u, double v) {
+
+Vector SSurface::NormalAt(double u, double v) const {
Vector tu, tv;
TangentsAt(u, v, &tu, &tv);
return tu.Cross(tv);
}
-void SSurface::ClosestPointTo(Vector p, Point2d *puv, bool converge) {
- ClosestPointTo(p, &(puv->x), &(puv->y), converge);
+void SSurface::ClosestPointTo(Vector p, Point2d *puv, bool mustConverge) {
+ ClosestPointTo(p, &(puv->x), &(puv->y), mustConverge);
}
-void SSurface::ClosestPointTo(Vector p, double *u, double *v, bool converge) {
+
+void SSurface::ClosestPointTo(Vector p, double *u, double *v, bool mustConverge) {
// A few special cases first; when control points are coincident the
- // derivative goes to zero at the conrol points, and would result in
+ // derivative goes to zero at the control points, and would result in
// nonconvergence. We avoid that here, and also guarantee a consistent
// (u, v) (of the infinitely many possible in one parameter).
if(p.Equals(ctrl[0] [0] )) { *u = 0; *v = 0; return; }
bu = (ctrl[1][0]).Minus(orig),
bv = (ctrl[0][1]).Minus(orig);
if((ctrl[1][1]).Equals(orig.Plus(bu).Plus(bv))) {
+
+ Vector n = bu.Cross(bv);
+ Vector ty = n.Cross(bu).ScaledBy(1.0/bu.MagSquared());
+ Vector tx = bv.Cross(n).ScaledBy(1.0/bv.MagSquared());
+
Vector dp = p.Minus(orig);
- *u = dp.Dot(bu) / bu.MagSquared();
- *v = dp.Dot(bv) / bv.MagSquared();
+ *u = dp.Dot(bu) / tx.MagSquared();
+ *v = dp.Dot(bv) / ty.MagSquared();
return;
}
}
// good if we're working our way along a curve or something else where
// we project successive points that are close to each other; something
// like a 20% speedup empirically.
- if(converge) {
+ if(mustConverge) {
double ut = cached.x, vt = cached.y;
- if(ClosestPointNewton(p, &ut, &vt, converge)) {
+ if(ClosestPointNewton(p, &ut, &vt, mustConverge)) {
cached.x = *u = ut;
cached.y = *v = vt;
return;
}
}
- if(ClosestPointNewton(p, u, v, converge)) {
+ if(ClosestPointNewton(p, u, v, mustConverge)) {
cached.x = *u;
cached.y = *v;
return;
}
// If we failed to converge, then at least don't return NaN.
- if(isnan(*u) || isnan(*v)) {
+ if(mustConverge) {
+// This is expected not to converge when the target point is not on the surface but nearby.
+// let's not pollute the output window for normal use.
+// Vector p0 = PointAt(*u, *v);
+// dbp("didn't converge");
+// dbp("have %.3f %.3f %.3f", CO(p0));
+// dbp("want %.3f %.3f %.3f", CO(p));
+// dbp("distance = %g", (p.Minus(p0)).Magnitude());
+ }
+ if(IsReasonable(*u) || IsReasonable(*v)) {
*u = *v = 0;
}
}
-bool SSurface::ClosestPointNewton(Vector p, double *u, double *v, bool converge)
+bool SSurface::ClosestPointNewton(Vector p, double *u, double *v, bool mustConverge) const
{
// Initial guess is in u, v; refine by Newton iteration.
Vector p0 = Vector::From(0, 0, 0);
- for(int i = 0; i < (converge ? 25 : 5); i++) {
+ for(int i = 0; i < (mustConverge ? 25 : 5); i++) {
p0 = PointAt(*u, *v);
- if(converge) {
+ if(mustConverge) {
if(p0.Equals(p, RATPOLY_EPS)) {
return true;
}
}
- Vector tu, tv;
+ Vector tu, tv, tx, ty;
TangentsAt(*u, *v, &tu, &tv);
+ Vector n = tu.Cross(tv);
+ // since tu and tv may not be orthogonal, use y in place of v.
+ // |y| = |v|sin(theta) where theta is the angle between tu and tv.
+ ty = n.Cross(tu).ScaledBy(1.0/tu.MagSquared());
+ tx = tv.Cross(n).ScaledBy(1.0/tv.MagSquared());
// Project the point into a plane through p0, with basis tu, tv; a
// second-order thing would converge faster but needs second
// derivatives.
Vector dp = p.Minus(p0);
- double du = dp.Dot(tu), dv = dp.Dot(tv);
- *u += du / (tu.MagSquared());
- *v += dv / (tv.MagSquared());
- }
+ double du = dp.Dot(tx),
+ dv = dp.Dot(ty);
+ *u += du / (tx.MagSquared());
+ *v += dv / (ty.MagSquared());
+
+ if (*u < 0.0) *u = 0.0;
+ else if (*u > 1.0) *u = 1.0;
+ if (*v < 0.0) *v = 0.0;
+ else if (*v > 1.0) *v = 1.0;
- if(converge) {
- dbp("didn't converge");
- dbp("have %.3f %.3f %.3f", CO(p0));
- dbp("want %.3f %.3f %.3f", CO(p));
- dbp("distance = %g", (p.Minus(p0)).Magnitude());
}
+
return false;
}
-bool SSurface::PointIntersectingLine(Vector p0, Vector p1, double *u, double *v)
+bool SSurface::PointIntersectingLine(Vector p0, Vector p1, double *u, double *v) const
{
int i;
- for(i = 0; i < 15; i++) {
+ for(i = 0; i < 20; i++) {
Vector pi, p, tu, tv;
p = PointAt(*u, *v);
TangentsAt(*u, *v, &tu, &tv);
bool parallel;
pi = Vector::AtIntersectionOfPlaneAndLine(n, d, p0, p1, ¶llel);
- if(parallel) break;
+ if(parallel) {
+ dbp("parallel (surface intersecting line)");
+ break;
+ }
// Check for convergence
if(pi.Equals(p, RATPOLY_EPS)) return true;
+ n = tu.Cross(tv);
+ Vector ty = n.Cross(tu).ScaledBy(1.0/tu.MagSquared());
+ Vector tx = tv.Cross(n).ScaledBy(1.0/tv.MagSquared());
+
// Adjust our guess and iterate
Vector dp = pi.Minus(p);
- double du = dp.Dot(tu), dv = dp.Dot(tv);
- *u += du / (tu.MagSquared());
- *v += dv / (tv.MagSquared());
+ double du = dp.Dot(tx), dv = dp.Dot(ty);
+ *u += du / tx.MagSquared();
+ *v += dv / ty.MagSquared();
}
-// dbp("didn't converge (surface intersecting line)");
+ dbp("didn't converge (surface intersecting line)");
return false;
}
SSurface *srf[2] = { this, srf2 };
for(j = 0; j < 2; j++) {
- (srf[j])->ClosestPointTo(p, &(puv[j]), false);
+ (srf[j])->ClosestPointTo(p, &(puv[j]), /*mustConverge=*/false);
}
for(i = 0; i < 10; i++) {
// Adjust our guess and iterate
for(j = 0; j < 2; j++) {
+ Vector n = tu[j].Cross(tv[j]);
+ Vector ty = n.Cross(tu[j]).ScaledBy(1.0/tu[j].MagSquared());
+ Vector tx = tv[j].Cross(n).ScaledBy(1.0/tv[j].MagSquared());
+
Vector dc = pc.Minus(cp[j]);
- double du = dc.Dot(tu[j]), dv = dc.Dot(tv[j]);
- puv[j].x += du / ((tu[j]).MagSquared());
- puv[j].y += dv / ((tv[j]).MagSquared());
+ double du = dc.Dot(tx), dv = dc.Dot(ty);
+ puv[j].x += du / tx.MagSquared();
+ puv[j].y += dv / ty.MagSquared();
}
}
if(i >= 10) {
((srf[1])->PointAt(puv[1]))).ScaledBy(0.5);
}
-void SSurface::PointOnSurfaces(SSurface *s1, SSurface *s2,
- double *up, double *vp)
+void SSurface::PointOnSurfaces(SSurface *s1, SSurface *s2, double *up, double *vp)
{
double u[3] = { *up, 0, 0 }, v[3] = { *vp, 0, 0 };
SSurface *srf[3] = { this, s1, s2 };
// Get initial guesses for (u, v) in the other surfaces
Vector p = PointAt(*u, *v);
- (srf[1])->ClosestPointTo(p, &(u[1]), &(v[1]), false);
- (srf[2])->ClosestPointTo(p, &(u[2]), &(v[2]), false);
+ (srf[1])->ClosestPointTo(p, &(u[1]), &(v[1]), /*mustConverge=*/false);
+ (srf[2])->ClosestPointTo(p, &(u[2]), &(v[2]), /*mustConverge=*/false);
int i, j;
for(i = 0; i < 20; i++) {
Vector pi = Vector::AtIntersectionOfPlanes(n[0], d[0],
n[1], d[1],
n[2], d[2], ¶llel);
- if(parallel) break;
+
+ if(parallel) { // lets try something else for parallel planes
+ pi = p[0].Plus(p[1]).Plus(p[2]).ScaledBy(1.0/3.0);
+ }
for(j = 0; j < 3; j++) {
+ Vector n = tu[j].Cross(tv[j]);
+ Vector ty = n.Cross(tu[j]).ScaledBy(1.0/tu[j].MagSquared());
+ Vector tx = tv[j].Cross(n).ScaledBy(1.0/tv[j].MagSquared());
+
Vector dp = pi.Minus(p[j]);
- double du = dp.Dot(tu[j]), dv = dp.Dot(tv[j]);
- u[j] += du / (tu[j]).MagSquared();
- v[j] += dv / (tv[j]).MagSquared();
+ double du = dp.Dot(tx), dv = dp.Dot(ty);
+
+ u[j] += du / tx.MagSquared();
+ v[j] += dv / ty.MagSquared();
}
}
dbp("didn't converge (three surfaces intersecting)");
}
+void SSurface::PointOnCurve(const SBezier *curve, double *up, double *vp)
+{
+ Vector tu,tv,n;
+ double u = *up, v = *vp;
+ Vector ps = PointAt(u, v);
+ // Get initial guesses for t on the curve
+ double tCurve = 0.5;
+ curve->ClosestPointTo(ps, &tCurve, /*mustConverge=*/false);
+ if(tCurve < 0.0) tCurve = 0.0;
+ if(tCurve > 1.0) tCurve = 1.0;
+
+ for(int i = 0; i < 30; i++) {
+ // Approximate the surface by a plane
+ Vector ps = PointAt(u, v);
+ TangentsAt(u, v, &tu, &tv);
+ n = tu.Cross(tv).WithMagnitude(1);
+
+ // point on curve and tangent line direction
+ Vector pc = curve->PointAt(tCurve);
+ Vector tc = curve->TangentAt(tCurve);
+
+ if(ps.Equals(pc, RATPOLY_EPS)) {
+ *up = u;
+ *vp = v;
+ return;
+ }
+
+ //pi is where the curve tangent line intersects the surface tangent plane
+ Vector pi;
+ double d = tc.Dot(n);
+ if (fabs(d) < 1e-10) { // parallel line and plane, guess the average rather than fail
+ pi = pc.Plus(ps).ScaledBy(0.5);
+ } else {
+ pi = pc.Minus(tc.ScaledBy(pc.Minus(ps).Dot(n)/d));
+ }
+
+ // project the point onto the tangent plane and line
+ {
+ Vector n = tu.Cross(tv);
+ Vector ty = n.Cross(tu).ScaledBy(1.0/tu.MagSquared());
+ Vector tx = tv.Cross(n).ScaledBy(1.0/tv.MagSquared());
+
+ Vector dp = pi.Minus(ps);
+ double du = dp.Dot(tx), dv = dp.Dot(ty);
+
+ u += du / tx.MagSquared();
+ v += dv / ty.MagSquared();
+ }
+ tCurve += pi.Minus(pc).Dot(tc) / tc.MagSquared();
+ if(tCurve < 0.0) tCurve = 0.0;
+ if(tCurve > 1.0) tCurve = 1.0;
+ }
+ dbp("didn't converge (surface and curve intersecting)");
+}
+
extern int FLAG;
-double SSurface::DepartureFromCoplanar(void) {
+double SSurface::DepartureFromCoplanar() const {
int i, j;
int ia, ja, ib = 0, jb = 0, ic = 0, jc = 0;
double best;
return farthest;
}
-void SSurface::WeightControlPoints(void) {
+void SSurface::WeightControlPoints() {
int i, j;
for(i = 0; i <= degm; i++) {
for(j = 0; j <= degn; j++) {
}
}
}
-void SSurface::UnWeightControlPoints(void) {
+void SSurface::UnWeightControlPoints() {
int i, j;
for(i = 0; i <= degm; i++) {
for(j = 0; j <= degn; j++) {
sa->degn = sb->degn = degn;
// by de Casteljau's algorithm in a projective space; so we must work
- // on points (w*x, w*y, w*z, w)
- WeightControlPoints();
+ // on points (w*x, w*y, w*z, w) so create a temporary copy
+ SSurface st;
+ st = *this;
+ st.WeightControlPoints();
switch(byU ? degm : degn) {
case 1:
- sa->CopyRowOrCol (byU, 0, this, 0);
- sb->CopyRowOrCol (byU, 1, this, 1);
+ sa->CopyRowOrCol (byU, 0, &st, 0);
+ sb->CopyRowOrCol (byU, 1, &st, 1);
- sa->BlendRowOrCol(byU, 1, this, 0, this, 1);
- sb->BlendRowOrCol(byU, 0, this, 0, this, 1);
+ sa->BlendRowOrCol(byU, 1, &st, 0, &st, 1);
+ sb->BlendRowOrCol(byU, 0, &st, 0, &st, 1);
break;
case 2:
- sa->CopyRowOrCol (byU, 0, this, 0);
- sb->CopyRowOrCol (byU, 2, this, 2);
+ sa->CopyRowOrCol (byU, 0, &st, 0);
+ sb->CopyRowOrCol (byU, 2, &st, 2);
- sa->BlendRowOrCol(byU, 1, this, 0, this, 1);
- sb->BlendRowOrCol(byU, 1, this, 1, this, 2);
+ sa->BlendRowOrCol(byU, 1, &st, 0, &st, 1);
+ sb->BlendRowOrCol(byU, 1, &st, 1, &st, 2);
sa->BlendRowOrCol(byU, 2, sa, 1, sb, 1);
sb->BlendRowOrCol(byU, 0, sa, 1, sb, 1);
break;
case 3: {
- SSurface st;
- st.degm = degm; st.degn = degn;
+ sa->CopyRowOrCol (byU, 0, &st, 0);
+ sb->CopyRowOrCol (byU, 3, &st, 3);
- sa->CopyRowOrCol (byU, 0, this, 0);
- sb->CopyRowOrCol (byU, 3, this, 3);
-
- sa->BlendRowOrCol(byU, 1, this, 0, this, 1);
- sb->BlendRowOrCol(byU, 2, this, 2, this, 3);
- st. BlendRowOrCol(byU, 0, this, 1, this, 2); // scratch var
+ sa->BlendRowOrCol(byU, 1, &st, 0, &st, 1);
+ sb->BlendRowOrCol(byU, 2, &st, 2, &st, 3);
+ st. BlendRowOrCol(byU, 0, &st, 1, &st, 2); // use row/col 0 as scratch
sa->BlendRowOrCol(byU, 2, sa, 1, &st, 0);
sb->BlendRowOrCol(byU, 1, sb, 2, &st, 0);
break;
}
- default: oops();
+ default: ssassert(false, "Unexpected degree of spline");
}
sa->UnWeightControlPoints();
sb->UnWeightControlPoints();
- UnWeightControlPoints();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SSurface::AllPointsIntersectingUntrimmed(Vector a, Vector b,
int *cnt, int *level,
- List<Inter> *l, bool segment,
+ List<Inter> *l, bool asSegment,
SSurface *sorig)
{
// Test if the line intersects our axis-aligned bounding box; if no, then
// no possibility of an intersection
- if(LineEntirelyOutsideBbox(a, b, segment)) return;
+ if(LineEntirelyOutsideBbox(a, b, asSegment)) return;
if(*cnt > 2000) {
dbp("!!! too many subdivisions (level=%d)!", *level);
ctrl[degm][0 ]).Plus(
ctrl[degm][degn]).ScaledBy(0.25);
Inter inter;
- sorig->ClosestPointTo(p, &(inter.p.x), &(inter.p.y), false);
+ sorig->ClosestPointTo(p, &(inter.p.x), &(inter.p.y), /*mustConverge=*/false);
if(sorig->PointIntersectingLine(a, b, &(inter.p.x), &(inter.p.y))) {
Vector p = sorig->PointAt(inter.p.x, inter.p.y);
// Debug check, verify that the point lies in both surfaces
int nextLevel = (*level) + 1;
(*level) = nextLevel;
- surf0.AllPointsIntersectingUntrimmed(a, b, cnt, level, l, segment, sorig);
+ surf0.AllPointsIntersectingUntrimmed(a, b, cnt, level, l, asSegment, sorig);
(*level) = nextLevel;
- surf1.AllPointsIntersectingUntrimmed(a, b, cnt, level, l, segment, sorig);
+ surf1.AllPointsIntersectingUntrimmed(a, b, cnt, level, l, asSegment, sorig);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SSurface::AllPointsIntersecting(Vector a, Vector b,
List<SInter> *l,
- bool seg, bool trimmed, bool inclTangent)
+ bool asSegment, bool trimmed, bool inclTangent)
{
- if(LineEntirelyOutsideBbox(a, b, seg)) return;
+ if(LineEntirelyOutsideBbox(a, b, asSegment)) return;
Vector ba = b.Minus(a);
double bam = ba.Magnitude();
double d = n.Dot(PointAt(0, 0));
// Trim to line segment now if requested, don't generate points that
// would just get discarded later.
- if(!seg ||
+ if(!asSegment ||
(n.Dot(a) > d + LENGTH_EPS && n.Dot(b) < d - LENGTH_EPS) ||
(n.Dot(b) > d + LENGTH_EPS && n.Dot(a) < d - LENGTH_EPS))
{
}
int i;
for(i = 0; i < ip_n; i++) {
- double t = (ip[i].Minus(ap)).DivPivoting(bp.Minus(ap));
+ double t = (ip[i].Minus(ap)).DivProjected(bp.Minus(ap));
// This is a point on the circle; but is it on the arc?
Point2d pp = ap.Plus((bp.Minus(ap)).ScaledBy(t));
double theta = atan2(pp.y, pp.x);
} else {
// General numerical solution by subdivision, fallback
int cnt = 0, level = 0;
- AllPointsIntersectingUntrimmed(a, b, &cnt, &level, &inters, seg, this);
+ AllPointsIntersectingUntrimmed(a, b, &cnt, &level, &inters, asSegment, this);
}
// Remove duplicate intersection points
int i, j;
for(i = 0; i < inters.n; i++) {
for(j = i + 1; j < inters.n; j++) {
- if(inters.elem[i].p.Equals(inters.elem[j].p)) {
- inters.elem[j].tag = 1;
+ if(inters[i].p.Equals(inters[j].p)) {
+ inters[j].tag = 1;
}
}
}
inters.RemoveTagged();
for(i = 0; i < inters.n; i++) {
- Point2d puv = inters.elem[i].p;
+ Point2d puv = inters[i].p;
// Make sure the point lies within the finite line segment
Vector pxyz = PointAt(puv.x, puv.y);
- double t = (pxyz.Minus(a)).DivPivoting(ba);
- if(seg && (t > 1 - LENGTH_EPS/bam || t < LENGTH_EPS/bam)) {
+ double t = (pxyz.Minus(a)).DivProjected(ba);
+ if(asSegment && (t > 1 - LENGTH_EPS/bam || t < LENGTH_EPS/bam)) {
continue;
}
// And that it lies inside our trim region
Point2d dummy = { 0, 0 };
- int c = (bsp) ? bsp->ClassifyPoint(puv, dummy, this) : SBspUv::OUTSIDE;
- if(trimmed && c == SBspUv::OUTSIDE) {
+ SBspUv::Class c = (bsp) ? bsp->ClassifyPoint(puv, dummy, this) : SBspUv::Class::OUTSIDE;
+ if(trimmed && c == SBspUv::Class::OUTSIDE) {
continue;
}
si.surfNormal = NormalAt(puv.x, puv.y);
si.pinter = puv;
si.srf = this;
- si.onEdge = (c != SBspUv::INSIDE);
+ si.onEdge = (c != SBspUv::Class::INSIDE);
l->Add(&si);
}
void SShell::AllPointsIntersecting(Vector a, Vector b,
List<SInter> *il,
- bool seg, bool trimmed, bool inclTangent)
+ bool asSegment, bool trimmed, bool inclTangent)
{
SSurface *ss;
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
- ss->AllPointsIntersecting(a, b, il, seg, trimmed, inclTangent);
+ ss->AllPointsIntersecting(a, b, il,
+ asSegment, trimmed, inclTangent);
}
}
-int SShell::ClassifyRegion(Vector edge_n, Vector inter_surf_n,
- Vector edge_surf_n)
+SShell::Class SShell::ClassifyRegion(Vector edge_n, Vector inter_surf_n,
+ Vector edge_surf_n) const
{
double dot = inter_surf_n.DirectionCosineWith(edge_n);
if(fabs(dot) < DOTP_TOL) {
// are coincident. Test the edge's surface normal
// to see if it's with same or opposite normals.
if(inter_surf_n.Dot(edge_surf_n) > 0) {
- return COINC_SAME;
+ return Class::COINC_SAME;
} else {
- return COINC_OPP;
+ return Class::COINC_OPP;
}
} else if(dot > 0) {
- return OUTSIDE;
+ return Class::OUTSIDE;
} else {
- return INSIDE;
+ return Class::INSIDE;
}
}
// using the closest intersection point. If the ray hits a surface on edge,
// then just reattempt in a different random direction.
//-----------------------------------------------------------------------------
-bool SShell::ClassifyEdge(int *indir, int *outdir,
+
+// table of vectors in 6 arbitrary directions covering 4 of the 8 octants.
+// use overlapping sets of 3 to reduce memory usage.
+static const double Random[8] = {1.278, 5.0103, 9.427, -2.331, 7.13, 2.954, 5.034, -4.777};
+
+bool SShell::ClassifyEdge(Class *indir, Class *outdir,
Vector ea, Vector eb,
Vector p,
Vector edge_n_in, Vector edge_n_out, Vector surf_n)
{
List<SInter> l = {};
- srand(0);
-
// First, check for edge-on-edge
int edge_inters = 0;
Vector inter_surf_n[2], inter_edge_n[2];
SSurface *srf;
for(srf = surface.First(); srf; srf = surface.NextAfter(srf)) {
- if(srf->LineEntirelyOutsideBbox(ea, eb, true)) continue;
+ if(srf->LineEntirelyOutsideBbox(ea, eb, /*asSegment=*/true)) continue;
SEdgeList *sel = &(srf->edges);
SEdge *se;
if(edge_inters < 2) {
// Edge-on-edge case
Point2d pm;
- srf->ClosestPointTo(p, &pm, false);
+ srf->ClosestPointTo(p, &pm, /*mustConverge=*/false);
// A vector normal to the surface, at the intersection point
inter_surf_n[edge_inters] = srf->NormalAt(pm);
// A vector normal to the intersecting edge (but within the
}
if(edge_inters == 2) {
- // TODO, make this use the appropriate curved normals
+ //! @todo make this use the appropriate curved normals
double dotp[2];
for(int i = 0; i < 2; i++) {
dotp[i] = edge_n_out.DirectionCosineWith(inter_surf_n[i]);
swap(inter_edge_n[0], inter_edge_n[1]);
}
- int coinc = (surf_n.Dot(inter_surf_n[0])) > 0 ? COINC_SAME : COINC_OPP;
+ Class coinc = (surf_n.Dot(inter_surf_n[0])) > 0 ? Class::COINC_SAME : Class::COINC_OPP;
if(fabs(dotp[0]) < DOTP_TOL && fabs(dotp[1]) < DOTP_TOL) {
// This is actually an edge on face case, just that the face
} else if(fabs(dotp[0]) < DOTP_TOL && dotp[1] > DOTP_TOL) {
if(edge_n_out.Dot(inter_edge_n[0]) > 0) {
*indir = coinc;
- *outdir = OUTSIDE;
+ *outdir = Class::OUTSIDE;
} else {
- *indir = INSIDE;
+ *indir = Class::INSIDE;
*outdir = coinc;
}
} else if(fabs(dotp[0]) < DOTP_TOL && dotp[1] < -DOTP_TOL) {
if(edge_n_out.Dot(inter_edge_n[0]) > 0) {
*indir = coinc;
- *outdir = INSIDE;
+ *outdir = Class::INSIDE;
} else {
- *indir = OUTSIDE;
+ *indir = Class::OUTSIDE;
*outdir = coinc;
}
} else if(dotp[0] > DOTP_TOL && dotp[1] > DOTP_TOL) {
- *indir = INSIDE;
- *outdir = OUTSIDE;
+ *indir = Class::INSIDE;
+ *outdir = Class::OUTSIDE;
} else if(dotp[0] < -DOTP_TOL && dotp[1] < -DOTP_TOL) {
- *indir = OUTSIDE;
- *outdir = INSIDE;
+ *indir = Class::OUTSIDE;
+ *outdir = Class::INSIDE;
} else {
// Edge is tangent to the shell at shell's edge, so can't be
// a boundary of the surface.
// the additional error from the line intersection.
for(srf = surface.First(); srf; srf = surface.NextAfter(srf)) {
- if(srf->LineEntirelyOutsideBbox(ea, eb, true)) continue;
+ if(srf->LineEntirelyOutsideBbox(ea, eb, /*asSegment=*/true)) continue;
Point2d puv;
- srf->ClosestPointTo(p, &(puv.x), &(puv.y), false);
+ srf->ClosestPointTo(p, &(puv.x), &(puv.y), /*mustConverge=*/false);
Vector pp = srf->PointAt(puv);
if((pp.Minus(p)).Magnitude() > LENGTH_EPS) continue;
Point2d dummy = { 0, 0 };
- int c = (srf->bsp) ? srf->bsp->ClassifyPoint(puv, dummy, srf) : SBspUv::OUTSIDE;
- if(c == SBspUv::OUTSIDE) continue;
+ SBspUv::Class c = (srf->bsp) ? srf->bsp->ClassifyPoint(puv, dummy, srf) : SBspUv::Class::OUTSIDE;
+ if(c == SBspUv::Class::OUTSIDE) continue;
// Edge-on-face (unless edge-on-edge above superceded)
Point2d pin, pout;
- srf->ClosestPointTo(p.Plus(edge_n_in), &pin, false);
- srf->ClosestPointTo(p.Plus(edge_n_out), &pout, false);
+ srf->ClosestPointTo(p.Plus(edge_n_in), &pin, /*mustConverge=*/false);
+ srf->ClosestPointTo(p.Plus(edge_n_out), &pout, /*mustConverge=*/false);
Vector surf_n_in = srf->NormalAt(pin),
surf_n_out = srf->NormalAt(pout);
// Cast a ray in a random direction (two-sided so that we test if
// the point lies on a surface, but use only one side for in/out
// testing)
- Vector ray = Vector::From(Random(1), Random(1), Random(1));
+ Vector ray = Vector::From(Random[cnt], Random[cnt+1], Random[cnt+2]);
AllPointsIntersecting(
- p.Minus(ray), p.Plus(ray), &l, false, true, false);
+ p.Minus(ray), p.Plus(ray), &l,
+ /*asSegment=*/false, /*trimmed=*/true, /*inclTangent=*/false);
// no intersections means it's outside
- *indir = OUTSIDE;
- *outdir = OUTSIDE;
+ *indir = Class::OUTSIDE;
+ *outdir = Class::OUTSIDE;
double dmin = VERY_POSITIVE;
bool onEdge = false;
edge_inters = 0;
SInter *si;
for(si = l.First(); si; si = l.NextAfter(si)) {
- double t = ((si->p).Minus(p)).DivPivoting(ray);
+ double t = ((si->p).Minus(p)).DivProjected(ray);
if(t*ray.Magnitude() < -LENGTH_EPS) {
// wrong side, doesn't count
continue;
// Edge does not lie on surface; either strictly inside
// or strictly outside
if((si->surfNormal).Dot(ray) > 0) {
- *indir = INSIDE;
- *outdir = INSIDE;
+ *indir = Class::INSIDE;
+ *outdir = Class::INSIDE;
} else {
- *indir = OUTSIDE;
- *outdir = OUTSIDE;
+ *indir = Class::OUTSIDE;
+ *outdir = Class::OUTSIDE;
}
onEdge = si->onEdge;
}
// then our ray always lies on edge, and that's okay. Otherwise
// try again in a different random direction.
if(!onEdge) break;
- if(cnt++ > 5) {
+ cnt++;
+ if(cnt > 5) {
dbp("can't find a ray that doesn't hit on edge!");
dbp("on edge = %d, edge_inters = %d", onEdge, edge_inters);
SS.nakedEdges.AddEdge(ea, eb);
return ret;
}
-bool SSurface::IsExtrusion(SBezier *of, Vector *alongp) {
+bool SSurface::IsExtrusion(SBezier *of, Vector *alongp) const {
int i;
if(degn != 1) return false;
}
bool SSurface::IsCylinder(Vector *axis, Vector *center, double *r,
- Vector *start, Vector *finish)
+ Vector *start, Vector *finish) const
{
SBezier sb;
if(!IsExtrusion(&sb, axis)) return false;
return true;
}
-SSurface SSurface::FromRevolutionOf(SBezier *sb, Vector pt, Vector axis,
- double thetas, double thetaf)
-{
+// Create a surface patch by revolving and possibly translating a curve.
+// Works for sections up to but not including 180 degrees.
+SSurface SSurface::FromRevolutionOf(SBezier *sb, Vector pt, Vector axis, double thetas,
+ double thetaf, double dists,
+ double distf) { // s is start, f is finish
SSurface ret = {};
-
-
ret.degm = sb->deg;
ret.degn = 2;
double dtheta = fabs(WRAP_SYMMETRIC(thetaf - thetas, 2*PI));
+ double w = cos(dtheta / 2);
- // We now wish to revolve the curve about the z axis
+ // Revolve the curve about the z axis
int i;
for(i = 0; i <= ret.degm; i++) {
Vector p = sb->ctrl[i];
Vector ps = p.RotatedAbout(pt, axis, thetas),
pf = p.RotatedAbout(pt, axis, thetaf);
- Vector ct;
- if(ps.Equals(pf)) {
- // Degenerate case: a control point lies on the axis of revolution,
- // so we get three coincident control points.
- ct = ps;
- } else {
- // Normal case, the control point sweeps out a circle.
- Vector c = ps.ClosestPointOnLine(pt, axis);
+ // The middle control point should be at the intersection of the tangents at ps and pf.
+ // This is equivalent but works for 0 <= angle < 180 degrees.
+ Vector mid = ps.Plus(pf).ScaledBy(0.5);
+ Vector c = ps.ClosestPointOnLine(pt, axis);
+ Vector ct = mid.Minus(c).ScaledBy(1 / (w * w)).Plus(c);
- Vector rs = ps.Minus(c),
- rf = pf.Minus(c);
-
- Vector ts = axis.Cross(rs),
- tf = axis.Cross(rf);
-
- ct = Vector::AtIntersectionOfLines(ps, ps.Plus(ts),
- pf, pf.Plus(tf),
- NULL, NULL, NULL);
+ // not sure this is needed
+ if(ps.Equals(pf)) {
+ ps = c;
+ ct = c;
+ pf = c;
}
-
- ret.ctrl[i][0] = ps;
- ret.ctrl[i][1] = ct;
- ret.ctrl[i][2] = pf;
+ // moving along the axis can create hilical surfaces (or straight extrusion if
+ // thetas==thetaf)
+ ret.ctrl[i][0] = ps.Plus(axis.ScaledBy(dists));
+ ret.ctrl[i][1] = ct.Plus(axis.ScaledBy((dists + distf) / 2));
+ ret.ctrl[i][2] = pf.Plus(axis.ScaledBy(distf));
ret.weight[i][0] = sb->weight[i];
- ret.weight[i][1] = sb->weight[i]*cos(dtheta/2);
+ ret.weight[i][1] = sb->weight[i] * w;
ret.weight[i][2] = sb->weight[i];
}
return ret;
}
-SSurface SSurface::FromTransformationOf(SSurface *a,
- Vector t, Quaternion q, double scale,
+SSurface SSurface::FromTransformationOf(SSurface *a, Vector t, Quaternion q, double scale,
bool includingTrims)
{
- SSurface ret = {};
+ bool needRotate = !EXACT(q.vx == 0.0 && q.vy == 0.0 && q.vz == 0.0 && q.w == 1.0);
+ bool needTranslate = !EXACT(t.x == 0.0 && t.y == 0.0 && t.z == 0.0);
+ bool needScale = !EXACT(scale == 1.0);
+ SSurface ret = {};
ret.h = a->h;
ret.color = a->color;
ret.face = a->face;
int i, j;
for(i = 0; i <= 3; i++) {
for(j = 0; j <= 3; j++) {
- ret.ctrl[i][j] = a->ctrl[i][j];
- ret.ctrl[i][j] = (ret.ctrl[i][j]).ScaledBy(scale);
- ret.ctrl[i][j] = (q.Rotate(ret.ctrl[i][j])).Plus(t);
-
+ Vector ctrl = a->ctrl[i][j];
+ if(needScale) {
+ ctrl = ctrl.ScaledBy(scale);
+ }
+ if(needRotate) {
+ ctrl = q.Rotate(ctrl);
+ }
+ if(needTranslate) {
+ ctrl = ctrl.Plus(t);
+ }
+ ret.ctrl[i][j] = ctrl;
ret.weight[i][j] = a->weight[i][j];
}
}
if(includingTrims) {
STrimBy *stb;
+ ret.trim.ReserveMore(a->trim.n);
for(stb = a->trim.First(); stb; stb = a->trim.NextAfter(stb)) {
STrimBy n = *stb;
- n.start = n.start.ScaledBy(scale);
- n.finish = n.finish.ScaledBy(scale);
- n.start = (q.Rotate(n.start)) .Plus(t);
- n.finish = (q.Rotate(n.finish)).Plus(t);
+ if(needScale) {
+ n.start = n.start.ScaledBy(scale);
+ n.finish = n.finish.ScaledBy(scale);
+ }
+ if(needRotate) {
+ n.start = q.Rotate(n.start);
+ n.finish = q.Rotate(n.finish);
+ }
+ if(needTranslate) {
+ n.start = n.start.Plus(t);
+ n.finish = n.finish.Plus(t);
+ }
ret.trim.Add(&n);
}
}
return ret;
}
-void SSurface::GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin) {
+void SSurface::GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin) const {
*ptMax = Vector::From(VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE);
*ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
}
}
-bool SSurface::LineEntirelyOutsideBbox(Vector a, Vector b, bool segment) {
+bool SSurface::LineEntirelyOutsideBbox(Vector a, Vector b, bool asSegment) const {
Vector amax, amin;
GetAxisAlignedBounding(&amax, &amin);
- if(!Vector::BoundingBoxIntersectsLine(amax, amin, a, b, segment)) {
+ if(!Vector::BoundingBoxIntersectsLine(amax, amin, a, b, asSegment)) {
// The line segment could fail to intersect the bbox, but lie entirely
// within it and intersect the surface.
if(a.OutsideAndNotOn(amax, amin) && b.OutsideAndNotOn(amax, amin)) {
// Generate the piecewise linear approximation of the trim stb, which applies
// to the curve sc.
//-----------------------------------------------------------------------------
-void SSurface::MakeTrimEdgesInto(SEdgeList *sel, int flags,
+void SSurface::MakeTrimEdgesInto(SEdgeList *sel, MakeAs flags,
SCurve *sc, STrimBy *stb)
{
Vector prev = Vector::From(0, 0, 0);
increment = 1;
}
for(i = first; i != (last + increment); i += increment) {
- Vector tpt, *pt = &(sc->pts.elem[i].p);
+ Vector tpt, *pt = &(sc->pts[i].p);
- if(flags & AS_UV) {
+ if(flags == MakeAs::UV) {
ClosestPointTo(*pt, &u, &v);
tpt = Vector::From(u, v, 0);
} else {
// the split curves from useCurvesFrom instead of the curves in our own
// shell.
//-----------------------------------------------------------------------------
-void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, int flags,
+void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, MakeAs flags,
SShell *useCurvesFrom)
{
STrimBy *stb;
// by taking the cross product of the surface normals. We choose the direction
// of this tangent so that its dot product with dir is positive.
//-----------------------------------------------------------------------------
-Vector SSurface::ExactSurfaceTangentAt(Vector p, SSurface *srfA, SSurface *srfB,
- Vector dir)
+Vector SSurface::ExactSurfaceTangentAt(Vector p, SSurface *srfA, SSurface *srfB, Vector dir)
{
Point2d puva, puvb;
srfA->ClosestPointTo(p, &puva);
// add its exact form to sbl. Otherwise, add its piecewise linearization to
// sel.
//-----------------------------------------------------------------------------
-void SSurface::MakeSectionEdgesInto(SShell *shell,
- SEdgeList *sel, SBezierList *sbl)
+void SSurface::MakeSectionEdgesInto(SShell *shell, SEdgeList *sel, SBezierList *sbl)
{
STrimBy *stb;
for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) {
sbl->l.Add(&keep_bef);
} else if(sbl && !sel && !sc->isExact) {
// We must approximate this trim curve, as piecewise cubic sections.
- SSurface *srfA = shell->surface.FindById(sc->surfA),
- *srfB = shell->surface.FindById(sc->surfB);
+ SSurface *srfA = shell->surface.FindById(sc->surfA);
+ SSurface *srfB = shell->surface.FindById(sc->surfB);
Vector s = stb->backwards ? stb->finish : stb->start,
f = stb->backwards ? stb->start : stb->finish;
int sp, fp;
for(sp = 0; sp < sc->pts.n; sp++) {
- if(s.Equals(sc->pts.elem[sp].p)) break;
+ if(s.Equals(sc->pts[sp].p)) break;
}
if(sp >= sc->pts.n) return;
for(fp = sp; fp < sc->pts.n; fp++) {
- if(f.Equals(sc->pts.elem[fp].p)) break;
+ if(f.Equals(sc->pts[fp].p)) break;
}
if(fp >= sc->pts.n) return;
// So now the curve we want goes from elem[sp] to elem[fp]
for(;;) {
// So construct a cubic Bezier with the correct endpoints
// and tangents for the current span.
- Vector st = sc->pts.elem[sp].p,
- ft = sc->pts.elem[fpt].p,
+ Vector st = sc->pts[sp].p,
+ ft = sc->pts[fpt].p,
sf = ft.Minus(st);
double m = sf.Magnitude() / 3;
int i;
bool tooFar = false;
for(i = sp + 1; i <= (fpt - 1); i++) {
- Vector p = sc->pts.elem[i].p;
+ Vector p = sc->pts[i].p;
double t;
- sb.ClosestPointTo(p, &t, false);
+ sb.ClosestPointTo(p, &t, /*mustConverge=*/false);
Vector pp = sb.PointAt(t);
if((pp.Minus(p)).Magnitude() > SS.ChordTolMm()/2) {
tooFar = true;
sp = fpt;
}
} else {
- if(sel) MakeTrimEdgesInto(sel, AS_XYZ, sc, stb);
+ if(sel) MakeTrimEdgesInto(sel, MakeAs::XYZ, sc, stb);
}
}
}
void SSurface::TriangulateInto(SShell *shell, SMesh *sm) {
SEdgeList el = {};
- MakeEdgesInto(shell, &el, AS_UV);
+ MakeEdgesInto(shell, &el, MakeAs::UV);
SPolygon poly = {};
- if(el.AssemblePolygon(&poly, NULL, true)) {
+ if(el.AssemblePolygon(&poly, NULL, /*keepDir=*/true)) {
int i, start = sm->l.n;
if(degm == 1 && degn == 1) {
// A surface with curvature along one direction only; so
STriMeta meta = { face, color };
for(i = start; i < sm->l.n; i++) {
- STriangle *st = &(sm->l.elem[i]);
+ STriangle *st = &(sm->l[i]);
st->meta = meta;
- if(st->meta.color.alpha != 255) sm->isTransparent = true;
st->an = NormalAt(st->a.x, st->a.y);
st->bn = NormalAt(st->b.x, st->b.y);
st->cn = NormalAt(st->c.x, st->c.y);
// normal. We therefore must reverse all our trim curves too. The uv
// coordinates change, but trim curves are stored as xyz so nothing happens
//-----------------------------------------------------------------------------
-void SSurface::Reverse(void) {
+void SSurface::Reverse() {
int i, j;
for(i = 0; i < (degm+1)/2; i++) {
for(j = 0; j <= degn; j++) {
}
}
-void SSurface::Clear(void) {
+void SSurface::Clear() {
trim.Clear();
}
Vector n = sbls->normal.ScaledBy(-1);
Vector u = n.Normal(0), v = n.Normal(1);
Vector orig = sbls->point;
- double umax = 1e-10, umin = 1e10;
+ double umax = VERY_NEGATIVE, umin = VERY_POSITIVE;
sbls->GetBoundingProjd(u, orig, &umin, &umax);
- double vmax = 1e-10, vmin = 1e10;
+ double vmax = VERY_NEGATIVE, vmin = VERY_POSITIVE;
sbls->GetBoundingProjd(v, orig, &vmin, &vmax);
// and now fix things up so that all u and v lie between 0 and 1
orig = orig.Plus(u.ScaledBy(umin));
STrimBy stb0, stb1;
// The translated curves trim the flat top and bottom surfaces.
- stb0 = STrimBy::EntireCurve(this, hc0, false);
- stb1 = STrimBy::EntireCurve(this, hc1, true);
+ stb0 = STrimBy::EntireCurve(this, hc0, /*backwards=*/false);
+ stb1 = STrimBy::EntireCurve(this, hc1, /*backwards=*/true);
(surface.FindById(hs0))->trim.Add(&stb0);
(surface.FindById(hs1))->trim.Add(&stb1);
// The translated curves also trim the surface of extrusion.
- stb0 = STrimBy::EntireCurve(this, hc0, true);
- stb1 = STrimBy::EntireCurve(this, hc1, false);
+ stb0 = STrimBy::EntireCurve(this, hc0, /*backwards=*/true);
+ stb1 = STrimBy::EntireCurve(this, hc1, /*backwards=*/false);
(surface.FindById(hsext))->trim.Add(&stb0);
(surface.FindById(hsext))->trim.Add(&stb1);
int i;
for(i = 0; i < trimLines.n; i++) {
- TrimLine *tl = &(trimLines.elem[i]);
+ TrimLine *tl = &(trimLines[i]);
SSurface *ss = surface.FindById(tl->hs);
- TrimLine *tlp = &(trimLines.elem[WRAP(i-1, trimLines.n)]);
+ TrimLine *tlp = &(trimLines[WRAP(i-1, trimLines.n)]);
STrimBy stb;
- stb = STrimBy::EntireCurve(this, tl->hc, true);
+ stb = STrimBy::EntireCurve(this, tl->hc, /*backwards=*/true);
ss->trim.Add(&stb);
- stb = STrimBy::EntireCurve(this, tlp->hc, false);
+ stb = STrimBy::EntireCurve(this, tlp->hc, /*backwards=*/false);
ss->trim.Add(&stb);
(curve.FindById(tl->hc))->surfA = ss->h;
}
}
-
-typedef struct {
- hSSurface d[4];
-} Revolved;
-
-void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbaColor color, Group *group)
+bool SShell::CheckNormalAxisRelationship(SBezierLoopSet *sbls, Vector pt, Vector axis, double da, double dx)
+// Check that the direction of revolution/extrusion ends up parallel to the normal of
+// the sketch, on the side of the axis where the sketch is.
{
SBezierLoop *sbl;
-
- int i0 = surface.n, i;
-
- // Normalize the axis direction so that the direction of revolution
- // ends up parallel to the normal of the sketch, on the side of the
- // axis where the sketch is.
Vector pto;
double md = VERY_NEGATIVE;
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
// if we choose a point that lies on the axis, for example.
// (And our surface will be self-intersecting if the sketch
// spans the axis, so don't worry about that.)
- Vector p = sb->Start();
- double d = p.DistanceToLine(pt, axis);
- if(d > md) {
- md = d;
- pto = p;
+ for(int i = 0; i <= sb->deg; i++) {
+ Vector p = sb->ctrl[i];
+ double d = p.DistanceToLine(pt, axis);
+ if(d > md) {
+ md = d;
+ pto = p;
+ }
}
}
}
Vector ptc = pto.ClosestPointOnLine(pt, axis),
- up = (pto.Minus(ptc)).WithMagnitude(1),
- vp = (sbls->normal).Cross(up);
- if(vp.Dot(axis) < 0) {
+ up = axis.Cross(pto.Minus(ptc)).ScaledBy(da),
+ vp = up.Plus(axis.ScaledBy(dx));
+
+ return (vp.Dot(sbls->normal) > 0);
+}
+
+// sketch must not contain the axis of revolution as a non-construction line for helix
+void SShell::MakeFromHelicalRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
+ RgbaColor color, Group *group, double angles,
+ double anglef, double dists, double distf) {
+ int i0 = surface.n; // number of pre-existing surfaces
+ SBezierLoop *sbl;
+ // for testing - hard code the axial distance, and number of sections.
+ // distance will need to be parameters in the future.
+ double dist = distf - dists;
+ int sections = (int)(fabs(anglef - angles) / (PI / 2) + 1);
+ double wedge = (anglef - angles) / sections;
+ int startMapping = Group::REMAP_LATHE_START, endMapping = Group::REMAP_LATHE_END;
+
+ if(CheckNormalAxisRelationship(sbls, pt, axis, anglef-angles, distf-dists)) {
+ swap(angles, anglef);
+ swap(dists, distf);
+ dist = -dist;
+ wedge = -wedge;
+ swap(startMapping, endMapping);
+ }
+
+ // Define a coordinate system to contain the original sketch, and get
+ // a bounding box in that csys
+ Vector n = sbls->normal.ScaledBy(-1);
+ Vector u = n.Normal(0), v = n.Normal(1);
+ Vector orig = sbls->point;
+ double umax = VERY_NEGATIVE, umin = VERY_POSITIVE;
+ sbls->GetBoundingProjd(u, orig, &umin, &umax);
+ double vmax = VERY_NEGATIVE, vmin = VERY_POSITIVE;
+ sbls->GetBoundingProjd(v, orig, &vmin, &vmax);
+ // and now fix things up so that all u and v lie between 0 and 1
+ orig = orig.Plus(u.ScaledBy(umin));
+ orig = orig.Plus(v.ScaledBy(vmin));
+ u = u.ScaledBy(umax - umin);
+ v = v.ScaledBy(vmax - vmin);
+
+ // So we can now generate the end caps of the extrusion within
+ // a translated and rotated (and maybe mirrored) version of that csys.
+ SSurface s0, s1;
+ s0 = SSurface::FromPlane(orig.RotatedAbout(pt, axis, angles).Plus(axis.ScaledBy(dists)),
+ u.RotatedAbout(axis, angles), v.RotatedAbout(axis, angles));
+ s0.color = color;
+
+ hEntity face0 = group->Remap(Entity::NO_ENTITY, startMapping);
+ s0.face = face0.v;
+
+ s1 = SSurface::FromPlane(
+ orig.Plus(u).RotatedAbout(pt, axis, anglef).Plus(axis.ScaledBy(distf)),
+ u.ScaledBy(-1).RotatedAbout(axis, anglef), v.RotatedAbout(axis, anglef));
+ s1.color = color;
+
+ hEntity face1 = group->Remap(Entity::NO_ENTITY, endMapping);
+ s1.face = face1.v;
+
+ hSSurface hs0 = surface.AddAndAssignId(&s0);
+ hSSurface hs1 = surface.AddAndAssignId(&s1);
+
+ // Now we actually build and trim the swept surfaces. One loop at a time.
+ for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
+ int i, j;
+ SBezier *sb;
+ List<std::vector<hSSurface>> hsl = {};
+
+ // This is where all the NURBS are created and Remapped to the generating curve
+ for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
+ std::vector<hSSurface> revs(sections);
+ for(j = 0; j < sections; j++) {
+ if((dist == 0) && sb->deg == 1 &&
+ (sb->ctrl[0]).DistanceToLine(pt, axis) < LENGTH_EPS &&
+ (sb->ctrl[1]).DistanceToLine(pt, axis) < LENGTH_EPS) {
+ // This is a line on the axis of revolution; it does
+ // not contribute a surface.
+ revs[j].v = 0;
+ } else {
+ SSurface ss = SSurface::FromRevolutionOf(
+ sb, pt, axis, angles + (wedge)*j, angles + (wedge) * (j + 1),
+ dists + j * dist / sections, dists + (j + 1) * dist / sections);
+ ss.color = color;
+ if(sb->entity != 0) {
+ hEntity he;
+ he.v = sb->entity;
+ hEntity hface = group->Remap(he, Group::REMAP_LINE_TO_FACE);
+ if(SK.entity.FindByIdNoOops(hface) != NULL) {
+ ss.face = hface.v;
+ }
+ }
+ revs[j] = surface.AddAndAssignId(&ss);
+ }
+ }
+ hsl.Add(&revs);
+ }
+ // Still the same loop. Need to create trim curves
+ for(i = 0; i < sbl->l.n; i++) {
+ std::vector<hSSurface> revs = hsl[i], revsp = hsl[WRAP(i - 1, sbl->l.n)];
+
+ sb = &(sbl->l[i]);
+
+ // we will need the grid t-values for this entire row of surfaces
+ List<double> t_values;
+ t_values = {};
+ if (revs[0].v) {
+ double ps = 0.0;
+ t_values.Add(&ps);
+ (surface.FindById(revs[0]))->MakeTriangulationGridInto(
+ &t_values, 0.0, 1.0, true, 0);
+ }
+ // we generate one more curve than we did surfaces
+ for(j = 0; j <= sections; j++) {
+ SCurve sc;
+ Quaternion qs = Quaternion::From(axis, angles + wedge * j);
+ // we want Q*(x - p) + p = Q*x + (p - Q*p)
+ Vector ts =
+ pt.Minus(qs.Rotate(pt)).Plus(axis.ScaledBy(dists + j * dist / sections));
+
+ // If this input curve generated a surface, then trim that
+ // surface with the rotated version of the input curve.
+ if(revs[0].v) { // not d[j] because crash on j==sections
+ sc = {};
+ sc.isExact = true;
+ sc.exact = sb->TransformedBy(ts, qs, 1.0);
+ // make the PWL for the curve based on t value list
+ for(int x = 0; x < t_values.n; x++) {
+ SCurvePt scpt;
+ scpt.tag = 0;
+ scpt.p = sc.exact.PointAt(t_values[x]);
+ scpt.vertex = (x == 0) || (x == (t_values.n - 1));
+ sc.pts.Add(&scpt);
+ }
+
+ // the surfaces already exists so trim with this curve
+ if(j < sections) {
+ sc.surfA = revs[j];
+ } else {
+ sc.surfA = hs1; // end cap
+ }
+
+ if(j > 0) {
+ sc.surfB = revs[j - 1];
+ } else {
+ sc.surfB = hs0; // staring cap
+ }
+
+ hSCurve hcb = curve.AddAndAssignId(&sc);
+
+ STrimBy stb;
+ stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/true);
+ (surface.FindById(sc.surfA))->trim.Add(&stb);
+ stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/false);
+ (surface.FindById(sc.surfB))->trim.Add(&stb);
+ } else if(j == 0) { // curve was on the rotation axis and is shared by the end caps.
+ sc = {};
+ sc.isExact = true;
+ sc.exact = sb->TransformedBy(ts, qs, 1.0);
+ (sc.exact).MakePwlInto(&(sc.pts));
+ sc.surfA = hs1; // end cap
+ sc.surfB = hs0; // staring cap
+ hSCurve hcb = curve.AddAndAssignId(&sc);
+
+ STrimBy stb;
+ stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/true);
+ (surface.FindById(sc.surfA))->trim.Add(&stb);
+ stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/false);
+ (surface.FindById(sc.surfB))->trim.Add(&stb);
+ }
+
+ // And if this input curve and the one after it both generated
+ // surfaces, then trim both of those by the appropriate
+ // curve based on the control points.
+ if((j < sections) && revs[j].v && revsp[j].v) {
+ SSurface *ss = surface.FindById(revs[j]);
+
+ sc = {};
+ sc.isExact = true;
+ sc.exact = SBezier::From(ss->ctrl[0][0], ss->ctrl[0][1], ss->ctrl[0][2]);
+ sc.exact.weight[1] = ss->weight[0][1];
+ double max_dt = 0.5;
+ if (sc.exact.deg > 1) max_dt = 0.125;
+ (sc.exact).MakePwlInto(&(sc.pts), 0.0, max_dt);
+ sc.surfA = revs[j];
+ sc.surfB = revsp[j];
+
+ hSCurve hcc = curve.AddAndAssignId(&sc);
+
+ STrimBy stb;
+ stb = STrimBy::EntireCurve(this, hcc, /*backwards=*/false);
+ (surface.FindById(sc.surfA))->trim.Add(&stb);
+ stb = STrimBy::EntireCurve(this, hcc, /*backwards=*/true);
+ (surface.FindById(sc.surfB))->trim.Add(&stb);
+ }
+ }
+ t_values.Clear();
+ }
+
+ hsl.Clear();
+ }
+
+ if(dist == 0) {
+ MakeFirstOrderRevolvedSurfaces(pt, axis, i0);
+ }
+}
+
+void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbaColor color,
+ Group *group) {
+ int i0 = surface.n; // number of pre-existing surfaces
+ SBezierLoop *sbl;
+
+ if(CheckNormalAxisRelationship(sbls, pt, axis, 1.0, 0.0)) {
axis = axis.ScaledBy(-1);
}
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
int i, j;
SBezier *sb;
- List<Revolved> hsl = {};
+ List<std::vector<hSSurface>> hsl = {};
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
- Revolved revs;
+ std::vector<hSSurface> revs(4);
for(j = 0; j < 4; j++) {
if(sb->deg == 1 &&
(sb->ctrl[0]).DistanceToLine(pt, axis) < LENGTH_EPS &&
{
// This is a line on the axis of revolution; it does
// not contribute a surface.
- revs.d[j].v = 0;
+ revs[j].v = 0;
} else {
- SSurface ss = SSurface::FromRevolutionOf(sb, pt, axis,
- (PI/2)*j,
- (PI/2)*(j+1));
+ SSurface ss = SSurface::FromRevolutionOf(sb, pt, axis, (PI / 2) * j,
+ (PI / 2) * (j + 1), 0.0, 0.0);
ss.color = color;
if(sb->entity != 0) {
hEntity he;
ss.face = hface.v;
}
}
- revs.d[j] = surface.AddAndAssignId(&ss);
+ revs[j] = surface.AddAndAssignId(&ss);
}
}
hsl.Add(&revs);
}
for(i = 0; i < sbl->l.n; i++) {
- Revolved revs = hsl.elem[i],
- revsp = hsl.elem[WRAP(i-1, sbl->l.n)];
+ std::vector<hSSurface> revs = hsl[i],
+ revsp = hsl[WRAP(i-1, sbl->l.n)];
- sb = &(sbl->l.elem[i]);
+ sb = &(sbl->l[i]);
for(j = 0; j < 4; j++) {
SCurve sc;
// If this input curve generate a surface, then trim that
// surface with the rotated version of the input curve.
- if(revs.d[j].v) {
+ if(revs[j].v) {
sc = {};
sc.isExact = true;
sc.exact = sb->TransformedBy(ts, qs, 1.0);
(sc.exact).MakePwlInto(&(sc.pts));
- sc.surfA = revs.d[j];
- sc.surfB = revs.d[WRAP(j-1, 4)];
+ sc.surfA = revs[j];
+ sc.surfB = revs[WRAP(j-1, 4)];
hSCurve hcb = curve.AddAndAssignId(&sc);
STrimBy stb;
- stb = STrimBy::EntireCurve(this, hcb, true);
+ stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/true);
(surface.FindById(sc.surfA))->trim.Add(&stb);
- stb = STrimBy::EntireCurve(this, hcb, false);
+ stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/false);
(surface.FindById(sc.surfB))->trim.Add(&stb);
}
// And if this input curve and the one after it both generated
// surfaces, then trim both of those by the appropriate
// circle.
- if(revs.d[j].v && revsp.d[j].v) {
- SSurface *ss = surface.FindById(revs.d[j]);
+ if(revs[j].v && revsp[j].v) {
+ SSurface *ss = surface.FindById(revs[j]);
sc = {};
sc.isExact = true;
ss->ctrl[0][2]);
sc.exact.weight[1] = ss->weight[0][1];
(sc.exact).MakePwlInto(&(sc.pts));
- sc.surfA = revs.d[j];
- sc.surfB = revsp.d[j];
+ sc.surfA = revs[j];
+ sc.surfB = revsp[j];
hSCurve hcc = curve.AddAndAssignId(&sc);
STrimBy stb;
- stb = STrimBy::EntireCurve(this, hcc, false);
+ stb = STrimBy::EntireCurve(this, hcc, /*backwards=*/false);
(surface.FindById(sc.surfA))->trim.Add(&stb);
- stb = STrimBy::EntireCurve(this, hcc, true);
+ stb = STrimBy::EntireCurve(this, hcc, /*backwards=*/true);
(surface.FindById(sc.surfB))->trim.Add(&stb);
}
}
hsl.Clear();
}
+ MakeFirstOrderRevolvedSurfaces(pt, axis, i0);
+}
+
+void SShell::MakeFirstOrderRevolvedSurfaces(Vector pt, Vector axis, int i0) {
+ int i;
+
for(i = i0; i < surface.n; i++) {
- SSurface *srf = &(surface.elem[i]);
+ SSurface *srf = &(surface[i]);
// Revolution of a line; this is potentially a plane, which we can
// rewrite to have degree (1, 1).
continue;
}
}
-
}
-
}
void SShell::MakeFromCopyOf(SShell *a) {
+ ssassert(this != a, "Can't make from copy of self");
MakeFromTransformationOf(a,
Vector::From(0, 0, 0), Quaternion::IDENTITY, 1.0);
}
Vector t, Quaternion q, double scale)
{
booleanFailed = false;
-
+ surface.ReserveMore(a->surface.n);
SSurface *s;
for(s = a->surface.First(); s; s = a->surface.NextAfter(s)) {
SSurface n;
- n = SSurface::FromTransformationOf(s, t, q, scale, true);
+ n = SSurface::FromTransformationOf(s, t, q, scale, /*includingTrims=*/true);
surface.Add(&n); // keeping the old ID
}
+ curve.ReserveMore(a->curve.n);
SCurve *c;
for(c = a->curve.First(); c; c = a->curve.NextAfter(c)) {
SCurve n;
void SShell::MakeEdgesInto(SEdgeList *sel) {
SSurface *s;
for(s = surface.First(); s; s = surface.NextAfter(s)) {
- s->MakeEdgesInto(this, sel, SSurface::AS_XYZ);
+ s->MakeEdgesInto(this, sel, SSurface::MakeAs::XYZ);
}
}
-void SShell::MakeSectionEdgesInto(Vector n, double d,
- SEdgeList *sel, SBezierList *sbl)
+void SShell::MakeSectionEdgesInto(Vector n, double d, SEdgeList *sel, SBezierList *sbl)
{
SSurface *s;
for(s = surface.First(); s; s = surface.NextAfter(s)) {
}
void SShell::TriangulateInto(SMesh *sm) {
- SSurface *s;
- for(s = surface.First(); s; s = surface.NextAfter(s)) {
- s->TriangulateInto(this, sm);
+#pragma omp parallel for
+ for(int i=0; i<surface.n; i++) {
+ SSurface *s = &surface[i];
+ SMesh m;
+ s->TriangulateInto(this, &m);
+ #pragma omp critical
+ sm->MakeFromCopyOf(&m);
+ m.Clear();
}
}
-bool SShell::IsEmpty(void) {
- return (surface.n == 0);
+bool SShell::IsEmpty() const {
+ return surface.IsEmpty();
}
-void SShell::Clear(void) {
+void SShell::Clear() {
SSurface *s;
for(s = surface.First(); s; s = surface.NextAfter(s)) {
s->Clear();
}
curve.Clear();
}
-
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
-#ifndef __SURFACE_H
-#define __SURFACE_H
-
-// Utility functions, Bernstein polynomials of order 1-3 and their derivatives.
-double Bernstein(int k, int deg, double t);
-double BernsteinDerivative(int k, int deg, double t);
+#ifndef SOLVESPACE_SURFACE_H
+#define SOLVESPACE_SURFACE_H
+class SBezierList;
class SSurface;
class SCurvePt;
SBspUv *more;
- enum {
+ enum class Class : uint32_t {
INSIDE = 100,
OUTSIDE = 200,
EDGE_PARALLEL = 300,
EDGE_OTHER = 500
};
- static SBspUv *Alloc(void);
+ static SBspUv *Alloc();
static SBspUv *From(SEdgeList *el, SSurface *srf);
- void ScalePoints(Point2d *pt, Point2d *a, Point2d *b, SSurface *srf);
+ void ScalePoints(Point2d *pt, Point2d *a, Point2d *b, SSurface *srf) const;
double ScaledSignedDistanceToLine(Point2d pt, Point2d a, Point2d b,
- SSurface *srf);
- double ScaledDistanceToLine(Point2d pt, Point2d a, Point2d b, bool seg,
- SSurface *srf);
+ SSurface *srf) const;
+ double ScaledDistanceToLine(Point2d pt, Point2d a, Point2d b, bool asSegment,
+ SSurface *srf) const;
void InsertEdge(Point2d a, Point2d b, SSurface *srf);
- static SBspUv *InsertOrCreateEdge(SBspUv *where, const Point2d &ea, const Point2d &eb, SSurface *srf);
- int ClassifyPoint(Point2d p, Point2d eb, SSurface *srf);
- int ClassifyEdge(Point2d ea, Point2d eb, SSurface *srf);
- double MinimumDistanceToEdge(Point2d p, SSurface *srf);
+ static SBspUv *InsertOrCreateEdge(SBspUv *where, Point2d ea, Point2d eb, SSurface *srf);
+ Class ClassifyPoint(Point2d p, Point2d eb, SSurface *srf) const;
+ Class ClassifyEdge(Point2d ea, Point2d eb, SSurface *srf) const;
+ double MinimumDistanceToEdge(Point2d p, SSurface *srf) const;
};
// Now the data structures to represent a shell of trimmed rational polynomial
uint32_t v;
};
+template<>
+struct IsHandleOracle<hSSurface> : std::true_type {};
+
class hSCurve {
public:
uint32_t v;
};
+template<>
+struct IsHandleOracle<hSCurve> : std::true_type {};
+
// Stuff for rational polynomial curves, of degree one to three. These are
// our inputs, and are also calculated for certain exact surface-surface
// intersections.
double weight[4];
uint32_t entity;
- Vector PointAt(double t);
- Vector TangentAt(double t);
- void ClosestPointTo(Vector p, double *t, bool converge=true);
- void SplitAt(double t, SBezier *bef, SBezier *aft);
- bool PointOnThisAndCurve(SBezier *sbb, Vector *p);
-
- Vector Start(void);
- Vector Finish(void);
- bool Equals(SBezier *b);
- void MakePwlInto(SEdgeList *sel, double chordTol=0);
- void MakePwlInto(List<SCurvePt> *l, double chordTol=0);
- void MakePwlInto(SContour *sc, double chordTol=0);
- void MakePwlInto(List<Vector> *l, double chordTol=0);
- void MakePwlWorker(List<Vector> *l, double ta, double tb, double chordTol);
- void MakePwlInitialWorker(List<Vector> *l, double ta, double tb, double chordTol);
-
- void AllIntersectionsWith(SBezier *sbb, SPointList *spl);
- void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax);
- void Reverse(void);
-
- bool IsInPlane(Vector n, double d);
- bool IsCircle(Vector axis, Vector *center, double *r);
- bool IsRational(void);
-
- SBezier TransformedBy(Vector t, Quaternion q, double scale);
+ Vector PointAt(double t) const;
+ Vector TangentAt(double t) const;
+ void ClosestPointTo(Vector p, double *t, bool mustConverge=true) const;
+ void SplitAt(double t, SBezier *bef, SBezier *aft) const;
+ bool PointOnThisAndCurve(const SBezier *sbb, Vector *p) const;
+
+ Vector Start() const;
+ Vector Finish() const;
+ bool Equals(SBezier *b) const;
+ void MakePwlInto(SEdgeList *sel, double chordTol=0, double max_dt=0.0) const;
+ void MakePwlInto(List<SCurvePt> *l, double chordTol=0, double max_dt=0.0) const;
+ void MakePwlInto(SContour *sc, double chordTol=0, double max_dt=0.0) const;
+ void MakePwlInto(List<Vector> *l, double chordTol=0, double max_dt=0.0) const;
+ void MakePwlWorker(List<Vector> *l, double ta, double tb, double chordTol, double max_dt) const;
+ void MakePwlInitialWorker(List<Vector> *l, double ta, double tb, double chordTol, double max_dt) const;
+ void MakeNonrationalCubicInto(SBezierList *bl, double tolerance, int depth = 0) const;
+
+ void AllIntersectionsWith(const SBezier *sbb, SPointList *spl) const;
+ void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax) const;
+ void Reverse();
+
+ bool IsInPlane(Vector n, double d) const;
+ bool IsCircle(Vector axis, Vector *center, double *r) const;
+ bool IsRational() const;
+
+ SBezier TransformedBy(Vector t, Quaternion q, double scale) const;
SBezier InPerspective(Vector u, Vector v, Vector n,
- Vector origin, double cameraTan);
+ Vector origin, double cameraTan) const;
void ScaleSelfBy(double s);
static SBezier From(Vector p0, Vector p1, Vector p2, Vector p3);
public:
List<SBezier> l;
- void Clear(void);
+ void Clear();
void ScaleSelfBy(double s);
- void CullIdenticalBeziers(void);
- void AllIntersectionsWith(SBezierList *sblb, SPointList *spl);
+ void CullIdenticalBeziers(bool both=true);
+ void AllIntersectionsWith(SBezierList *sblb, SPointList *spl) const;
bool GetPlaneContainingBeziers(Vector *p, Vector *u, Vector *v,
- Vector *notCoplanarAt);
+ Vector *notCoplanarAt) const;
};
class SBezierLoop {
int tag;
List<SBezier> l;
- inline void Clear(void) { l.Clear(); }
- bool IsClosed(void);
- void Reverse(void);
- void MakePwlInto(SContour *sc, double chordTol=0);
- void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax);
+ inline void Clear() { l.Clear(); }
+ bool IsClosed() const;
+ void Reverse();
+ void MakePwlInto(SContour *sc, double chordTol=0) const;
+ void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax) const;
static SBezierLoop FromCurves(SBezierList *spcl,
bool *allClosed, SEdge *errorAt);
List<SBezierLoop> l;
Vector normal;
Vector point;
+ double area;
static SBezierLoopSet From(SBezierList *spcl, SPolygon *poly,
double chordTol,
bool *allClosed, SEdge *errorAt,
- SBezierList *openContours);
+ SBezierLoopSet *openContours);
- void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax);
- void MakePwlInto(SPolygon *sp);
- void Clear(void);
+ void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax) const;
+ double SignedArea();
+ void MakePwlInto(SPolygon *sp) const;
+ void Clear();
};
class SBezierLoopSetSet {
double chordTol,
bool *allClosed, SEdge *notClosedAt,
bool *allCoplanar, Vector *notCoplanarAt,
- SBezierList *openContours);
+ SBezierLoopSet *openContours);
void AddOpenPath(SBezier *sb);
- void Clear(void);
+ void Clear();
};
// Stuff for the surface trim curves: piecewise linear
// therefore must get new hSCurves assigned. For the curves in A and B,
// we use newH to record their new handle in C.
hSCurve newH;
- enum {
- FROM_A = 100,
- FROM_B = 200,
- FROM_INTERSECTION = 300
+ enum class Source : uint32_t {
+ A = 100,
+ B = 200,
+ INTERSECTION = 300
};
- int source;
+ Source source;
bool isExact;
SBezier exact;
hSSurface surfA;
hSSurface surfB;
- static SCurve FromTransformationOf(SCurve *a, Vector t, Quaternion q,
- double scale);
+ static SCurve FromTransformationOf(SCurve *a, Vector t,
+ Quaternion q, double scale);
SCurve MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
- SSurface *srfA, SSurface *srfB);
+ SSurface *srfA, SSurface *srfB) const;
void RemoveShortSegments(SSurface *srfA, SSurface *srfB);
- SSurface *GetSurfaceA(SShell *a, SShell *b);
- SSurface *GetSurfaceB(SShell *a, SShell *b);
+ SSurface *GetSurfaceA(SShell *a, SShell *b) const;
+ SSurface *GetSurfaceB(SShell *a, SShell *b) const;
- void Clear(void);
+ void Clear();
+ void GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin) const;
};
// A segment of a curve by which a surface is trimmed: indicates which curve,
Vector start;
Vector finish;
- static STrimBy EntireCurve(SShell *shell, hSCurve hsc, bool bkwds);
+ static STrimBy EntireCurve(SShell *shell, hSCurve hsc, bool backwards);
};
// An intersection point between a line and a surface
// A rational polynomial surface in Bezier form.
class SSurface {
public:
+
+ enum class CombineAs : uint32_t {
+ UNION = 10,
+ DIFFERENCE = 11,
+ INTERSECTION = 12
+ };
+
int tag;
hSSurface h;
Point2d cached;
static SSurface FromExtrusionOf(SBezier *spc, Vector t0, Vector t1);
- static SSurface FromRevolutionOf(SBezier *sb, Vector pt, Vector axis,
- double thetas, double thetaf);
+ static SSurface FromRevolutionOf(SBezier *sb, Vector pt, Vector axis, double thetas,
+ double thetaf, double dists, double distf);
static SSurface FromPlane(Vector pt, Vector u, Vector v);
static SSurface FromTransformationOf(SSurface *a, Vector t, Quaternion q,
double scale,
SShell *shell, SShell *sha, SShell *shb);
void FindChainAvoiding(SEdgeList *src, SEdgeList *dest, SPointList *avoid);
SSurface MakeCopyTrimAgainst(SShell *parent, SShell *a, SShell *b,
- SShell *into, int type);
+ SShell *into, SSurface::CombineAs type, int dbg_index);
void TrimFromEdgeList(SEdgeList *el, bool asUv);
void IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
SShell *into);
int tag;
Point2d p;
} Inter;
- void WeightControlPoints(void);
- void UnWeightControlPoints(void);
+ void WeightControlPoints();
+ void UnWeightControlPoints();
void CopyRowOrCol(bool row, int this_ij, SSurface *src, int src_ij);
void BlendRowOrCol(bool row, int this_ij, SSurface *a, int a_ij,
SSurface *b, int b_ij);
- double DepartureFromCoplanar(void);
+ double DepartureFromCoplanar() const;
void SplitInHalf(bool byU, SSurface *sa, SSurface *sb);
void AllPointsIntersecting(Vector a, Vector b,
- List<SInter> *l,
- bool seg, bool trimmed, bool inclTangent);
+ List<SInter> *l,
+ bool asSegment, bool trimmed, bool inclTangent);
void AllPointsIntersectingUntrimmed(Vector a, Vector b,
- int *cnt, int *level,
- List<Inter> *l, bool segment,
- SSurface *sorig);
+ int *cnt, int *level,
+ List<Inter> *l, bool asSegment,
+ SSurface *sorig);
- void ClosestPointTo(Vector p, Point2d *puv, bool converge=true);
- void ClosestPointTo(Vector p, double *u, double *v, bool converge=true);
- bool ClosestPointNewton(Vector p, double *u, double *v, bool converge=true);
+ void ClosestPointTo(Vector p, Point2d *puv, bool mustConverge=true);
+ void ClosestPointTo(Vector p, double *u, double *v, bool mustConverge=true);
+ bool ClosestPointNewton(Vector p, double *u, double *v, bool mustConverge=true) const;
- bool PointIntersectingLine(Vector p0, Vector p1, double *u, double *v);
+ bool PointIntersectingLine(Vector p0, Vector p1, double *u, double *v) const;
Vector ClosestPointOnThisAndSurface(SSurface *srf2, Vector p);
void PointOnSurfaces(SSurface *s1, SSurface *s2, double *u, double *v);
- Vector PointAt(double u, double v);
- Vector PointAt(Point2d puv);
- void TangentsAt(double u, double v, Vector *tu, Vector *tv);
- Vector NormalAt(Point2d puv);
- Vector NormalAt(double u, double v);
- bool LineEntirelyOutsideBbox(Vector a, Vector b, bool segment);
- void GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin);
- bool CoincidentWithPlane(Vector n, double d);
- bool CoincidentWith(SSurface *ss, bool sameNormal);
- bool IsExtrusion(SBezier *of, Vector *along);
+ void PointOnCurve(const SBezier *curve, double *up, double *vp);
+ Vector PointAt(double u, double v) const;
+ Vector PointAt(Point2d puv) const;
+ void TangentsAt(double u, double v, Vector *tu, Vector *tv, bool retry=true) const;
+ Vector NormalAt(Point2d puv) const;
+ Vector NormalAt(double u, double v) const;
+ bool LineEntirelyOutsideBbox(Vector a, Vector b, bool asSegment) const;
+ void GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin) const;
+ bool CoincidentWithPlane(Vector n, double d) const;
+ bool CoincidentWith(SSurface *ss, bool sameNormal) const;
+ bool ContainsPlaneCurve(SCurve *sc) const;
+ bool IsExtrusion(SBezier *of, Vector *along) const;
bool IsCylinder(Vector *axis, Vector *center, double *r,
- Vector *start, Vector *finish);
+ Vector *start, Vector *finish) const;
void TriangulateInto(SShell *shell, SMesh *sm);
// these are intended as bitmasks, even though there's just one now
- enum {
- AS_UV = 0x01,
- AS_XYZ = 0x00
+ enum class MakeAs : uint32_t {
+ UV = 0x01,
+ XYZ = 0x00
};
- void MakeTrimEdgesInto(SEdgeList *sel, int flags, SCurve *sc, STrimBy *stb);
- void MakeEdgesInto(SShell *shell, SEdgeList *sel, int flags,
- SShell *useCurvesFrom=NULL);
+ void MakeTrimEdgesInto(SEdgeList *sel, MakeAs flags, SCurve *sc, STrimBy *stb);
+ void MakeEdgesInto(SShell *shell, SEdgeList *sel, MakeAs flags,
+ SShell *useCurvesFrom=NULL);
Vector ExactSurfaceTangentAt(Vector p, SSurface *srfA, SSurface *srfB,
Vector dir);
void MakeSectionEdgesInto(SShell *shell, SEdgeList *sel, SBezierList *sbl);
void MakeClassifyingBsp(SShell *shell, SShell *useCurvesFrom);
- double ChordToleranceForEdge(Vector a, Vector b);
+ double ChordToleranceForEdge(Vector a, Vector b) const;
void MakeTriangulationGridInto(List<double> *l, double vs, double vf,
- bool swapped);
- Vector PointAtMaybeSwapped(double u, double v, bool swapped);
+ bool swapped, int depth) const;
+ Vector PointAtMaybeSwapped(double u, double v, bool swapped) const;
+ Vector NormalAtMaybeSwapped(double u, double v, bool swapped) const;
- void Reverse(void);
- void Clear(void);
+ void Reverse();
+ void Clear();
};
class SShell {
void MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1,
RgbaColor color);
+ bool CheckNormalAxisRelationship(SBezierLoopSet *sbls, Vector pt, Vector axis, double da, double dx);
void MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
RgbaColor color, Group *group);
-
+ void MakeFromHelicalRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbaColor color,
+ Group *group, double angles, double anglef, double dists, double distf);
+ void MakeFirstOrderRevolvedSurfaces(Vector pt, Vector axis, int i0);
void MakeFromUnionOf(SShell *a, SShell *b);
void MakeFromDifferenceOf(SShell *a, SShell *b);
- enum {
- AS_UNION = 10,
- AS_DIFFERENCE = 11,
- AS_INTERSECT = 12
- };
- void MakeFromBoolean(SShell *a, SShell *b, int type);
+ void MakeFromIntersectionOf(SShell *a, SShell *b);
+ void MakeFromBoolean(SShell *a, SShell *b, SSurface::CombineAs type);
void CopyCurvesSplitAgainst(bool opA, SShell *agnst, SShell *into);
- void CopySurfacesTrimAgainst(SShell *sha, SShell *shb, SShell *into,
- int type);
+ void CopySurfacesTrimAgainst(SShell *sha, SShell *shb, SShell *into, SSurface::CombineAs type);
void MakeIntersectionCurvesAgainst(SShell *against, SShell *into);
void MakeClassifyingBsps(SShell *useCurvesFrom);
void AllPointsIntersecting(Vector a, Vector b, List<SInter> *il,
- bool seg, bool trimmed, bool inclTangent);
+ bool asSegment, bool trimmed, bool inclTangent);
void MakeCoincidentEdgesInto(SSurface *proto, bool sameNormal,
SEdgeList *el, SShell *useCurvesFrom);
void RewriteSurfaceHandlesForCurves(SShell *a, SShell *b);
- void CleanupAfterBoolean(void);
+ void CleanupAfterBoolean();
// Definitions when classifying regions of a surface; it is either inside,
// outside, or coincident (with parallel or antiparallel normal) with a
// shell.
- enum {
+ enum class Class : uint32_t {
INSIDE = 100,
OUTSIDE = 200,
COINC_SAME = 300,
COINC_OPP = 400
};
static const double DOTP_TOL;
- int ClassifyRegion(Vector edge_n, Vector inter_surf_n, Vector edge_surf_n);
- bool ClassifyEdge(int *indir, int *outdir,
+ Class ClassifyRegion(Vector edge_n, Vector inter_surf_n,
+ Vector edge_surf_n) const;
+
+ bool ClassifyEdge(Class *indir, Class *outdir,
Vector ea, Vector eb,
- Vector p,
- Vector edge_n_in, Vector edge_n_out, Vector surf_n);
+ Vector p, Vector edge_n_in,
+ Vector edge_n_out, Vector surf_n);
void MakeFromCopyOf(SShell *a);
void MakeFromTransformationOf(SShell *a,
- Vector trans, Quaternion q, double scale);
+ Vector trans, Quaternion q, double scale);
void MakeFromAssemblyOf(SShell *a, SShell *b);
- void MergeCoincidentSurfaces(void);
+ void MergeCoincidentSurfaces();
void TriangulateInto(SMesh *sm);
void MakeEdgesInto(SEdgeList *sel);
- void MakeSectionEdgesInto(Vector n, double d,
- SEdgeList *sel, SBezierList *sbl);
- bool IsEmpty(void);
+ void MakeSectionEdgesInto(Vector n, double d, SEdgeList *sel, SBezierList *sbl);
+ bool IsEmpty() const;
void RemapFaces(Group *g, int remap);
- void Clear(void);
+ void Clear();
};
#endif
extern int FLAG;
void SSurface::AddExactIntersectionCurve(SBezier *sb, SSurface *srfB,
- SShell *agnstA, SShell *agnstB, SShell *into)
+ SShell *agnstA, SShell *agnstB, SShell *into)
{
SCurve sc = {};
// Important to keep the order of (surfA, surfB) consistent; when we later
SBezier sbrev = *sb;
sbrev.Reverse();
bool backwards = false;
- for(se = into->curve.First(); se; se = into->curve.NextAfter(se)) {
- if(se->isExact) {
- if(sb->Equals(&(se->exact))) {
- existing = se;
- break;
- }
- if(sbrev.Equals(&(se->exact))) {
- existing = se;
- backwards = true;
- break;
+#pragma omp critical(into)
+ {
+ for(se = into->curve.First(); se; se = into->curve.NextAfter(se)) {
+ if(se->isExact) {
+ if(sb->Equals(&(se->exact))) {
+ existing = se;
+ break;
+ }
+ if(sbrev.Equals(&(se->exact))) {
+ existing = se;
+ backwards = true;
+ break;
+ }
}
}
- }
+ }// end omp critical
if(existing) {
SCurvePt *v;
for(v = existing->pts.First(); v; v = existing->pts.NextAfter(v)) {
}
}
#endif // 0
- // Nothing should be generating zero-len edges.
- if((sb->Start()).Equals(sb->Finish())) oops();
+ ssassert(!(sb->Start()).Equals(sb->Finish()),
+ "Unexpected zero-length edge");
- split.source = SCurve::FROM_INTERSECTION;
- into->curve.AddAndAssignId(&split);
+ split.source = SCurve::Source::INTERSECTION;
+#pragma omp critical(into)
+ {
+ into->curve.AddAndAssignId(&split);
+ }
}
void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
p0 = n.ScaledBy(d).Plus(alu.ScaledBy(pm.Dot(alu)));
List<SInter> inters = {};
- sext->AllPointsIntersecting(
- p0, p0.Plus(dp), &inters, false, false, true);
+ sext->AllPointsIntersecting(p0, p0.Plus(dp), &inters,
+ /*asSegment=*/false, /*trimmed=*/false, /*inclTangent=*/true);
SInter *si;
for(si = inters.First(); si; si = inters.NextAfter(si)) {
int i;
for(i = 0; i < lv.n - 1; i++) {
- Vector pa = lv.elem[i], pb = lv.elem[i+1];
+ Vector pa = lv[i], pb = lv[i+1];
pa = pa.Minus(axis.ScaledBy(pa.Dot(axis)));
pb = pb.Minus(axis.ScaledBy(pb.Dot(axis)));
pa = pa.Plus(axisc);
pb = pb.Plus(axisc);
- b->AllPointsIntersecting(pa, pb, &inters, true, false, false);
+ b->AllPointsIntersecting(pa, pb, &inters,
+ /*asSegment=*/true,/*trimmed=*/false, /*inclTangent=*/false);
}
SInter *si;
for(si = inters.First(); si; si = inters.NextAfter(si)) {
Vector p = (si->p).Minus(axis.ScaledBy((si->p).Dot(axis)));
double ub, vb;
- b->ClosestPointTo(p, &ub, &vb, true);
+ b->ClosestPointTo(p, &ub, &vb, /*mustConverge=*/true);
SSurface plane;
plane = SSurface::FromPlane(p, axis.Normal(0), axis.Normal(1));
inters.Clear();
lv.Clear();
} else {
+ if((degm == 1 && degn == 1) || (b->degm == 1 && b->degn == 1)) {
+ // we should only be here if just one surface is a plane because the
+ // plane-plane case was already handled above. Need to check the other
+ // nonplanar surface for trim curves that lie in the plane and are not
+ // already trimming both surfaces. This happens when we cut a Lathe shell
+ // on one of the seams for example.
+ // This also seems necessary to merge some coincident surfaces.
+ SSurface *splane, *sext;
+ SShell *shext;
+ if(degm == 1 && degn == 1) { // this and other checks assume coplanar ctrl pts.
+ splane = this;
+ sext = b;
+ shext = agnstB;
+ } else {
+ splane = b;
+ sext = this;
+ shext = agnstA;
+ }
+ bool foundExact = false;
+ SCurve *sc;
+ for(sc = shext->curve.First(); sc; sc = shext->curve.NextAfter(sc)) {
+ if(sc->source == SCurve::Source::INTERSECTION) continue;
+ if(!sc->isExact) continue;
+ if((sc->surfA != sext->h) && (sc->surfB != sext->h)) continue;
+ // we have a curve belonging to the curved surface and not the plane.
+ // does it lie completely in the plane?
+ if(splane->ContainsPlaneCurve(sc)) {
+ SBezier bezier = sc->exact;
+ AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
+ foundExact = true;
+ }
+ }
+ // if we found at lest one of these we don't want to do the numerical
+ // intersection as well. Sometimes it will also find the same curve but
+ // with different PWLs and the polygon will fail to assemble.
+ if(foundExact)
+ return;
+ }
+
// Try intersecting the surfaces numerically, by a marching algorithm.
// First, we find all the intersections between a surface and the
// boundary of the other surface.
*srfB = (a == 0) ? b : this;
SEdgeList el = {};
- srfA->MakeEdgesInto(shA, &el, AS_XYZ, NULL);
+ srfA->MakeEdgesInto(shA, &el, MakeAs::XYZ, NULL);
SEdge *se;
for(se = el.l.First(); se; se = el.l.NextAfter(se)) {
List<SInter> lsi = {};
srfB->AllPointsIntersecting(se->a, se->b, &lsi,
- true, true, false);
- if(lsi.n == 0) continue;
+ /*asSegment=*/true, /*trimmed=*/true, /*inclTangent=*/false);
+ if(lsi.IsEmpty())
+ continue;
// Find the other surface that this curve trims.
hSCurve hsc = { (uint32_t)se->auxA };
SCurve *sc = shA->curve.FindById(hsc);
- hSSurface hother = (sc->surfA.v == srfA->h.v) ?
+ hSSurface hother = (sc->surfA == srfA->h) ?
sc->surfB : sc->surfA;
SSurface *other = shA->surface.FindById(hother);
Vector p = si->p;
double u, v;
srfB->ClosestPointTo(p, &u, &v);
- srfB->PointOnSurfaces(srfA, other, &u, &v);
+ if(sc->isExact) {
+ srfB->PointOnCurve(&(sc->exact), &u, &v);
+ } else {
+ srfB->PointOnSurfaces(srfA, other, &u, &v);
+ }
p = srfB->PointAt(u, v);
if(!spl.ContainsPoint(p)) {
SPoint sp;
sc.surfA = h;
sc.surfB = b->h;
sc.isExact = false;
- sc.source = SCurve::FROM_INTERSECTION;
+ sc.source = SCurve::Source::INTERSECTION;
- Vector start = spl.l.elem[0].p,
- startv = spl.l.elem[0].auxv;
+ Vector start = spl.l[0].p,
+ startv = spl.l[0].auxv;
spl.l.ClearTags();
- spl.l.elem[0].tag = 1;
+ spl.l[0].tag = 1;
spl.l.RemoveTagged();
// Our chord tolerance is whatever the user specified
// And now we split and insert the curve
SCurve split = sc.MakeCopySplitAgainst(agnstA, agnstB, this, b);
sc.Clear();
- into->curve.AddAndAssignId(&split);
+#pragma omp critical(into)
+ {
+ into->curve.AddAndAssignId(&split);
+ }
}
spl.Clear();
}
// Are two surfaces coincident, with the same (or with opposite) normals?
// Currently handles planes only.
//-----------------------------------------------------------------------------
-bool SSurface::CoincidentWith(SSurface *ss, bool sameNormal) {
+bool SSurface::CoincidentWith(SSurface *ss, bool sameNormal) const {
if(degm != 1 || degn != 1) return false;
if(ss->degm != 1 || ss->degn != 1) return false;
return true;
}
-bool SSurface::CoincidentWithPlane(Vector n, double d) {
+bool SSurface::CoincidentWithPlane(Vector n, double d) const {
if(degm != 1 || degn != 1) return false;
if(fabs(n.Dot(ctrl[0][0]) - d) > LENGTH_EPS) return false;
if(fabs(n.Dot(ctrl[0][1]) - d) > LENGTH_EPS) return false;
return true;
}
+//-----------------------------------------------------------------------------
+// Does a planar surface contain a curve? Does the curve lie completely in plane?
+//-----------------------------------------------------------------------------
+bool SSurface::ContainsPlaneCurve(SCurve *sc) const {
+ if(degm != 1 || degn != 1) return false;
+ if(!sc->isExact) return false; // we don't handle those (yet?)
+
+ Vector p = ctrl[0][0];
+ Vector n = NormalAt(0, 0).WithMagnitude(1);
+ double d = n.Dot(p);
+
+ // check all control points on the curve
+ for(int i=0; i<= sc->exact.deg; i++) {
+ if(fabs(n.Dot(sc->exact.ctrl[i]) - d) > LENGTH_EPS) return false;
+ }
+ return true;
+}
+
//-----------------------------------------------------------------------------
// In our shell, find all surfaces that are coincident with the prototype
// surface (with same or opposite normal, as specified), and copy all of
SSurface *ss;
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
if(proto->CoincidentWith(ss, sameNormal)) {
- ss->MakeEdgesInto(this, el, SSurface::AS_XYZ, useCurvesFrom);
+ ss->MakeEdgesInto(this, el, SSurface::MakeAs::XYZ, useCurvesFrom);
}
}
SContour merged = {};
top->tag = 1;
top->CopyInto(&merged);
- (merged.l.n)--;
+ merged.l.RemoveLast(1);
// List all of the edges, for testing whether bridges work.
SEdgeList el = {};
SEdgeList *avoidEdges, List<Vector> *avoidPts)
{
int i, j;
+ bool withbridge = true;
// Start looking for a bridge on our new hole near its leftmost (min x)
// point.
int sco = 0;
for(i = 0; i < (sc->l.n - 1); i++) {
- if((sc->l.elem[i].p).EqualsExactly(sc->xminPt)) {
+ if((sc->l[i].p).EqualsExactly(sc->xminPt)) {
sco = i;
}
}
// to the leftmost point of the new segment.
int thiso = 0;
double dmin = 1e10;
- for(i = 0; i < l.n; i++) {
- Vector p = l.elem[i].p;
+ for(i = 0; i < l.n-1; i++) {
+ Vector p = l[i].p;
double d = (p.Minus(sc->xminPt)).MagSquared();
if(d < dmin) {
dmin = d;
// First check if the contours share a point; in that case we should
// merge them there, without a bridge.
for(i = 0; i < l.n; i++) {
- thisp = WRAP(i+thiso, l.n);
- a = l.elem[thisp].p;
+ thisp = WRAP(i+thiso, l.n-1);
+ a = l[thisp].p;
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
if(f->Equals(a)) break;
for(j = 0; j < (sc->l.n - 1); j++) {
scp = WRAP(j+sco, (sc->l.n - 1));
- b = sc->l.elem[scp].p;
+ b = sc->l[scp].p;
if(a.Equals(b)) {
+ withbridge = false;
goto haveEdge;
}
}
// If that fails, look for a bridge that does not intersect any edges.
for(i = 0; i < l.n; i++) {
thisp = WRAP(i+thiso, l.n);
- a = l.elem[thisp].p;
+ a = l[thisp].p;
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
if(f->Equals(a)) break;
for(j = 0; j < (sc->l.n - 1); j++) {
scp = WRAP(j+sco, (sc->l.n - 1));
- b = sc->l.elem[scp].p;
+ b = sc->l[scp].p;
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
if(f->Equals(b)) break;
haveEdge:
SContour merged = {};
for(i = 0; i < l.n; i++) {
- merged.AddPoint(l.elem[i].p);
+ if(withbridge || (i != thisp)) {
+ merged.AddPoint(l[i].p);
+ }
if(i == thisp) {
// less than or equal; need to duplicate the join point
for(j = 0; j <= (sc->l.n - 1); j++) {
int jp = WRAP(j + scp, (sc->l.n - 1));
- merged.AddPoint((sc->l.elem[jp]).p);
+ merged.AddPoint((sc->l[jp]).p);
}
// and likewise duplicate join point for the outer curve
- merged.AddPoint(l.elem[i].p);
+ if(withbridge) {
+ merged.AddPoint(l[i].p);
+ }
}
}
// and future bridges mustn't cross our bridge, and it's tricky to get
// things right if two bridges come from the same point
- avoidEdges->AddEdge(a, b);
- avoidPts->Add(&a);
+ if(withbridge) {
+ avoidEdges->AddEdge(a, b);
+ avoidPts->Add(&a);
+ }
avoidPts->Add(&b);
l.Clear();
return true;
}
-bool SContour::IsEar(int bp, double scaledEps) {
+bool SContour::IsEmptyTriangle(int ap, int bp, int cp, double scaledEPS) const {
+
+ STriangle tr = {};
+ tr.a = l[ap].p;
+ tr.b = l[bp].p;
+ tr.c = l[cp].p;
+
+ // Accelerate with an axis-aligned bounding box test
+ Vector maxv = tr.a, minv = tr.a;
+ (tr.b).MakeMaxMin(&maxv, &minv);
+ (tr.c).MakeMaxMin(&maxv, &minv);
+
+ Vector n = Vector::From(0, 0, -1);
+
+ int i;
+ for(i = 0; i < l.n; i++) {
+ if(i == ap || i == bp || i == cp) continue;
+
+ Vector p = l[i].p;
+ if(p.OutsideAndNotOn(maxv, minv)) continue;
+
+ // A point on the edge of the triangle is considered to be inside,
+ // and therefore makes it a non-ear; but a point on the vertex is
+ // "outside", since that's necessary to make bridges work.
+ if(p.EqualsExactly(tr.a)) continue;
+ if(p.EqualsExactly(tr.b)) continue;
+ if(p.EqualsExactly(tr.c)) continue;
+
+ if(tr.ContainsPointProjd(n, p)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Test if ray b->d passes through triangle a,b,c
+static bool RayIsInside(Vector a, Vector c, Vector b, Vector d) {
+ // coincident edges are not considered to intersect the triangle
+ if (d.Equals(a)) return false;
+ if (d.Equals(c)) return false;
+ // if d and c are on opposite sides of ba, we are ok
+ // likewise if d and a are on opposite sides of bc
+ Vector ba = a.Minus(b);
+ Vector bc = c.Minus(b);
+ Vector bd = d.Minus(b);
+
+ // perpendicular to (x,y) is (x,-y) so dot that with the two points. If they
+ // have opposite signs their product will be negative. If bd and bc are on
+ // opposite sides of ba the ray does not intersect. Likewise for bd,ba and bc.
+ if ( (bd.x*(ba.y) + (bd.y * (-ba.x))) * ( bc.x*(ba.y) + (bc.y * (-ba.x))) < LENGTH_EPS)
+ return false;
+ if ( (bd.x*(bc.y) + (bd.y * (-bc.x))) * ( ba.x*(bc.y) + (ba.y * (-bc.x))) < LENGTH_EPS)
+ return false;
+
+ return true;
+}
+
+bool SContour::IsEar(int bp, double scaledEps) const {
int ap = WRAP(bp-1, l.n),
cp = WRAP(bp+1, l.n);
STriangle tr = {};
- tr.a = l.elem[ap].p;
- tr.b = l.elem[bp].p;
- tr.c = l.elem[cp].p;
+ tr.a = l[ap].p;
+ tr.b = l[bp].p;
+ tr.c = l[cp].p;
if((tr.a).Equals(tr.c)) {
// This is two coincident and anti-parallel edges. Zero-area, so
for(i = 0; i < l.n; i++) {
if(i == ap || i == bp || i == cp) continue;
- Vector p = l.elem[i].p;
+ Vector p = l[i].p;
if(p.OutsideAndNotOn(maxv, minv)) continue;
// A point on the edge of the triangle is considered to be inside,
// and therefore makes it a non-ear; but a point on the vertex is
// "outside", since that's necessary to make bridges work.
if(p.EqualsExactly(tr.a)) continue;
- if(p.EqualsExactly(tr.b)) continue;
if(p.EqualsExactly(tr.c)) continue;
+ // points coincident with bp have to be allowed for bridges but edges
+ // from that other point must not cross through our triangle.
+ if(p.EqualsExactly(tr.b)) {
+ int j = WRAP(i-1, l.n);
+ int k = WRAP(i+1, l.n);
+ Vector jp = l[j].p;
+ Vector kp = l[k].p;
+
+ // two consecutive bridges (A,B,C) and later (C,B,A) are not an ear
+ if (jp.Equals(tr.c) && kp.Equals(tr.a)) return false;
+ // check both edges from the point in question
+ if (!RayIsInside(tr.a, tr.c, p,jp) && !RayIsInside(tr.a, tr.c, p,kp))
+ continue;
+ }
if(tr.ContainsPointProjd(n, p)) {
return false;
cp = WRAP(bp+1, l.n);
STriangle tr = {};
- tr.a = l.elem[ap].p;
- tr.b = l.elem[bp].p;
- tr.c = l.elem[cp].p;
+ tr.a = l[ap].p;
+ tr.b = l[bp].p;
+ tr.c = l[cp].p;
if(tr.Normal().MagSquared() < scaledEps*scaledEps) {
// A vertex with more than two edges will cause us to generate
// zero-area triangles, which must be culled.
// By deleting the point at bp, we may change the ear-ness of the points
// on either side.
- l.elem[ap].ear = SPoint::UNKNOWN;
- l.elem[cp].ear = SPoint::UNKNOWN;
+ l[ap].ear = EarType::UNKNOWN;
+ l[cp].ear = EarType::UNKNOWN;
l.ClearTags();
- l.elem[bp].tag = 1;
+ l[bp].tag = 1;
l.RemoveTagged();
}
int i;
// Clean the original contour by removing any zero-length edges.
+ // initialize eartypes to unknown while we're going over them.
l.ClearTags();
+ l[0].ear = EarType::UNKNOWN;
for(i = 1; i < l.n; i++) {
- if((l.elem[i].p).Equals(l.elem[i-1].p)) {
- l.elem[i].tag = 1;
+ l[i].ear = EarType::UNKNOWN;
+ if((l[i].p).Equals(l[i-1].p)) {
+ l[i].tag = 1;
}
}
- l.RemoveTagged();
-
- // Now calculate the ear-ness of each vertex
- for(i = 0; i < l.n; i++) {
- (l.elem[i]).ear = IsEar(i, scaledEps) ? SPoint::EAR : SPoint::NOT_EAR;
+ if( (l[0].p).Equals(l[l.n-1].p) ) {
+ l[l.n-1].tag = 1;
}
+ l.RemoveTagged();
- bool toggle = false;
- while(l.n > 3) {
- // Some points may have changed ear-ness, so recalculate
- for(i = 0; i < l.n; i++) {
- if(l.elem[i].ear == SPoint::UNKNOWN) {
- (l.elem[i]).ear = IsEar(i, scaledEps) ?
- SPoint::EAR : SPoint::NOT_EAR;
+ // Handle simple triangle fans all at once. This pass is optional.
+ if(srf->degm == 1 && srf->degn == 1) {
+ l.ClearTags();
+ int j=0;
+ int pstart = 0;
+ double elen = -1.0;
+ double oldspan = 0.0;
+ for(i = 1; i < l.n; i++) {
+ Vector ab = l[i].p.Minus(l[i-1].p);
+ // first time just measure the segment
+ if (elen < 0.0) {
+ elen = ab.Dot(ab);
+ oldspan = elen;
+ j = 1;
+ continue;
+ }
+ // check for consecutive segments of similar size which are also
+ // ears and where the group forms a convex ear
+ bool end = false;
+ double ratio = ab.Dot(ab) / elen;
+ if ((ratio < 0.25) || (ratio > 4.0)) end = true;
+
+ double slen = l[pstart].p.Minus(l[i].p).MagSquared();
+ if (slen < oldspan) end = true;
+
+ if (!IsEar(i-1, scaledEps) ) end = true;
+// if ((j>0) && !IsEar(pstart, i-1, i, scaledEps)) end = true;
+ if ((j>0) && !IsEmptyTriangle(pstart, i-1, i, scaledEps)) end = true;
+ // the new segment is valid so add to the fan
+ if (!end) {
+ j++;
+ oldspan = slen;
+ }
+ // we need to stop at the end of polygon but may still
+ if (i == l.n-1) {
+ end = true;
+ }
+ if (end) { // triangulate the fan and tag the verticies
+ if (j > 3) {
+ Vector center = l[pstart+1].p.Plus(l[pstart+j-1].p).ScaledBy(0.5);
+ for (int x=0; x<j; x++) {
+ STriangle tr = {};
+ tr.a = center;
+ tr.b = l[pstart+x].p;
+ tr.c = l[pstart+x+1].p;
+ m->AddTriangle(&tr);
+ }
+ for (int x=1; x<j; x++) {
+ l[pstart+x].tag = 1;
+ }
+ STriangle tr = {};
+ tr.a = center;
+ tr.b = l[pstart+j].p;
+ tr.c = l[pstart].p;
+ m->AddTriangle(&tr);
+ }
+ pstart = i-1;
+ elen = ab.Dot(ab);
+ oldspan = elen;
+ j = 1;
}
}
+ l.RemoveTagged();
+ } // end optional fan creation pass
+ bool toggle = false;
+ while(l.n > 3) {
int bestEar = -1;
double bestChordTol = VERY_POSITIVE;
// Alternate the starting position so we generate strip-like
int offset = toggle ? -1 : 0;
for(i = 0; i < l.n; i++) {
int ear = WRAP(i+offset, l.n);
- if(l.elem[ear].ear == SPoint::EAR) {
+ if(l[ear].ear == EarType::UNKNOWN) {
+ (l[ear]).ear = IsEar(ear, scaledEps) ? EarType::EAR : EarType::NOT_EAR;
+ }
+ if(l[ear].ear == EarType::EAR) {
if(srf->degm == 1 && srf->degn == 1) {
// This is a plane; any ear is a good ear.
bestEar = ear;
// If we are triangulating a curved surface, then try to
// clip ears that have a small chord tolerance from the
// surface.
- Vector prev = l.elem[WRAP((i+offset-1), l.n)].p,
- next = l.elem[WRAP((i+offset+1), l.n)].p;
+ Vector prev = l[WRAP((i+offset-1), l.n)].p,
+ next = l[WRAP((i+offset+1), l.n)].p;
double tol = srf->ChordToleranceForEdge(prev, next);
if(tol < bestChordTol - scaledEps) {
bestEar = ear;
ClipEarInto(m, 0, scaledEps); // add the last triangle
}
-double SSurface::ChordToleranceForEdge(Vector a, Vector b) {
+double SSurface::ChordToleranceForEdge(Vector a, Vector b) const {
Vector as = PointAt(a.x, a.y), bs = PointAt(b.x, b.y);
double worst = VERY_NEGATIVE;
return sqrt(worst);
}
-Vector SSurface::PointAtMaybeSwapped(double u, double v, bool swapped) {
+Vector SSurface::PointAtMaybeSwapped(double u, double v, bool swapped) const {
if(swapped) {
return PointAt(v, u);
} else {
}
}
+Vector SSurface::NormalAtMaybeSwapped(double u, double v, bool swapped) const {
+ Vector du, dv;
+ if(swapped) {
+ TangentsAt(v, u, &dv, &du);
+ } else {
+ TangentsAt(u, v, &du, &dv);
+ }
+ return du.Cross(dv).WithMagnitude(1.0);
+}
+
void SSurface::MakeTriangulationGridInto(List<double> *l, double vs, double vf,
- bool swapped)
+ bool swapped, int depth) const
{
double worst = 0;
// Try piecewise linearizing four curves, at u = 0, 1/3, 2/3, 1; choose
// the worst chord tolerance of any of those.
+ double worst_twist = 1.0;
int i;
for(i = 0; i <= 3; i++) {
double u = i/3.0;
Vector pm1 = PointAtMaybeSwapped(u, vm1, swapped),
pm2 = PointAtMaybeSwapped(u, vm2, swapped);
+ // 0.999 is about 2.5 degrees of twist over the middle 1/3 V-span.
+ // we don't check at the ends because the derivative may not be valid there.
+ double twist = 1.0;
+ if (degm == 1) twist = NormalAtMaybeSwapped(u, vm1, swapped).Dot(
+ NormalAtMaybeSwapped(u, vm2, swapped) );
+ if (twist < worst_twist) worst_twist = twist;
+
worst = max(worst, pm1.DistanceToLine(ps, pf.Minus(ps)));
worst = max(worst, pm2.DistanceToLine(ps, pf.Minus(ps)));
}
double step = 1.0/SS.GetMaxSegments();
- if((vf - vs) < step || worst < SS.ChordTolMm()) {
+ if( ((vf - vs) < step || worst < SS.ChordTolMm())
+ && ((worst_twist > 0.999) || (depth > 3)) ) {
l->Add(&vf);
} else {
- MakeTriangulationGridInto(l, vs, (vs+vf)/2, swapped);
- MakeTriangulationGridInto(l, (vs+vf)/2, vf, swapped);
+ MakeTriangulationGridInto(l, vs, (vs+vf)/2, swapped, depth+1);
+ MakeTriangulationGridInto(l, (vs+vf)/2, vf, swapped, depth+1);
}
}
List<double> li, lj;
li = {};
lj = {};
- double v = 0;
- li.Add(&v);
- srf->MakeTriangulationGridInto(&li, 0, 1, true);
- lj.Add(&v);
- srf->MakeTriangulationGridInto(&lj, 0, 1, false);
-
- // Now iterate over each quad in the grid. If it's outside the polygon,
- // or if it intersects the polygon, then we discard it. Otherwise we
- // generate two triangles in the mesh, and cut it out of our polygon.
- int i, j;
- for(i = 0; i < (li.n - 1); i++) {
- for(j = 0; j < (lj.n - 1); j++) {
- double us = li.elem[i], uf = li.elem[i+1],
- vs = lj.elem[j], vf = lj.elem[j+1];
-
- Vector a = Vector::From(us, vs, 0),
- b = Vector::From(us, vf, 0),
- c = Vector::From(uf, vf, 0),
- d = Vector::From(uf, vs, 0);
-
- if(orig.AnyEdgeCrossings(a, b, NULL) ||
- orig.AnyEdgeCrossings(b, c, NULL) ||
- orig.AnyEdgeCrossings(c, d, NULL) ||
- orig.AnyEdgeCrossings(d, a, NULL))
- {
- continue;
- }
+ double v[5] = {0.0, 0.25, 0.5, 0.75, 1.0};
+ li.Add(&v[0]);
+ srf->MakeTriangulationGridInto(&li, 0, 1, /*swapped=*/true, 0);
+ lj.Add(&v[0]);
+ srf->MakeTriangulationGridInto(&lj, 0, 1, /*swapped=*/false, 0);
+
+ // force 2nd order grid to have at least 4 segments in each direction
+ if ((li.n < 5) && (srf->degm>1)) { // 4 segments minimun
+ li.Clear();
+ li.Add(&v[0]);li.Add(&v[1]);li.Add(&v[2]);li.Add(&v[3]);li.Add(&v[4]);
+ }
+ if ((lj.n < 5) && (srf->degn>1)) { // 4 segments minimun
+ lj.Clear();
+ lj.Add(&v[0]);lj.Add(&v[1]);lj.Add(&v[2]);lj.Add(&v[3]);lj.Add(&v[4]);
+ }
- // There's no intersections, so it doesn't matter which point
- // we decide to test.
- if(!this->ContainsPoint(a)) {
- continue;
- }
+ if ((li.n > 3) && (lj.n > 3)) {
+ // Now iterate over each quad in the grid. If it's outside the polygon,
+ // or if it intersects the polygon, then we discard it. Otherwise we
+ // generate two triangles in the mesh, and cut it out of our polygon.
+ // Quads around the perimeter would be rejected by AnyEdgeCrossings.
+ std::vector<bool> bottom(lj.n, false); // did we use this quad?
+ Vector tu = {0,0,0}, tv = {0,0,0};
+ int i, j;
+ for(i = 1; i < (li.n-1); i++) {
+ bool prev_flag = false;
+ for(j = 1; j < (lj.n-1); j++) {
+ bool this_flag = true;
+ double us = li[i], uf = li[i+1],
+ vs = lj[j], vf = lj[j+1];
+
+ Vector a = Vector::From(us, vs, 0),
+ b = Vector::From(us, vf, 0),
+ c = Vector::From(uf, vf, 0),
+ d = Vector::From(uf, vs, 0);
+
+ // | d-----c
+ // | | |
+ // | | |
+ // | a-----b
+ // |
+ // +-------------> j/v axis
+
+ if( (i==(li.n-2)) || (j==(lj.n-2)) ||
+ orig.AnyEdgeCrossings(a, b, NULL) ||
+ orig.AnyEdgeCrossings(b, c, NULL) ||
+ orig.AnyEdgeCrossings(c, d, NULL) ||
+ orig.AnyEdgeCrossings(d, a, NULL))
+ {
+ this_flag = false;
+ }
- // Add the quad to our mesh
- STriangle tr = {};
- tr.a = a;
- tr.b = b;
- tr.c = c;
- mesh->AddTriangle(&tr);
- tr.a = a;
- tr.b = c;
- tr.c = d;
- mesh->AddTriangle(&tr);
-
- holes.AddEdge(a, b);
- holes.AddEdge(b, c);
- holes.AddEdge(c, d);
- holes.AddEdge(d, a);
+ // There's no intersections, so it doesn't matter which point
+ // we decide to test.
+ if(!this->ContainsPoint(a)) {
+ this_flag = false;
+ }
+
+ if (this_flag) {
+ // Add the quad to our mesh
+ srf->TangentsAt(us,vs, &tu,&tv);
+ if(tu.Dot(tv) < LENGTH_EPS) {
+ /* Split "the other way" if angle>90
+ compare to LENGTH_EPS instead of zero to avoid alternating triangle
+ "orientations" when the tangents are orthogonal (revolve, lathe etc.)
+ this results in a higher quality mesh. */
+ STriangle tr = {};
+ tr.a = a;
+ tr.b = b;
+ tr.c = c;
+ mesh->AddTriangle(&tr);
+ tr.a = a;
+ tr.b = c;
+ tr.c = d;
+ mesh->AddTriangle(&tr);
+ } else{
+ STriangle tr = {};
+ tr.a = a;
+ tr.b = b;
+ tr.c = d;
+ mesh->AddTriangle(&tr);
+ tr.a = b;
+ tr.b = c;
+ tr.c = d;
+ mesh->AddTriangle(&tr);
+ }
+ if (!prev_flag) // add our own left edge
+ holes.AddEdge(d, a);
+ if (!bottom[j]) // add our own bottom edge
+ holes.AddEdge(a, b);
+ } else {
+ if (prev_flag) // add our left neighbots right edge
+ holes.AddEdge(a, d);
+ if (bottom[j]) // add our bottom neighbors top edge
+ holes.AddEdge(b, a);
+ }
+ prev_flag = this_flag;
+ bottom[j] = this_flag;
+ }
}
- }
- holes.CullExtraneousEdges();
- SPolygon hp = {};
- holes.AssemblePolygon(&hp, NULL, true);
+ // Because no duplicate edges were created we do not need to cull them.
+ SPolygon hp = {};
+ holes.AssemblePolygon(&hp, NULL, /*keepDir=*/true);
- SContour *sc;
- for(sc = hp.l.First(); sc; sc = hp.l.NextAfter(sc)) {
- l.Add(sc);
+ SContour *sc;
+ for(sc = hp.l.First(); sc; sc = hp.l.NextAfter(sc)) {
+ l.Add(sc);
+ }
+ hp.l.Clear();
}
-
orig.Clear();
holes.Clear();
li.Clear();
lj.Clear();
- hp.l.Clear();
-
UvTriangulateInto(mesh, srf);
}
+void SPolygon::TriangulateInto(SMesh *m) const {
+ Vector n = normal;
+ if(n.Equals(Vector::From(0.0, 0.0, 0.0))) {
+ n = ComputeNormal();
+ }
+ Vector u = n.Normal(0);
+ Vector v = n.Normal(1);
+
+ SPolygon p = {};
+ this->InverseTransformInto(&p, u, v, n);
+
+ SSurface srf = SSurface::FromPlane(Vector::From(0.0, 0.0, 0.0),
+ Vector::From(1.0, 0.0, 0.0),
+ Vector::From(0.0, 1.0, 0.0));
+ SMesh pm = {};
+ p.UvTriangulateInto(&pm, &srf);
+ for(STriangle st : pm.l) {
+ st = st.Transform(u, v, n);
+ m->AddTriangle(&st);
+ }
+ p.Clear();
+ pm.Clear();
+}
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
#include "solvespace.h"
-#include <png.h>
-
-#define DEFAULT_TEXT_HEIGHT 11.5
const Style::Default Style::Defaults[] = {
{ { ACTIVE_GRP }, "ActiveGrp", RGBf(1.0, 1.0, 1.0), 1.5, 4 },
{ { HOVERED }, "Hovered", RGBf(1.0, 1.0, 0.0), 1.5, 0 },
{ { CONTOUR_FILL }, "ContourFill", RGBf(0.0, 0.1, 0.1), 1.0, 0 },
{ { NORMALS }, "Normals", RGBf(0.0, 0.4, 0.4), 1.0, 0 },
- { { ANALYZE }, "Analyze", RGBf(0.0, 1.0, 1.0), 1.0, 0 },
+ { { ANALYZE }, "Analyze", RGBf(0.0, 1.0, 1.0), 3.0, 0 },
{ { DRAW_ERROR }, "DrawError", RGBf(1.0, 0.0, 0.0), 8.0, 0 },
{ { DIM_SOLID }, "DimSolid", RGBf(0.1, 0.1, 0.1), 1.0, 0 },
- { { HIDDEN_EDGE }, "HiddenEdge", RGBf(0.8, 0.8, 0.8), 2.0, 1 },
+ { { HIDDEN_EDGE }, "HiddenEdge", RGBf(0.8, 0.8, 0.8), 1.0, 1 },
{ { OUTLINE }, "Outline", RGBf(0.8, 0.8, 0.8), 3.0, 5 },
{ { 0 }, NULL, RGBf(0.0, 0.0, 0.0), 0.0, 0 }
};
return name;
}
-void Style::CreateAllDefaultStyles(void) {
+void Style::CreateAllDefaultStyles() {
const Default *d;
for(d = &(Defaults[0]); d->h.v; d++) {
(void)Get(d->h);
bool isDefaultStyle = true;
const Default *d;
for(d = &(Defaults[0]); d->h.v; d++) {
- if(d->h.v == h.v) break;
+ if(d->h == h) break;
}
if(!d->h.v) {
// Not a default style; so just create it the same as our default
}
void Style::FillDefaultStyle(Style *s, const Default *d, bool factory) {
+ Platform::SettingsRef settings = Platform::GetSettings();
+
if(d == NULL) d = &Defaults[0];
- s->color = (factory) ? d->color : CnfThawColor(d->color, CnfColor(d->cnfPrefix));
- s->width = (factory) ? d->width : CnfThawFloat((float)(d->width), CnfWidth(d->cnfPrefix));
- s->widthAs = UNITS_AS_PIXELS;
- s->textHeight = (factory) ? DEFAULT_TEXT_HEIGHT
- : CnfThawFloat(DEFAULT_TEXT_HEIGHT, CnfTextHeight(d->cnfPrefix));
- s->textHeightAs = UNITS_AS_PIXELS;
- s->textOrigin = 0;
+ s->color = (factory)
+ ? d->color
+ : settings->ThawColor(CnfColor(d->cnfPrefix), d->color);
+ s->width = (factory)
+ ? d->width
+ : settings->ThawFloat(CnfWidth(d->cnfPrefix), (float)(d->width));
+ s->widthAs = UnitsAs::PIXELS;
+ s->textHeight = (factory) ? 11.5
+ : settings->ThawFloat(CnfTextHeight(d->cnfPrefix), 11.5);
+ s->textHeightAs = UnitsAs::PIXELS;
+ s->textOrigin = TextOrigin::NONE;
s->textAngle = 0;
s->visible = true;
s->exportable = true;
s->filled = false;
s->fillColor = RGBf(0.3, 0.3, 0.3);
- s->stippleType = (d->h.v == Style::HIDDEN_EDGE) ? Style::STIPPLE_DASH
- : Style::STIPPLE_CONTINUOUS;
+ s->stippleType = (d->h.v == Style::HIDDEN_EDGE) ? StipplePattern::DASH
+ : StipplePattern::CONTINUOUS;
s->stippleScale = 15.0;
s->zIndex = d->zIndex;
}
-void Style::LoadFactoryDefaults(void) {
+void Style::LoadFactoryDefaults() {
const Default *d;
for(d = &(Defaults[0]); d->h.v; d++) {
Style *s = Get(d->h);
FillDefaultStyle(s, d, /*factory=*/true);
}
SS.backgroundColor = RGBi(0, 0, 0);
- if(SS.bgImage.fromFile) MemFree(SS.bgImage.fromFile);
- SS.bgImage.fromFile = NULL;
}
-void Style::FreezeDefaultStyles(void) {
+void Style::FreezeDefaultStyles(Platform::SettingsRef settings) {
const Default *d;
for(d = &(Defaults[0]); d->h.v; d++) {
- CnfFreezeColor(Color(d->h), CnfColor(d->cnfPrefix));
- CnfFreezeFloat((float)Width(d->h), CnfWidth(d->cnfPrefix));
- CnfFreezeFloat((float)TextHeight(d->h), CnfTextHeight(d->cnfPrefix));
+ settings->FreezeColor(CnfColor(d->cnfPrefix), Color(d->h));
+ settings->FreezeFloat(CnfWidth(d->cnfPrefix), (float)Width(d->h));
+ settings->FreezeFloat(CnfTextHeight(d->cnfPrefix), (float)TextHeight(d->h));
}
}
if(!c->IsStylable()) continue;
c->disp.style.v = v;
+ SS.MarkGroupDirty(c->group);
}
if(showError) {
- Error("Can't assign style to an entity that's derived from another "
- "entity; try assigning a style to this entity's parent.");
+ Error(_("Can't assign style to an entity that's derived from another "
+ "entity; try assigning a style to this entity's parent."));
}
SS.GW.ClearSelection();
- InvalidateGraphics();
- SS.ScheduleGenerateAll();
+ SS.GW.Invalidate();
// And show that style's info screen in the text window.
- SS.TW.GoToScreen(TextWindow::SCREEN_STYLE_INFO);
+ SS.TW.GoToScreen(TextWindow::Screen::STYLE_INFO);
SS.TW.shown.style.v = v;
SS.ScheduleShowTW();
}
hStyle hs = { (uint32_t)s };
return Color(hs, forExport);
}
-float Style::Width(int s) {
+double Style::Width(int s) {
hStyle hs = { (uint32_t)s };
return Width(hs);
}
//-----------------------------------------------------------------------------
// Return the width associated with our style in pixels..
//-----------------------------------------------------------------------------
-float Style::Width(hStyle h) {
- double r = 1.0;
+double Style::Width(hStyle h) {
Style *s = Get(h);
- if(s->widthAs == UNITS_AS_MM) {
- r = s->width * SS.GW.scale;
- } else if(s->widthAs == UNITS_AS_PIXELS) {
- r = s->width;
+ switch(s->widthAs) {
+ case UnitsAs::MM: return s->width * SS.GW.scale;
+ case UnitsAs::PIXELS: return s->width;
}
- // This returns a float because ssglLineWidth expects a float, avoid casts.
- return (float)r;
+ ssassert(false, "Unexpected units");
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Return the associated text height, in pixels.
//-----------------------------------------------------------------------------
-double Style::TextHeight(hStyle hs) {
- Style *s = Get(hs);
- if(s->textHeightAs == UNITS_AS_MM) {
- return s->textHeight * SS.GW.scale;
- } else /* s->textHeightAs == UNITS_AS_PIXELS */ {
- return s->textHeight;
+double Style::TextHeight(hStyle h) {
+ Style *s = Get(h);
+ switch(s->textHeightAs) {
+ case UnitsAs::MM: return s->textHeight * SS.GW.scale;
+ case UnitsAs::PIXELS: return s->textHeight;
}
+ ssassert(false, "Unexpected units");
}
double Style::DefaultTextHeight() {
return TextHeight(hs);
}
+//-----------------------------------------------------------------------------
+// Return the parameters of this style, as a canvas stroke.
+//-----------------------------------------------------------------------------
+Canvas::Stroke Style::Stroke(hStyle hs) {
+ Canvas::Stroke stroke = {};
+ Style *style = Style::Get(hs);
+ stroke.color = style->color;
+ stroke.stipplePattern = style->stippleType;
+ stroke.stippleScale = style->stippleScale;
+ stroke.width = style->width;
+ switch(style->widthAs) {
+ case Style::UnitsAs::PIXELS:
+ stroke.unit = Canvas::Unit::PX;
+ break;
+ case Style::UnitsAs::MM:
+ stroke.unit = Canvas::Unit::MM;
+ break;
+ }
+ return stroke;
+}
+
+Canvas::Stroke Style::Stroke(int hsv) {
+ hStyle hs = { (uint32_t) hsv };
+ return Style::Stroke(hs);
+}
+
//-----------------------------------------------------------------------------
// Should lines and curves from this style appear in the output file? Only
// if it's both shown and exportable.
// Otherwise, we use the default rules.
hStyle hs;
- if(e->group.v != SS.GW.activeGroup.v) {
+ if(e->group != SS.GW.activeGroup) {
hs.v = INACTIVE_GRP;
} else if(e->construction) {
hs.v = CONSTRUCTION;
return hs;
}
-int Style::PatternType(hStyle hs) {
+StipplePattern Style::PatternType(hStyle hs) {
Style *s = Get(hs);
return s->stippleType;
}
double Style::StippleScaleMm(hStyle hs) {
Style *s = Get(hs);
- if(s->widthAs == UNITS_AS_MM) {
+ if(s->widthAs == UnitsAs::MM) {
return s->stippleScale;
- } else if(s->widthAs == UNITS_AS_PIXELS) {
+ } else if(s->widthAs == UnitsAs::PIXELS) {
return s->stippleScale / SS.GW.scale;
}
return 1.0;
}
-std::string Style::DescriptionString(void) {
+std::string Style::DescriptionString() const {
if(name.empty()) {
return ssprintf("s%03x-(unnamed)", h.v);
} else {
void TextWindow::ScreenShowListOfStyles(int link, uint32_t v) {
- SS.TW.GoToScreen(SCREEN_LIST_OF_STYLES);
+ SS.TW.GoToScreen(Screen::LIST_OF_STYLES);
}
void TextWindow::ScreenShowStyleInfo(int link, uint32_t v) {
- SS.TW.GoToScreen(SCREEN_STYLE_INFO);
+ GraphicsWindow::MenuEdit(Command::UNSELECT_ALL);
+ SS.TW.GoToScreen(Screen::STYLE_INFO);
SS.TW.shown.style.v = v;
}
void TextWindow::ScreenLoadFactoryDefaultStyles(int link, uint32_t v) {
Style::LoadFactoryDefaults();
- SS.TW.GoToScreen(SCREEN_LIST_OF_STYLES);
+ SS.TW.GoToScreen(Screen::LIST_OF_STYLES);
}
void TextWindow::ScreenCreateCustomStyle(int link, uint32_t v) {
void TextWindow::ScreenChangeBackgroundColor(int link, uint32_t v) {
RgbaColor rgb = SS.backgroundColor;
SS.TW.ShowEditControlWithColorPicker(3, rgb);
- SS.TW.edit.meaning = EDIT_BACKGROUND_COLOR;
+ SS.TW.edit.meaning = Edit::BACKGROUND_COLOR;
}
-static int RoundUpToPowerOfTwo(int v)
-{
- int i;
- for(i = 0; i < 31; i++) {
- int vt = (1 << i);
- if(vt >= v) {
- return vt;
- }
- }
- return 0;
-}
-
-void TextWindow::ScreenBackgroundImage(int link, uint32_t v) {
- if(SS.bgImage.fromFile) MemFree(SS.bgImage.fromFile);
- SS.bgImage.fromFile = NULL;
-
- if(link == 'l') {
- FILE *f = NULL;
- png_struct *png_ptr = NULL;
- png_info *info_ptr = NULL;
-
- std::string importFile;
- if(!GetOpenFile(&importFile, "", PngFileFilter)) goto err;
- f = ssfopen(importFile, "rb");
- if(!f) goto err;
-
- uint8_t header[8];
- if (fread(header, 1, 8, f) != 8)
- goto err;
- if(png_sig_cmp(header, 0, 8)) goto err;
-
- png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
- NULL, NULL, NULL);
- if(!png_ptr) goto err;
-
- info_ptr = png_create_info_struct(png_ptr);
- if(!info_ptr) goto err;
-
- if(setjmp(png_jmpbuf(png_ptr))) goto err;
-
- png_init_io(png_ptr, f);
- png_set_sig_bytes(png_ptr, 8);
-
- png_read_png(png_ptr, info_ptr,
- PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_ALPHA, NULL);
-
- int w; w = (int)png_get_image_width(png_ptr, info_ptr);
- int h; h = (int)png_get_image_height(png_ptr, info_ptr);
- uint8_t **rows; rows = png_get_rows(png_ptr, info_ptr);
-
- // Round to next-highest powers of two, since the textures require
- // that. And round up to 4, to guarantee 32-bit alignment.
- int rw; rw = max(4, RoundUpToPowerOfTwo(w));
- int rh; rh = max(4, RoundUpToPowerOfTwo(h));
-
- SS.bgImage.fromFile = (uint8_t *)MemAlloc(rw*rh*3);
- {for(int i = 0; i < h; i++) {
- memcpy(SS.bgImage.fromFile + ((h - 1) - i)*(rw*3), rows[i], w*3);
- }}
- SS.bgImage.w = w;
- SS.bgImage.h = h;
- SS.bgImage.rw = rw;
- SS.bgImage.rh = rh;
- SS.bgImage.scale = SS.GW.scale;
- SS.bgImage.origin = SS.GW.offset.ScaledBy(-1);
-
-err:
- if(png_ptr) png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
- if(f) fclose(f);
- }
- SS.ScheduleShowTW();
-}
-
-void TextWindow::ScreenChangeBackgroundImageScale(int link, uint32_t v) {
- SS.TW.edit.meaning = EDIT_BACKGROUND_IMG_SCALE;
- SS.TW.ShowEditControl(10, ssprintf("%.3f", SS.bgImage.scale * SS.MmPerUnit()));
-}
-
-void TextWindow::ShowListOfStyles(void) {
+void TextWindow::ShowListOfStyles() {
Printf(true, "%Ft color style-name");
bool darkbg = false;
rgb.redF(), rgb.greenF(), rgb.blueF(),
top[rows-1] + 2, &ScreenChangeBackgroundColor);
- Printf(false, "");
- Printf(false, "%Ft background bitmap image%E");
- if(SS.bgImage.fromFile) {
- Printf(false, "%Ba %Ftwidth:%E %dpx %Ftheight:%E %dpx",
- SS.bgImage.w, SS.bgImage.h);
-
- Printf(false, " %Ftscale:%E %# px/%s %Fl%Ll%f%D[change]%E",
- SS.bgImage.scale*SS.MmPerUnit(),
- SS.UnitName(),
- &ScreenChangeBackgroundImageScale, top[rows-1] + 2);
-
- Printf(false, "%Ba %Fl%Lc%fclear background image%E",
- &ScreenBackgroundImage);
- } else {
- Printf(false, "%Ba none - %Fl%Ll%fload background image%E",
- &ScreenBackgroundImage);
- Printf(false, " (bottom left will be center of view)");
- }
-
Printf(false, "");
Printf(false, " %Fl%Ll%fload factory defaults%E",
&ScreenLoadFactoryDefaultStyles);
Style *s = Style::Get(hs);
SS.TW.ShowEditControl(12, s->name);
SS.TW.edit.style = hs;
- SS.TW.edit.meaning = EDIT_STYLE_NAME;
+ SS.TW.edit.meaning = Edit::STYLE_NAME;
}
void TextWindow::ScreenDeleteStyle(int link, uint32_t v) {
// And it will get recreated automatically if something is still using
// the style, so no need to do anything else.
}
- SS.TW.GoToScreen(SCREEN_LIST_OF_STYLES);
- InvalidateGraphics();
+ SS.TW.GoToScreen(Screen::LIST_OF_STYLES);
+ SS.GW.Invalidate();
}
void TextWindow::ScreenChangeStylePatternType(int link, uint32_t v) {
hStyle hs = { v };
Style *s = Style::Get(hs);
- s->stippleType = link - 1;
+ s->stippleType = (StipplePattern)(link - 1);
+ SS.GW.persistentDirty = true;
}
void TextWindow::ScreenChangeStyleMetric(int link, uint32_t v) {
hStyle hs = { v };
Style *s = Style::Get(hs);
double val;
- int units, meaning, col;
+ Style::UnitsAs units;
+ Edit meaning;
+ int col;
switch(link) {
case 't':
val = s->textHeight;
units = s->textHeightAs;
col = 10;
- meaning = EDIT_STYLE_TEXT_HEIGHT;
+ meaning = Edit::STYLE_TEXT_HEIGHT;
break;
case 's':
val = s->stippleScale;
units = s->widthAs;
col = 17;
- meaning = EDIT_STYLE_STIPPLE_PERIOD;
+ meaning = Edit::STYLE_STIPPLE_PERIOD;
break;
case 'w':
val = s->width;
units = s->widthAs;
col = 9;
- meaning = EDIT_STYLE_WIDTH;
+ meaning = Edit::STYLE_WIDTH;
break;
- default: oops();
+ default: ssassert(false, "Unexpected link");
}
std::string edit_value;
- if(units == Style::UNITS_AS_PIXELS) {
+ if(units == Style::UnitsAs::PIXELS) {
edit_value = ssprintf("%.2f", val);
} else {
edit_value = SS.MmToString(val);
Style *s = Style::Get(hs);
SS.TW.ShowEditControl(9, ssprintf("%.2f", s->textAngle));
SS.TW.edit.style = hs;
- SS.TW.edit.meaning = EDIT_STYLE_TEXT_ANGLE;
+ SS.TW.edit.meaning = Edit::STYLE_TEXT_ANGLE;
}
void TextWindow::ScreenChangeStyleColor(int link, uint32_t v) {
hStyle hs = { v };
Style *s = Style::Get(hs);
// Same function used for stroke and fill colors
- int em;
+ Edit em;
RgbaColor rgb;
if(link == 's') {
- em = EDIT_STYLE_COLOR;
+ em = Edit::STYLE_COLOR;
rgb = s->color;
} else if(link == 'f') {
- em = EDIT_STYLE_FILL_COLOR;
+ em = Edit::STYLE_FILL_COLOR;
rgb = s->fillColor;
- } else {
- oops();
- }
+ } else ssassert(false, "Unexpected link");
SS.TW.ShowEditControlWithColorPicker(13, rgb);
SS.TW.edit.style = hs;
SS.TW.edit.meaning = em;
switch(link) {
// Units for the width
case 'w':
- if(s->widthAs != Style::UNITS_AS_MM) {
- s->widthAs = Style::UNITS_AS_MM;
+ if(s->widthAs != Style::UnitsAs::MM) {
+ s->widthAs = Style::UnitsAs::MM;
s->width /= SS.GW.scale;
s->stippleScale /= SS.GW.scale;
}
break;
case 'W':
- if(s->widthAs != Style::UNITS_AS_PIXELS) {
- s->widthAs = Style::UNITS_AS_PIXELS;
+ if(s->widthAs != Style::UnitsAs::PIXELS) {
+ s->widthAs = Style::UnitsAs::PIXELS;
s->width *= SS.GW.scale;
s->stippleScale *= SS.GW.scale;
}
// Units for the height
case 'g':
- if(s->textHeightAs != Style::UNITS_AS_MM) {
- s->textHeightAs = Style::UNITS_AS_MM;
+ if(s->textHeightAs != Style::UnitsAs::MM) {
+ s->textHeightAs = Style::UnitsAs::MM;
s->textHeight /= SS.GW.scale;
}
break;
case 'G':
- if(s->textHeightAs != Style::UNITS_AS_PIXELS) {
- s->textHeightAs = Style::UNITS_AS_PIXELS;
+ if(s->textHeightAs != Style::UnitsAs::PIXELS) {
+ s->textHeightAs = Style::UnitsAs::PIXELS;
s->textHeight *= SS.GW.scale;
}
break;
// Horizontal text alignment
case 'L':
- s->textOrigin |= Style::ORIGIN_LEFT;
- s->textOrigin &= ~Style::ORIGIN_RIGHT;
+ s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin | (uint32_t)Style::TextOrigin::LEFT);
+ s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::RIGHT);
break;
case 'H':
- s->textOrigin &= ~Style::ORIGIN_LEFT;
- s->textOrigin &= ~Style::ORIGIN_RIGHT;
+ s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::LEFT);
+ s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::RIGHT);
break;
case 'R':
- s->textOrigin &= ~Style::ORIGIN_LEFT;
- s->textOrigin |= Style::ORIGIN_RIGHT;
+ s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::LEFT);
+ s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin | (uint32_t)Style::TextOrigin::RIGHT);
break;
// Vertical text alignment
case 'B':
- s->textOrigin |= Style::ORIGIN_BOT;
- s->textOrigin &= ~Style::ORIGIN_TOP;
+ s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin | (uint32_t)Style::TextOrigin::BOT);
+ s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::TOP);
break;
case 'V':
- s->textOrigin &= ~Style::ORIGIN_BOT;
- s->textOrigin &= ~Style::ORIGIN_TOP;
+ s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::BOT);
+ s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::TOP);
break;
case 'T':
- s->textOrigin &= ~Style::ORIGIN_BOT;
- s->textOrigin |= Style::ORIGIN_TOP;
+ s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::BOT);
+ s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin | (uint32_t)Style::TextOrigin::TOP);
break;
}
- InvalidateGraphics();
+ SS.GW.Invalidate(/*clearPersistent=*/true);
}
-bool TextWindow::EditControlDoneForStyles(const char *str) {
+bool TextWindow::EditControlDoneForStyles(const std::string &str) {
Style *s;
switch(edit.meaning) {
- case EDIT_STYLE_STIPPLE_PERIOD:
- case EDIT_STYLE_TEXT_HEIGHT:
- case EDIT_STYLE_WIDTH: {
+ case Edit::STYLE_STIPPLE_PERIOD:
+ case Edit::STYLE_TEXT_HEIGHT:
+ case Edit::STYLE_WIDTH: {
SS.UndoRemember();
s = Style::Get(edit.style);
double v;
- int units = (edit.meaning == EDIT_STYLE_TEXT_HEIGHT) ?
+ Style::UnitsAs units = (edit.meaning == Edit::STYLE_TEXT_HEIGHT) ?
s->textHeightAs : s->widthAs;
- if(units == Style::UNITS_AS_MM) {
+ if(units == Style::UnitsAs::MM) {
v = SS.StringToMm(str);
} else {
- v = atof(str);
+ v = atof(str.c_str());
}
v = max(0.0, v);
- if(edit.meaning == EDIT_STYLE_TEXT_HEIGHT) {
+ if(edit.meaning == Edit::STYLE_TEXT_HEIGHT) {
s->textHeight = v;
- } else if(edit.meaning == EDIT_STYLE_STIPPLE_PERIOD) {
+ } else if(edit.meaning == Edit::STYLE_STIPPLE_PERIOD) {
s->stippleScale = v;
} else {
s->width = v;
}
break;
}
- case EDIT_STYLE_TEXT_ANGLE:
+ case Edit::STYLE_TEXT_ANGLE:
SS.UndoRemember();
s = Style::Get(edit.style);
- s->textAngle = WRAP_SYMMETRIC(atof(str), 360);
+ s->textAngle = WRAP_SYMMETRIC(atof(str.c_str()), 360);
break;
- case EDIT_BACKGROUND_COLOR:
- case EDIT_STYLE_FILL_COLOR:
- case EDIT_STYLE_COLOR: {
+ case Edit::BACKGROUND_COLOR:
+ case Edit::STYLE_FILL_COLOR:
+ case Edit::STYLE_COLOR: {
Vector rgb;
- if(sscanf(str, "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
+ if(sscanf(str.c_str(), "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
rgb = rgb.ClampWithin(0, 1);
- if(edit.meaning == EDIT_STYLE_COLOR) {
+ if(edit.meaning == Edit::STYLE_COLOR) {
SS.UndoRemember();
s = Style::Get(edit.style);
s->color = RGBf(rgb.x, rgb.y, rgb.z);
- } else if(edit.meaning == EDIT_STYLE_FILL_COLOR) {
+ } else if(edit.meaning == Edit::STYLE_FILL_COLOR) {
SS.UndoRemember();
s = Style::Get(edit.style);
s->fillColor = RGBf(rgb.x, rgb.y, rgb.z);
SS.backgroundColor = RGBf(rgb.x, rgb.y, rgb.z);
}
} else {
- Error("Bad format: specify color as r, g, b");
+ Error(_("Bad format: specify color as r, g, b"));
}
break;
}
- case EDIT_STYLE_NAME:
- if(!*str) {
- Error("Style name cannot be empty");
+ case Edit::STYLE_NAME:
+ if(str.empty()) {
+ Error(_("Style name cannot be empty"));
} else {
SS.UndoRemember();
s = Style::Get(edit.style);
}
break;
- case EDIT_BACKGROUND_IMG_SCALE: {
- Expr *e = Expr::From(str, true);
- if(e) {
- double ev = e->Eval();
- if(ev < 0.001 || isnan(ev)) {
- Error("Scale must not be zero or negative!");
- } else {
- SS.bgImage.scale = ev / SS.MmPerUnit();
- }
- }
- break;
- }
default: return false;
}
+ SS.GW.persistentDirty = true;
return true;
}
-void TextWindow::ShowStyleInfo(void) {
+void TextWindow::ShowStyleInfo() {
Printf(true, "%Fl%f%Ll(back to list of styles)%E", &ScreenShowListOfStyles);
Style *s = Style::Get(shown.style);
s->h.v, ScreenChangeStyleColor);
// The line width, and its units
- if(s->widthAs == Style::UNITS_AS_PIXELS) {
+ if(s->widthAs == Style::UnitsAs::PIXELS) {
Printf(false, " %Ftwidth%E %@ %D%f%Lp%Fl[change]%E",
s->width,
s->h.v, &ScreenChangeStyleMetric,
(s->h.v < Style::FIRST_CUSTOM) ? 'w' : 'W');
}
- if(s->h.v >= Style::FIRST_CUSTOM) {
- if(s->widthAs == Style::UNITS_AS_PIXELS) {
- Printf(false, "%Ba %Ftstipple width%E %@ %D%f%Lp%Fl[change]%E",
- s->stippleScale,
- s->h.v, &ScreenChangeStyleMetric, 's');
- } else {
- Printf(false, "%Ba %Ftstipple width%E %s %D%f%Lp%Fl[change]%E",
- SS.MmToString(s->stippleScale).c_str(),
- s->h.v, &ScreenChangeStyleMetric, 's');
- }
+ if(s->widthAs == Style::UnitsAs::PIXELS) {
+ Printf(false, "%Ba %Ftstipple width%E %@ %D%f%Lp%Fl[change]%E",
+ s->stippleScale,
+ s->h.v, &ScreenChangeStyleMetric, 's');
+ } else {
+ Printf(false, "%Ba %Ftstipple width%E %s %D%f%Lp%Fl[change]%E",
+ SS.MmToString(s->stippleScale).c_str(),
+ s->h.v, &ScreenChangeStyleMetric, 's');
}
- bool widthpx = (s->widthAs == Style::UNITS_AS_PIXELS);
+ bool widthpx = (s->widthAs == Style::UnitsAs::PIXELS);
if(s->h.v < Style::FIRST_CUSTOM) {
Printf(false," %Ftin units of %Fdpixels%E");
} else {
Printf(false,"%Ba %Ftstipple type:%E");
- const size_t patternCount = Style::LAST_STIPPLE + 1;
+ const size_t patternCount = (size_t)StipplePattern::LAST + 1;
const char *patternsSource[patternCount] = {
"___________",
+ "- - - - ",
"- - - - - -",
"__ __ __ __",
"-.-.-.-.-.-",
};
std::string patterns[patternCount];
- for(int i = 0; i <= Style::LAST_STIPPLE; i++) {
+ for(uint32_t i = 0; i <= (uint32_t)StipplePattern::LAST; i++) {
const char *str = patternsSource[i];
do {
switch(*str) {
case '_': patterns[i] += "\xEE\x80\x85"; break;
case '-': patterns[i] += "\xEE\x80\x86"; break;
case '~': patterns[i] += "\xEE\x80\x87"; break;
- default: oops();
+ default: ssassert(false, "Unexpected stipple pattern element");
}
} while(*(++str));
}
- for(int i = 0; i <= Style::LAST_STIPPLE; i++) {
- const char *radio = s->stippleType == i ? RADIO_TRUE : RADIO_FALSE;
+ for(uint32_t i = 0; i <= (uint32_t)StipplePattern::LAST; i++) {
+ const char *radio = s->stippleType == (StipplePattern)i ? RADIO_TRUE : RADIO_FALSE;
Printf(false, "%Bp %D%f%Lp%s %s%E",
(i % 2 == 0) ? 'd' : 'a',
s->h.v, &ScreenChangeStylePatternType,
Printf(false, "");
Printf(false, "%Ft text style%E");
- if(s->textHeightAs == Style::UNITS_AS_PIXELS) {
+ if(s->textHeightAs == Style::UnitsAs::PIXELS) {
Printf(false, "%Ba %Ftheight %E%@ %D%f%Lt%Fl%s%E",
s->textHeight,
s->h.v, &ScreenChangeStyleMetric,
"[change]");
}
- bool textHeightpx = (s->textHeightAs == Style::UNITS_AS_PIXELS);
+ bool textHeightpx = (s->textHeightAs == Style::UnitsAs::PIXELS);
if(s->h.v < Style::FIRST_CUSTOM) {
Printf(false,"%Bd %Ftin units of %Fdpixels");
} else {
Printf(false, "");
Printf(false, "%Ft text comment alignment%E");
bool neither;
- neither = !(s->textOrigin & (Style::ORIGIN_LEFT | Style::ORIGIN_RIGHT));
+ neither = !((uint32_t)s->textOrigin & ((uint32_t)Style::TextOrigin::LEFT | (uint32_t)Style::TextOrigin::RIGHT));
Printf(false, "%Ba "
"%D%f%LL%s left%E "
"%D%f%LH%s center%E "
"%D%f%LR%s right%E ",
s->h.v, &ScreenChangeStyleYesNo,
- (s->textOrigin & Style::ORIGIN_LEFT) ? RADIO_TRUE : RADIO_FALSE,
+ ((uint32_t)s->textOrigin & (uint32_t)Style::TextOrigin::LEFT) ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
neither ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
- (s->textOrigin & Style::ORIGIN_RIGHT) ? RADIO_TRUE : RADIO_FALSE);
+ ((uint32_t)s->textOrigin & (uint32_t)Style::TextOrigin::RIGHT) ? RADIO_TRUE : RADIO_FALSE);
- neither = !(s->textOrigin & (Style::ORIGIN_BOT | Style::ORIGIN_TOP));
+ neither = !((uint32_t)s->textOrigin & ((uint32_t)Style::TextOrigin::BOT | (uint32_t)Style::TextOrigin::TOP));
Printf(false, "%Bd "
"%D%f%LB%s bottom%E "
"%D%f%LV%s center%E "
"%D%f%LT%s top%E ",
s->h.v, &ScreenChangeStyleYesNo,
- (s->textOrigin & Style::ORIGIN_BOT) ? RADIO_TRUE : RADIO_FALSE,
+ ((uint32_t)s->textOrigin & (uint32_t)Style::TextOrigin::BOT) ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
neither ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
- (s->textOrigin & Style::ORIGIN_TOP) ? RADIO_TRUE : RADIO_FALSE);
+ ((uint32_t)s->textOrigin & (uint32_t)Style::TextOrigin::TOP) ? RADIO_TRUE : RADIO_FALSE);
}
if(s->h.v >= Style::FIRST_CUSTOM) {
void TextWindow::ScreenAssignSelectionToStyle(int link, uint32_t v) {
Style::AssignSelectionToStyle(v);
}
-
const double System::CONVERGE_TOLERANCE = (LENGTH_EPS/(1e2));
bool System::WriteJacobian(int tag) {
- int a, i, j;
- j = 0;
- for(a = 0; a < param.n; a++) {
- if(j >= MAX_UNKNOWNS) return false;
+ int j = 0;
+ for(auto &p : param) {
+ if(j >= MAX_UNKNOWNS)
+ return false;
- Param *p = &(param.elem[a]);
- if(p->tag != tag) continue;
- mat.param[j] = p->h;
+ if(p.tag != tag)
+ continue;
+ mat.param[j] = p.h;
j++;
}
mat.n = j;
- i = 0;
- for(a = 0; a < eq.n; a++) {
+ int i = 0;
+
+ for(auto &e : eq) {
if(i >= MAX_UNKNOWNS) return false;
- Equation *e = &(eq.elem[a]);
- if(e->tag != tag) continue;
+ if(e.tag != tag)
+ continue;
- mat.eq[i] = e->h;
- Expr *f = e->e->DeepCopyWithParamsAsPointers(¶m, &(SK.param));
+ mat.eq[i] = e.h;
+ Expr *f = e.e->DeepCopyWithParamsAsPointers(¶m, &(SK.param));
f = f->FoldConstants();
// Hash table (61 bits) to accelerate generation of zero partials.
return true;
}
-void System::EvalJacobian(void) {
+void System::EvalJacobian() {
int i, j;
for(i = 0; i < mat.m; i++) {
for(j = 0; j < mat.n; j++) {
bool System::IsDragged(hParam p) {
hParam *pp;
for(pp = dragged.First(); pp; pp = dragged.NextAfter(pp)) {
- if(p.v == pp->v) return true;
+ if(p == *pp) return true;
}
return false;
}
-void System::SolveBySubstitution(void) {
- int i;
- for(i = 0; i < eq.n; i++) {
- Equation *teq = &(eq.elem[i]);
- Expr *tex = teq->e;
+void System::SolveBySubstitution() {
+ for(auto &teq : eq) {
+ Expr *tex = teq.e;
- if(tex->op == Expr::MINUS &&
- tex->a->op == Expr::PARAM &&
- tex->b->op == Expr::PARAM)
+ if(tex->op == Expr::Op::MINUS &&
+ tex->a->op == Expr::Op::PARAM &&
+ tex->b->op == Expr::Op::PARAM)
{
hParam a = tex->a->parh;
hParam b = tex->b->parh;
if(IsDragged(a)) {
// A is being dragged, so A should stay, and B should go
- hParam t = a;
- a = b;
- b = t;
+ std::swap(a, b);
}
- int j;
- for(j = 0; j < eq.n; j++) {
- Equation *req = &(eq.elem[j]);
- (req->e)->Substitute(a, b); // A becomes B, B unchanged
+ for(auto &req : eq) {
+ req.e->Substitute(a, b); // A becomes B, B unchanged
}
- for(j = 0; j < param.n; j++) {
- Param *rp = &(param.elem[j]);
- if(rp->substd.v == a.v) {
- rp->substd = b;
+ for(auto &rp : param) {
+ if(rp.substd == a) {
+ rp.substd = b;
}
}
Param *ptr = param.FindById(a);
ptr->tag = VAR_SUBSTITUTED;
ptr->substd = b;
- teq->tag = EQ_SUBSTITUTED;
+ teq.tag = EQ_SUBSTITUTED;
}
}
}
// in place. A row (~equation) is considered to be all zeros if its magnitude
// is less than the tolerance RANK_MAG_TOLERANCE.
//-----------------------------------------------------------------------------
-int System::CalculateRank(void) {
+int System::CalculateRank() {
// Actually work with magnitudes squared, not the magnitudes
double rowMag[MAX_UNKNOWNS] = {};
double tol = RANK_MAG_TOLERANCE*RANK_MAG_TOLERANCE;
return rank;
}
-bool System::TestRank(void) {
+bool System::TestRank(int *rank) {
EvalJacobian();
- return CalculateRank() == mat.m;
+ int jacobianRank = CalculateRank();
+ if(rank) *rank = jacobianRank;
+ return jacobianRank == mat.m;
}
bool System::SolveLinearSystem(double X[], double A[][MAX_UNKNOWNS],
// greater. First, find a pivot (between rows i and N-1).
max = 0;
for(ip = i; ip < n; ip++) {
- if(ffabs(A[ip][i]) > max) {
+ if(fabs(A[ip][i]) > max) {
imax = ip;
- max = ffabs(A[ip][i]);
+ max = fabs(A[ip][i]);
}
}
// Don't give up on a singular matrix unless it's really bad; the
// assumption code is responsible for identifying that condition,
// so we're not responsible for reporting that error.
- if(ffabs(max) < 1e-20) continue;
+ if(fabs(max) < 1e-20) continue;
// Swap row imax with row i
for(jp = 0; jp < n; jp++) {
// We've put the matrix in upper triangular form, so at this point we
// can solve by back-substitution.
for(i = n - 1; i >= 0; i--) {
- if(ffabs(A[i][i]) < 1e-20) continue;
+ if(fabs(A[i][i]) < 1e-20) continue;
temp = B[i];
for(j = n - 1; j > i; j--) {
return true;
}
-bool System::SolveLeastSquares(void) {
+bool System::SolveLeastSquares() {
int r, c, i;
// Scale the columns; this scale weights the parameters for the least
for(i = 0; i < mat.n; i++) {
Param *p = param.FindById(mat.param[i]);
p->val -= mat.X[i];
- if(isnan(p->val)) {
+ if(IsReasonable(p->val)) {
// Very bad, and clearly not convergent
return false;
}
// Check for convergence
converged = true;
for(i = 0; i < mat.m; i++) {
- if(isnan(mat.B.num[i])) {
+ if(IsReasonable(mat.B.num[i])) {
return false;
}
- if(ffabs(mat.B.num[i]) > CONVERGE_TOLERANCE) {
+ if(fabs(mat.B.num[i]) > CONVERGE_TOLERANCE) {
converged = false;
break;
}
}
void System::WriteEquationsExceptFor(hConstraint hc, Group *g) {
- int i;
// Generate all the equations from constraints in this group
- for(i = 0; i < SK.constraint.n; i++) {
- ConstraintBase *c = &(SK.constraint.elem[i]);
- if(c->group.v != g->h.v) continue;
- if(c->h.v == hc.v) continue;
+ for(auto &con : SK.constraint) {
+ ConstraintBase *c = &con;
+ if(c->group != g->h) continue;
+ if(c->h == hc) continue;
- if(c->HasLabel() && c->type != Constraint::COMMENT &&
+ if(c->HasLabel() && c->type != Constraint::Type::COMMENT &&
g->allDimsReference)
{
// When all dimensions are reference, we adjust them to display
c->ModifyToSatisfy();
continue;
}
- if(g->relaxConstraints && c->type != Constraint::POINTS_COINCIDENT) {
+ if(g->relaxConstraints && c->type != Constraint::Type::POINTS_COINCIDENT) {
// When the constraints are relaxed, we keep only the point-
// coincident constraints, and the constraints generated by
// the entities and groups.
continue;
}
- c->Generate(&eq);
+ c->GenerateEquations(&eq);
}
// And the equations from entities
- for(i = 0; i < SK.entity.n; i++) {
- EntityBase *e = &(SK.entity.elem[i]);
- if(e->group.v != g->h.v) continue;
+ for(auto &ent : SK.entity) {
+ EntityBase *e = &ent;
+ if(e->group != g->h) continue;
e->GenerateEquations(&eq);
}
g->GenerateEquations(&eq);
}
-void System::FindWhichToRemoveToFixJacobian(Group *g, List<hConstraint> *bad) {
- int a, i;
+void System::FindWhichToRemoveToFixJacobian(Group *g, List<hConstraint> *bad, bool forceDofCheck) {
+ auto time = GetMilliseconds();
+ g->solved.timeout = false;
+ int a;
for(a = 0; a < 2; a++) {
- for(i = 0; i < SK.constraint.n; i++) {
- ConstraintBase *c = &(SK.constraint.elem[i]);
- if(c->group.v != g->h.v) continue;
- if((c->type == Constraint::POINTS_COINCIDENT && a == 0) ||
- (c->type != Constraint::POINTS_COINCIDENT && a == 1))
+ for(auto &con : SK.constraint) {
+ if((GetMilliseconds() - time) > g->solved.findToFixTimeout) {
+ g->solved.timeout = true;
+ return;
+ }
+
+ ConstraintBase *c = &con;
+ if(c->group != g->h) continue;
+ if((c->type == Constraint::Type::POINTS_COINCIDENT && a == 0) ||
+ (c->type != Constraint::Type::POINTS_COINCIDENT && a == 1))
{
// Do the constraints in two passes: first everything but
// the point-coincident constraints, then only those
// It's a major speedup to solve the easy ones by substitution here,
// and that doesn't break anything.
- SolveBySubstitution();
+ if(!forceDofCheck) {
+ SolveBySubstitution();
+ }
WriteJacobian(0);
EvalJacobian();
}
}
-int System::Solve(Group *g, int *dof, List<hConstraint> *bad,
- bool andFindBad, bool andFindFree)
+SolveResult System::Solve(Group *g, int *rank, int *dof, List<hConstraint> *bad,
+ bool andFindBad, bool andFindFree, bool forceDofCheck)
{
WriteEquationsExceptFor(Constraint::NO_CONSTRAINT, g);
/*
dbp("%d equations", eq.n);
for(i = 0; i < eq.n; i++) {
- dbp(" %.3f = %s = 0", eq.elem[i].e->Eval(), eq.elem[i].e->Print());
+ dbp(" %.3f = %s = 0", eq[i].e->Eval(), eq[i].e->Print());
}
dbp("%d parameters", param.n);
for(i = 0; i < param.n; i++) {
- dbp(" param %08x at %.3f", param.elem[i].h.v, param.elem[i].val);
+ dbp(" param %08x at %.3f", param[i].h.v, param[i].val);
} */
// All params and equations are assigned to group zero.
param.ClearTags();
eq.ClearTags();
- SolveBySubstitution();
+ // Solving by substitution eliminates duplicate e.g. H/V constraints, which can cause rank test
+ // to succeed even on overdefined systems, which will fail later.
+ if(!forceDofCheck) {
+ SolveBySubstitution();
+ }
// Before solving the big system, see if we can find any equations that
// are soluble alone. This can be a huge speedup. We don't know whether
// the system is consistent yet, but if it isn't then we'll catch that
// later.
int alone = 1;
- for(i = 0; i < eq.n; i++) {
- Equation *e = &(eq.elem[i]);
- if(e->tag != 0) continue;
+ for(auto &e : eq) {
+ if(e.tag != 0)
+ continue;
- hParam hp = e->e->ReferencedParams(¶m);
- if(hp.v == Expr::NO_PARAMS.v) continue;
- if(hp.v == Expr::MULTIPLE_PARAMS.v) continue;
+ hParam hp = e.e->ReferencedParams(¶m);
+ if(hp == Expr::NO_PARAMS) continue;
+ if(hp == Expr::MULTIPLE_PARAMS) continue;
Param *p = param.FindById(hp);
if(p->tag != 0) continue; // let rank test catch inconsistency
- e->tag = alone;
+ e.tag = alone;
p->tag = alone;
WriteJacobian(alone);
if(!NewtonSolve(alone)) {
+ // We don't do the rank test, so let's arbitrarily return
+ // the DIDNT_CONVERGE result here.
+ rankOk = true;
// Failed to converge, bail out early
goto didnt_converge;
}
// Now write the Jacobian for what's left, and do a rank test; that
// tells us if the system is inconsistently constrained.
if(!WriteJacobian(0)) {
- return System::TOO_MANY_UNKNOWNS;
+ return SolveResult::TOO_MANY_UNKNOWNS;
}
- rankOk = TestRank();
+ rankOk = TestRank(rank);
// And do the leftovers as one big system
if(!NewtonSolve(0)) {
goto didnt_converge;
}
- rankOk = TestRank();
+ rankOk = TestRank(rank);
if(!rankOk) {
- if(!g->allowRedundant) {
- if(andFindBad) FindWhichToRemoveToFixJacobian(g, bad);
- }
+ if(andFindBad) FindWhichToRemoveToFixJacobian(g, bad, forceDofCheck);
} else {
// This is not the full Jacobian, but any substitutions or single-eq
// solves removed one equation and one unknown, therefore no effect
// on the number of DOF.
- if(dof) *dof = mat.n - mat.m;
-
- // If requested, find all the free (unbound) variables. This might be
- // more than the number of degrees of freedom. Don't always do this,
- // because the display would get annoying and it's slow.
- for(i = 0; i < param.n; i++) {
- Param *p = &(param.elem[i]);
- p->free = false;
-
- if(andFindFree) {
- if(p->tag == 0) {
- p->tag = VAR_DOF_TEST;
- WriteJacobian(0);
- EvalJacobian();
- int rank = CalculateRank();
- if(rank == mat.m) {
- p->free = true;
- }
- p->tag = 0;
- }
- }
- }
+ if(dof) *dof = CalculateDof();
+ MarkParamsFree(andFindFree);
}
// System solved correctly, so write the new values back in to the
// main parameter table.
- for(i = 0; i < param.n; i++) {
- Param *p = &(param.elem[i]);
+ for(auto &p : param) {
double val;
- if(p->tag == VAR_SUBSTITUTED) {
- val = param.FindById(p->substd)->val;
+ if(p.tag == VAR_SUBSTITUTED) {
+ val = param.FindById(p.substd)->val;
} else {
- val = p->val;
+ val = p.val;
}
- Param *pp = SK.GetParam(p->h);
+ Param *pp = SK.GetParam(p.h);
pp->val = val;
pp->known = true;
- pp->free = p->free;
+ pp->free = p.free;
}
- return rankOk ? System::SOLVED_OKAY : System::REDUNDANT_OKAY;
+ return rankOk ? SolveResult::OKAY : SolveResult::REDUNDANT_OKAY;
didnt_converge:
SK.constraint.ClearTags();
+ // Not using range-for here because index is used in additional ways
for(i = 0; i < eq.n; i++) {
- if(ffabs(mat.B.num[i]) > CONVERGE_TOLERANCE || isnan(mat.B.num[i])) {
+ if(fabs(mat.B.num[i]) > CONVERGE_TOLERANCE || IsReasonable(mat.B.num[i])) {
// This constraint is unsatisfied.
if(!mat.eq[i].isFromConstraint()) continue;
}
}
- return rankOk ? System::DIDNT_CONVERGE : System::REDUNDANT_DIDNT_CONVERGE;
+ return rankOk ? SolveResult::DIDNT_CONVERGE : SolveResult::REDUNDANT_DIDNT_CONVERGE;
+}
+
+SolveResult System::SolveRank(Group *g, int *rank, int *dof, List<hConstraint> *bad,
+ bool andFindBad, bool andFindFree)
+{
+ WriteEquationsExceptFor(Constraint::NO_CONSTRAINT, g);
+
+ // All params and equations are assigned to group zero.
+ param.ClearTags();
+ eq.ClearTags();
+
+ // Now write the Jacobian, and do a rank test; that
+ // tells us if the system is inconsistently constrained.
+ if(!WriteJacobian(0)) {
+ return SolveResult::TOO_MANY_UNKNOWNS;
+ }
+
+ bool rankOk = TestRank(rank);
+ if(!rankOk) {
+ if(andFindBad) FindWhichToRemoveToFixJacobian(g, bad, /*forceDofCheck=*/true);
+ } else {
+ if(dof) *dof = CalculateDof();
+ MarkParamsFree(andFindFree);
+ }
+ return rankOk ? SolveResult::OKAY : SolveResult::REDUNDANT_OKAY;
}
-void System::Clear(void) {
+void System::Clear() {
entity.Clear();
param.Clear();
eq.Clear();
dragged.Clear();
}
+
+void System::MarkParamsFree(bool find) {
+ // If requested, find all the free (unbound) variables. This might be
+ // more than the number of degrees of freedom. Don't always do this,
+ // because the display would get annoying and it's slow.
+ for(auto &p : param) {
+ p.free = false;
+
+ if(find) {
+ if(p.tag == 0) {
+ p.tag = VAR_DOF_TEST;
+ WriteJacobian(0);
+ EvalJacobian();
+ int rank = CalculateRank();
+ if(rank == mat.m) {
+ p.free = true;
+ }
+ p.tag = 0;
+ }
+ }
+ }
+}
+
+int System::CalculateDof() {
+ return mat.n - mat.m;
+}
+
// link to bring us back home.
//-----------------------------------------------------------------------------
void TextWindow::ScreenHome(int link, uint32_t v) {
- SS.TW.GoToScreen(SCREEN_LIST_OF_GROUPS);
+ SS.TW.GoToScreen(Screen::LIST_OF_GROUPS);
}
void TextWindow::ShowHeader(bool withNav) {
ClearScreen();
// to hide or show them, and to view them in detail. This is our home page.
//-----------------------------------------------------------------------------
void TextWindow::ScreenSelectGroup(int link, uint32_t v) {
- SS.TW.GoToScreen(SCREEN_GROUP_INFO);
+ GraphicsWindow::MenuEdit(Command::UNSELECT_ALL);
+ SS.TW.GoToScreen(Screen::GROUP_INFO);
SS.TW.shown.group.v = v;
}
void TextWindow::ScreenToggleGroupShown(int link, uint32_t v) {
}
void TextWindow::ScreenShowGroupsSpecial(int link, uint32_t v) {
bool state = link == 's';
- for(int i = 0; i < SK.groupOrder.n; i++) {
- Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
+ for(hGroup hg : SK.groupOrder) {
+ Group *g = SK.GetGroup(hg);
+
g->visible = state;
}
+ SS.GW.persistentDirty = true;
}
void TextWindow::ScreenActivateGroup(int link, uint32_t v) {
SS.GW.activeGroup.v = v;
}
void TextWindow::ReportHowGroupSolved(hGroup hg) {
SS.GW.ClearSuper();
- SS.TW.GoToScreen(SCREEN_GROUP_SOLVE_INFO);
- SS.TW.shown.group.v = hg.v;
+ SS.TW.GoToScreen(Screen::GROUP_SOLVE_INFO);
+ SS.TW.shown.group = hg;
SS.ScheduleShowTW();
}
void TextWindow::ScreenHowGroupSolved(int link, uint32_t v) {
if(SS.GW.activeGroup.v != v) {
ScreenActivateGroup(link, v);
}
- SS.TW.GoToScreen(SCREEN_GROUP_SOLVE_INFO);
+ SS.TW.GoToScreen(Screen::GROUP_SOLVE_INFO);
SS.TW.shown.group.v = v;
}
void TextWindow::ScreenShowConfiguration(int link, uint32_t v) {
- SS.TW.GoToScreen(SCREEN_CONFIGURATION);
+ SS.TW.GoToScreen(Screen::CONFIGURATION);
}
void TextWindow::ScreenShowEditView(int link, uint32_t v) {
- SS.TW.GoToScreen(SCREEN_EDIT_VIEW);
+ SS.TW.GoToScreen(Screen::EDIT_VIEW);
}
void TextWindow::ScreenGoToWebsite(int link, uint32_t v) {
- OpenWebsite("http://solvespace.com/txtlink");
+ Platform::OpenInBrowser("http://solvespace.com/txtlink");
}
-void TextWindow::ShowListOfGroups(void) {
+void TextWindow::ShowListOfGroups() {
const char *radioTrue = " " RADIO_TRUE " ",
*radioFalse = " " RADIO_FALSE " ",
*checkTrue = " " CHECK_TRUE " ",
*checkFalse = " " CHECK_FALSE " ";
Printf(true, "%Ft active");
- Printf(false, "%Ft shown ok group-name%E");
- int i;
+ Printf(false, "%Ft shown dof group-name%E");
bool afterActive = false;
- for(i = 0; i < SK.groupOrder.n; i++) {
- Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
+ bool backgroundParity = false;
+ for(hGroup hg : SK.groupOrder) {
+ Group *g = SK.GetGroup(hg);
+
std::string s = g->DescriptionString();
- bool active = (g->h.v == SS.GW.activeGroup.v);
+ bool active = (g->h == SS.GW.activeGroup);
bool shown = g->visible;
bool ok = g->IsSolvedOkay();
- bool ref = (g->h.v == Group::HGROUP_REFERENCES.v);
- Printf(false, "%Bp%Fd "
+ bool warn = (g->type == Group::Type::DRAWING_WORKPLANE &&
+ g->polyError.how != PolyError::GOOD) ||
+ ((g->type == Group::Type::EXTRUDE ||
+ g->type == Group::Type::LATHE) &&
+ SK.GetGroup(g->opA)->polyError.how != PolyError::GOOD);
+ int dof = g->solved.dof;
+ char sdof[16] = "ok ";
+ if(ok && dof > 0) {
+ if(dof > 999) {
+ strcpy(sdof, "###");
+ } else {
+ sprintf(sdof, "%-3d", dof);
+ }
+ }
+ bool ref = (g->h == Group::HGROUP_REFERENCES);
+ Printf(false,
+ "%Bp%Fd "
"%Ft%s%Fb%D%f%Ll%s%E "
"%Fb%s%D%f%Ll%s%E "
- "%Fp%D%f%s%Ll%s%E "
+ "%Fp%D%f%s%Ll%s%E "
"%Fl%Ll%D%f%s",
- // Alternate between light and dark backgrounds, for readability
- (i & 1) ? 'd' : 'a',
- // Link that activates the group
- ref ? " " : "",
- g->h.v, (&TextWindow::ScreenActivateGroup),
- ref ? "" : (active ? radioTrue : radioFalse),
- // Link that hides or shows the group
- afterActive ? " - " : "",
- g->h.v, (&TextWindow::ScreenToggleGroupShown),
- afterActive ? "" : (shown ? checkTrue : checkFalse),
- // Link to the errors, if a problem occured while solving
- ok ? 's' : 'x', g->h.v, (&TextWindow::ScreenHowGroupSolved),
- ok ? "ok" : "",
- ok ? "" : "NO",
- // Link to a screen that gives more details on the group
- g->h.v, (&TextWindow::ScreenSelectGroup), s.c_str());
+ // Alternate between light and dark backgrounds, for readability
+ backgroundParity ? 'd' : 'a',
+ // Link that activates the group
+ ref ? " " : "",
+ g->h.v, (&TextWindow::ScreenActivateGroup),
+ ref ? "" : (active ? radioTrue : radioFalse),
+ // Link that hides or shows the group
+ afterActive ? " - " : "",
+ g->h.v, (&TextWindow::ScreenToggleGroupShown),
+ afterActive ? "" : (shown ? checkTrue : checkFalse),
+ // Link to the errors, if a problem occurred while solving
+ ok ? (warn ? 'm' : (dof > 0 ? 'i' : 's')) : 'x',
+ g->h.v, (&TextWindow::ScreenHowGroupSolved),
+ ok ? (warn ? "err" : sdof) : "",
+ ok ? "" : "ERR",
+ // Link to a screen that gives more details on the group
+ g->h.v, (&TextWindow::ScreenSelectGroup), s.c_str());
if(active) afterActive = true;
+ backgroundParity = !backgroundParity;
}
Printf(true, " %Fl%Ls%fshow all%E / %Fl%Lh%fhide all%E",
// The screen that shows information about a specific group, and allows the
// user to edit various things about it.
//-----------------------------------------------------------------------------
-void TextWindow::ScreenHoverConstraint(int link, uint32_t v) {
- if(!SS.GW.showConstraints) return;
-
- hConstraint hc = { v };
- Constraint *c = SK.GetConstraint(hc);
- if(c->group.v != SS.GW.activeGroup.v) {
- // Only constraints in the active group are visible
- return;
- }
+void TextWindow::ScreenHoverGroupWorkplane(int link, uint32_t v) {
SS.GW.hover.Clear();
- SS.GW.hover.constraint = hc;
+ hGroup hg = { v };
+ SS.GW.hover.entity = hg.entity(0);
+ SS.GW.hover.emphasized = true;
+}
+void TextWindow::ScreenHoverEntity(int link, uint32_t v) {
+ SS.GW.hover.Clear();
+ hEntity he = { v };
+ SS.GW.hover.entity = he;
SS.GW.hover.emphasized = true;
}
void TextWindow::ScreenHoverRequest(int link, uint32_t v) {
SS.GW.hover.entity = hr.entity(0);
SS.GW.hover.emphasized = true;
}
-void TextWindow::ScreenSelectConstraint(int link, uint32_t v) {
+void TextWindow::ScreenHoverConstraint(int link, uint32_t v) {
+ if(!SS.GW.showConstraints) return;
+
+ hConstraint hc = { v };
+ SS.GW.hover.Clear();
+ SS.GW.hover.constraint = hc;
+ SS.GW.hover.emphasized = true;
+}
+void TextWindow::ScreenSelectEntity(int link, uint32_t v) {
SS.GW.ClearSelection();
GraphicsWindow::Selection sel = {};
- sel.constraint.v = v;
+ hEntity he = { v };
+ sel.entity = he;
SS.GW.selection.Add(&sel);
}
void TextWindow::ScreenSelectRequest(int link, uint32_t v) {
sel.entity = hr.entity(0);
SS.GW.selection.Add(&sel);
}
+void TextWindow::ScreenSelectConstraint(int link, uint32_t v) {
+ SS.GW.ClearSelection();
+ GraphicsWindow::Selection sel = {};
+ sel.constraint.v = v;
+ SS.GW.selection.Add(&sel);
+}
void TextWindow::ScreenChangeGroupOption(int link, uint32_t v) {
SS.UndoRemember();
Group *g = SK.GetGroup(SS.TW.shown.group);
switch(link) {
- case 's': g->subtype = Group::ONE_SIDED; break;
- case 'S': g->subtype = Group::TWO_SIDED; break;
+ case 's': g->subtype = Group::Subtype::ONE_SIDED; break;
+ case 'S': g->subtype = Group::Subtype::TWO_SIDED; break;
case 'k': g->skipFirst = true; break;
case 'K': g->skipFirst = false; break;
case 'c':
- // When an extrude group is first created, it's positioned for a union
- // extrusion. If no constraints were added, flip it when we switch between
- // union and difference modes to avoid manual work doing the smae.
- if(g->meshCombine != (int)v && g->GetNumConstraints() == 0 &&
- (v == Group::COMBINE_AS_DIFFERENCE ||
- g->meshCombine == Group::COMBINE_AS_DIFFERENCE)) {
- g->ExtrusionForceVectorTo(g->ExtrusionGetVector().Negated());
+ if(g->type == Group::Type::EXTRUDE) {
+ // When an extrude group is first created, it's positioned for a union
+ // extrusion. If no constraints were added, flip it when we switch between
+ // union/assemble and difference/intersection modes to avoid manual work doing the same.
+ if(g->meshCombine != (Group::CombineAs)v && g->GetNumConstraints() == 0) {
+ // I apologise for this if statement
+ if((((Group::CombineAs::DIFFERENCE == g->meshCombine) ||
+ (Group::CombineAs::INTERSECTION == g->meshCombine)) &&
+ (Group::CombineAs::DIFFERENCE != (Group::CombineAs)v) &&
+ (Group::CombineAs::INTERSECTION != (Group::CombineAs)v)) ||
+ ((Group::CombineAs::DIFFERENCE != g->meshCombine) &&
+ (Group::CombineAs::INTERSECTION != g->meshCombine) &&
+ ((Group::CombineAs::DIFFERENCE == (Group::CombineAs)v) ||
+ (Group::CombineAs::INTERSECTION == (Group::CombineAs)v)))) {
+ g->ExtrusionForceVectorTo(g->ExtrusionGetVector().Negated());
+ }
+ }
}
- g->meshCombine = v;
+ g->meshCombine = (Group::CombineAs)v;
break;
case 'P': g->suppress = !(g->suppress); break;
}
SS.MarkGroupDirty(g->h);
- SS.GenerateAll();
SS.GW.ClearSuper();
}
Group *g = SK.GetGroup(SS.TW.shown.group);
SS.TW.ShowEditControlWithColorPicker(3, g->color);
- SS.TW.edit.meaning = EDIT_GROUP_COLOR;
+ SS.TW.edit.meaning = Edit::GROUP_COLOR;
}
void TextWindow::ScreenOpacity(int link, uint32_t v) {
Group *g = SK.GetGroup(SS.TW.shown.group);
SS.TW.ShowEditControl(11, ssprintf("%.2f", g->color.alphaF()));
- SS.TW.edit.meaning = EDIT_GROUP_OPACITY;
- SS.TW.edit.group.v = g->h.v;
+ SS.TW.edit.meaning = Edit::GROUP_OPACITY;
+ SS.TW.edit.group = g->h;
}
void TextWindow::ScreenChangeExprA(int link, uint32_t v) {
Group *g = SK.GetGroup(SS.TW.shown.group);
SS.TW.ShowEditControl(10, ssprintf("%d", (int)g->valA));
- SS.TW.edit.meaning = EDIT_TIMES_REPEATED;
+ SS.TW.edit.meaning = Edit::TIMES_REPEATED;
SS.TW.edit.group.v = v;
}
void TextWindow::ScreenChangeGroupName(int link, uint32_t v) {
Group *g = SK.GetGroup(SS.TW.shown.group);
SS.TW.ShowEditControl(12, g->DescriptionString().substr(5));
- SS.TW.edit.meaning = EDIT_GROUP_NAME;
+ SS.TW.edit.meaning = Edit::GROUP_NAME;
SS.TW.edit.group.v = v;
}
void TextWindow::ScreenChangeGroupScale(int link, uint32_t v) {
Group *g = SK.GetGroup(SS.TW.shown.group);
SS.TW.ShowEditControl(13, ssprintf("%.3f", g->scale));
- SS.TW.edit.meaning = EDIT_GROUP_SCALE;
+ SS.TW.edit.meaning = Edit::GROUP_SCALE;
SS.TW.edit.group.v = v;
}
void TextWindow::ScreenDeleteGroup(int link, uint32_t v) {
SS.UndoRemember();
hGroup hg = SS.TW.shown.group;
- if(hg.v == SS.GW.activeGroup.v) {
+ if(hg == SS.GW.activeGroup) {
SS.GW.activeGroup = SK.GetGroup(SS.GW.activeGroup)->PreviousGroup()->h;
}
// This is a major change, so let's re-solve everything.
SK.group.RemoveById(hg);
- SS.GenerateAll(SolveSpaceUI::GENERATE_ALL);
+ SS.GenerateAll(SolveSpaceUI::Generate::ALL);
// Reset the graphics window. This will also recreate the default
// group if it was removed.
SS.GW.ClearSuper();
}
-void TextWindow::ShowGroupInfo(void) {
+void TextWindow::ShowGroupInfo() {
Group *g = SK.GetGroup(shown.group);
const char *s = "???";
- if(shown.group.v == Group::HGROUP_REFERENCES.v) {
+ if(shown.group == Group::HGROUP_REFERENCES) {
Printf(true, "%FtGROUP %E%s", g->DescriptionString().c_str());
goto list_items;
} else {
g->h.v, &TextWindow::ScreenDeleteGroup);
}
- if(g->type == Group::LATHE) {
+ if(g->type == Group::Type::LATHE) {
Printf(true, " %Ftlathe plane sketch");
- } else if(g->type == Group::EXTRUDE || g->type == Group::ROTATE ||
- g->type == Group::TRANSLATE)
- {
- if(g->type == Group::EXTRUDE) {
+ } else if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::ROTATE ||
+ g->type == Group::Type::TRANSLATE || g->type == Group::Type::REVOLVE ||
+ g->type == Group::Type::HELIX) {
+ if(g->type == Group::Type::EXTRUDE) {
s = "extrude plane sketch";
- } else if(g->type == Group::TRANSLATE) {
+ } else if(g->type == Group::Type::TRANSLATE) {
s = "translate original sketch";
- } else if(g->type == Group::ROTATE) {
+ } else if(g->type == Group::Type::HELIX) {
+ s = "create helical extrusion";
+ } else if(g->type == Group::Type::ROTATE) {
s = "rotate original sketch";
+ } else if(g->type == Group::Type::REVOLVE) {
+ s = "revolve original sketch";
}
Printf(true, " %Ft%s%E", s);
- bool one = (g->subtype == Group::ONE_SIDED);
+ bool one = (g->subtype == Group::Subtype::ONE_SIDED);
Printf(false,
"%Ba %f%Ls%Fd%s one-sided%E "
"%f%LS%Fd%s two-sided%E",
&TextWindow::ScreenChangeGroupOption,
!one ? RADIO_TRUE : RADIO_FALSE);
- if(g->type == Group::ROTATE || g->type == Group::TRANSLATE) {
- if(g->subtype == Group::ONE_SIDED) {
+ if(g->type == Group::Type::ROTATE || g->type == Group::Type::TRANSLATE) {
+ if(g->subtype == Group::Subtype::ONE_SIDED) {
bool skip = g->skipFirst;
Printf(false,
"%Bd %Ftstart %f%LK%Fd%s with original%E "
int times = (int)(g->valA);
Printf(false, "%Bp %Ftrepeat%E %d time%s %Fl%Ll%D%f[change]%E",
- (g->subtype == Group::ONE_SIDED) ? 'a' : 'd',
+ (g->subtype == Group::Subtype::ONE_SIDED) ? 'a' : 'd',
times, times == 1 ? "" : "s",
g->h.v, &TextWindow::ScreenChangeExprA);
}
- } else if(g->type == Group::LINKED) {
+ } else if(g->type == Group::Type::LINKED) {
Printf(true, " %Ftlink geometry from file%E");
- Printf(false, "%Ba '%s'", g->linkFileRel.c_str());
+ Platform::Path relativePath = g->linkFile.RelativeTo(SS.saveFile.Parent());
+ if(relativePath.IsEmpty()) {
+ Printf(false, "%Ba '%s'", g->linkFile.raw.c_str());
+ } else {
+ Printf(false, "%Ba '%s'", relativePath.raw.c_str());
+ }
Printf(false, "%Bd %Ftscaled by%E %# %Fl%Ll%f%D[change]%E",
g->scale,
&TextWindow::ScreenChangeGroupScale, g->h.v);
- } else if(g->type == Group::DRAWING_3D) {
+ } else if(g->type == Group::Type::DRAWING_3D) {
Printf(true, " %Ftsketch in 3d%E");
- } else if(g->type == Group::DRAWING_WORKPLANE) {
+ } else if(g->type == Group::Type::DRAWING_WORKPLANE) {
Printf(true, " %Ftsketch in new workplane%E");
} else {
Printf(true, "???");
}
Printf(false, "");
- if(g->type == Group::EXTRUDE ||
- g->type == Group::LATHE ||
- g->type == Group::LINKED)
- {
- bool un = (g->meshCombine == Group::COMBINE_AS_UNION);
- bool diff = (g->meshCombine == Group::COMBINE_AS_DIFFERENCE);
- bool asy = (g->meshCombine == Group::COMBINE_AS_ASSEMBLE);
- bool asa = (g->type == Group::LINKED);
+ if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE ||
+ g->type == Group::Type::REVOLVE || g->type == Group::Type::LINKED ||
+ g->type == Group::Type::HELIX) {
+ bool un = (g->meshCombine == Group::CombineAs::UNION);
+ bool diff = (g->meshCombine == Group::CombineAs::DIFFERENCE);
+ bool intr = (g->meshCombine == Group::CombineAs::INTERSECTION);
+ bool asy = (g->meshCombine == Group::CombineAs::ASSEMBLE);
Printf(false, " %Ftsolid model as");
Printf(false, "%Ba %f%D%Lc%Fd%s union%E "
- "%f%D%Lc%Fd%s difference%E "
- "%f%D%Lc%Fd%s%s%E ",
+ "%f%D%Lc%Fd%s assemble%E ",
&TextWindow::ScreenChangeGroupOption,
- Group::COMBINE_AS_UNION,
+ Group::CombineAs::UNION,
un ? RADIO_TRUE : RADIO_FALSE,
&TextWindow::ScreenChangeGroupOption,
- Group::COMBINE_AS_DIFFERENCE,
+ Group::CombineAs::ASSEMBLE,
+ (asy ? RADIO_TRUE : RADIO_FALSE));
+ Printf(false, "%Ba %f%D%Lc%Fd%s difference%E "
+ "%f%D%Lc%Fd%s intersection%E ",
+ &TextWindow::ScreenChangeGroupOption,
+ Group::CombineAs::DIFFERENCE,
diff ? RADIO_TRUE : RADIO_FALSE,
&TextWindow::ScreenChangeGroupOption,
- Group::COMBINE_AS_ASSEMBLE,
- asa ? (asy ? RADIO_TRUE : RADIO_FALSE) : " ",
- asa ? " assemble" : "");
+ Group::CombineAs::INTERSECTION,
+ intr ? RADIO_TRUE : RADIO_FALSE);
- if(g->type == Group::EXTRUDE ||
- g->type == Group::LATHE)
- {
+ if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE ||
+ g->type == Group::Type::REVOLVE || g->type == Group::Type::HELIX) {
Printf(false,
"%Bd %Ftcolor %E%Bz %Bd (%@, %@, %@) %f%D%Lf%Fl[change]%E",
&g->color,
Printf(false, "%Bd %Ftopacity%E %@ %f%Lf%Fl[change]%E",
g->color.alphaF(),
&TextWindow::ScreenOpacity);
- } else if(g->type == Group::LINKED) {
+ }
+
+ if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE ||
+ g->type == Group::Type::REVOLVE || g->type == Group::Type::LINKED ||
+ g->type == Group::Type::HELIX) {
Printf(false, " %Fd%f%LP%s suppress this group's solid model",
&TextWindow::ScreenChangeGroupOption,
g->suppress ? CHECK_TRUE : CHECK_FALSE);
&TextWindow::ScreenChangeGroupOption,
g->visible ? CHECK_TRUE : CHECK_FALSE);
- Group *pg; pg = g->PreviousGroup();
- if(pg && pg->runningMesh.IsEmpty() && g->thisMesh.IsEmpty()) {
+ if(!g->IsForcedToMeshBySource()) {
Printf(false, " %f%Lf%Fd%s force NURBS surfaces to triangle mesh",
&TextWindow::ScreenChangeGroupOption,
g->forceToMesh ? CHECK_TRUE : CHECK_FALSE);
Printf(false, "");
Printf(false, "%Ft requests in group");
- int i, a = 0;
- for(i = 0; i < SK.request.n; i++) {
- Request *r = &(SK.request.elem[i]);
+ int a = 0;
+ for(auto &r : SK.request) {
- if(r->group.v == shown.group.v) {
- std::string s = r->DescriptionString();
+ if(r.group == shown.group) {
+ std::string s = r.DescriptionString();
Printf(false, "%Bp %Fl%Ll%D%f%h%s%E",
- (a & 1) ? 'd' : 'a',
- r->h.v, (&TextWindow::ScreenSelectRequest),
- &(TextWindow::ScreenHoverRequest), s.c_str());
+ (a & 1) ? 'd' : 'a',
+ r.h.v,
+ (&TextWindow::ScreenSelectRequest),
+ &(TextWindow::ScreenHoverRequest),
+ s.c_str());
a++;
}
}
a = 0;
Printf(false, "");
Printf(false, "%Ft constraints in group (%d DOF)", g->solved.dof);
- for(i = 0; i < SK.constraint.n; i++) {
- Constraint *c = &(SK.constraint.elem[i]);
+ for(auto &c : SK.constraint) {
- if(c->group.v == shown.group.v) {
- std::string s = c->DescriptionString();
+ if(c.group == shown.group) {
+ std::string s = c.DescriptionString();
Printf(false, "%Bp %Fl%Ll%D%f%h%s%E %s",
- (a & 1) ? 'd' : 'a',
- c->h.v, (&TextWindow::ScreenSelectConstraint),
- (&TextWindow::ScreenHoverConstraint), s.c_str(),
- c->reference ? "(ref)" : "");
+ (a & 1) ? 'd' : 'a',
+ c.h.v,
+ (&TextWindow::ScreenSelectConstraint),
+ (&TextWindow::ScreenHoverConstraint),
+ s.c_str(),
+ c.reference ? "(ref)" : "");
a++;
}
}
Group *g = SK.GetGroup(SS.TW.shown.group);
g->allowRedundant = true;
- SS.GenerateAll();
+ SS.MarkGroupDirty(SS.TW.shown.group);
- SS.TW.shown.screen = SCREEN_GROUP_INFO;
+ SS.TW.shown.screen = Screen::GROUP_INFO;
SS.TW.Show();
}
-void TextWindow::ShowGroupSolveInfo(void) {
+void TextWindow::ShowGroupSolveInfo() {
Group *g = SK.GetGroup(shown.group);
if(g->IsSolvedOkay()) {
// Go back to the default group info screen
- shown.screen = SCREEN_GROUP_INFO;
+ shown.screen = Screen::GROUP_INFO;
Show();
return;
}
Printf(true, "%FtGROUP %E%s", g->DescriptionString().c_str());
switch(g->solved.how) {
- case System::DIDNT_CONVERGE:
+ case SolveResult::DIDNT_CONVERGE:
Printf(true, "%FxSOLVE FAILED!%Fd unsolvable constraints");
Printf(true, "the following constraints are incompatible");
break;
- case System::REDUNDANT_DIDNT_CONVERGE:
+ case SolveResult::REDUNDANT_DIDNT_CONVERGE:
Printf(true, "%FxSOLVE FAILED!%Fd unsolvable constraints");
Printf(true, "the following constraints are unsatisfied");
break;
- case System::REDUNDANT_OKAY:
+ case SolveResult::REDUNDANT_OKAY:
Printf(true, "%FxSOLVE FAILED!%Fd redundant constraints");
Printf(true, "remove any one of these to fix it");
break;
- case System::TOO_MANY_UNKNOWNS:
+ case SolveResult::TOO_MANY_UNKNOWNS:
Printf(true, "Too many unknowns in a single group!");
return;
+
+ default: ssassert(false, "Unexpected solve result");
}
for(int i = 0; i < g->solved.remove.n; i++) {
- hConstraint hc = g->solved.remove.elem[i];
+ hConstraint hc = g->solved.remove[i];
Constraint *c = SK.constraint.FindByIdNoOops(hc);
if(!c) continue;
c->DescriptionString().c_str());
}
+ if(g->solved.timeout) {
+ Printf(true, "%FxSome items in list have been ommitted%Fd");
+ Printf(false, "%Fxbecause the operation timed out.%Fd");
+ }
+
Printf(true, "It may be possible to fix the problem ");
Printf(false, "by selecting Edit -> Undo.");
- if(g->solved.how == System::REDUNDANT_OKAY) {
+ if(g->solved.how == SolveResult::REDUNDANT_OKAY) {
Printf(true, "It is possible to suppress this error ");
Printf(false, "by %Fl%f%Llallowing redundant constraints%E in ",
&TextWindow::ScreenAllowRedundant);
// time.
//-----------------------------------------------------------------------------
void TextWindow::ScreenStepDimFinish(int link, uint32_t v) {
- SS.TW.edit.meaning = EDIT_STEP_DIM_FINISH;
+ SS.TW.edit.meaning = Edit::STEP_DIM_FINISH;
std::string edit_value;
- if(SS.TW.shown.dimIsDistance) {
- edit_value = SS.MmToString(SS.TW.shown.dimFinish);
+ if(SS.TW.stepDim.isDistance) {
+ edit_value = SS.MmToString(SS.TW.stepDim.finish);
} else {
- edit_value = ssprintf("%.3f", SS.TW.shown.dimFinish);
+ edit_value = ssprintf("%.3f", SS.TW.stepDim.finish);
}
SS.TW.ShowEditControl(12, edit_value);
}
void TextWindow::ScreenStepDimSteps(int link, uint32_t v) {
- SS.TW.edit.meaning = EDIT_STEP_DIM_STEPS;
- SS.TW.ShowEditControl(12, ssprintf("%d", SS.TW.shown.dimSteps));
+ SS.TW.edit.meaning = Edit::STEP_DIM_STEPS;
+ SS.TW.ShowEditControl(12, ssprintf("%d", SS.TW.stepDim.steps));
}
void TextWindow::ScreenStepDimGo(int link, uint32_t v) {
hConstraint hc = SS.TW.shown.constraint;
Constraint *c = SK.constraint.FindByIdNoOops(hc);
if(c) {
SS.UndoRemember();
- double start = c->valA, finish = SS.TW.shown.dimFinish;
- int i, n = SS.TW.shown.dimSteps;
- for(i = 1; i <= n; i++) {
- c = SK.GetConstraint(hc);
- c->valA = start + ((finish - start)*i)/n;
- SS.MarkGroupDirty(c->group);
- SS.GenerateAll();
- if(!SS.ActiveGroupsOkay()) {
- // Failed to solve, so quit
- break;
- }
- PaintGraphics();
+
+ double start = c->valA, finish = SS.TW.stepDim.finish;
+ SS.TW.stepDim.time = GetMilliseconds();
+ SS.TW.stepDim.step = 1;
+
+ if(!SS.TW.stepDim.timer) {
+ SS.TW.stepDim.timer = Platform::CreateTimer();
}
+ SS.TW.stepDim.timer->onTimeout = [=] {
+ if(SS.TW.stepDim.step <= SS.TW.stepDim.steps) {
+ c->valA = start + ((finish - start)*SS.TW.stepDim.step)/SS.TW.stepDim.steps;
+ SS.MarkGroupDirty(c->group);
+ SS.GenerateAll();
+ if(!SS.ActiveGroupsOkay()) {
+ // Failed to solve, so quit
+ return;
+ }
+ SS.TW.stepDim.step++;
+
+ const int64_t STEP_MILLIS = 50;
+ int64_t time = GetMilliseconds();
+ if(time - SS.TW.stepDim.time < STEP_MILLIS) {
+ SS.TW.stepDim.timer->RunAfterNextFrame();
+ } else {
+ SS.TW.stepDim.timer->RunAfter((unsigned)(time - SS.TW.stepDim.time - STEP_MILLIS));
+ }
+ SS.TW.stepDim.time = time;
+ } else {
+ SS.TW.GoToScreen(Screen::LIST_OF_GROUPS);
+ SS.ScheduleShowTW();
+ }
+ SS.GW.Invalidate();
+ };
+ SS.TW.stepDim.timer->RunAfterNextFrame();
}
- InvalidateGraphics();
- SS.TW.GoToScreen(SCREEN_LIST_OF_GROUPS);
}
-void TextWindow::ShowStepDimension(void) {
+void TextWindow::ShowStepDimension() {
Constraint *c = SK.constraint.FindByIdNoOops(shown.constraint);
if(!c) {
- shown.screen = SCREEN_LIST_OF_GROUPS;
+ shown.screen = Screen::LIST_OF_GROUPS;
Show();
return;
}
Printf(true, "%FtSTEP DIMENSION%E %s", c->DescriptionString().c_str());
- if(shown.dimIsDistance) {
+ if(stepDim.isDistance) {
Printf(true, "%Ba %Ftstart%E %s", SS.MmToString(c->valA).c_str());
Printf(false, "%Bd %Ftfinish%E %s %Fl%Ll%f[change]%E",
- SS.MmToString(shown.dimFinish).c_str(), &ScreenStepDimFinish);
+ SS.MmToString(stepDim.finish).c_str(), &ScreenStepDimFinish);
} else {
Printf(true, "%Ba %Ftstart%E %@", c->valA);
Printf(false, "%Bd %Ftfinish%E %@ %Fl%Ll%f[change]%E",
- shown.dimFinish, &ScreenStepDimFinish);
+ stepDim.finish, &ScreenStepDimFinish);
}
Printf(false, "%Ba %Ftsteps%E %d %Fl%Ll%f%D[change]%E",
- shown.dimSteps, &ScreenStepDimSteps);
+ stepDim.steps, &ScreenStepDimSteps);
Printf(true, " %Fl%Ll%fstep dimension now%E", &ScreenStepDimGo);
void TextWindow::ScreenChangeTangentArc(int link, uint32_t v) {
switch(link) {
case 'r': {
- SS.TW.edit.meaning = EDIT_TANGENT_ARC_RADIUS;
+ SS.TW.edit.meaning = Edit::TANGENT_ARC_RADIUS;
SS.TW.ShowEditControl(3, SS.MmToString(SS.tangentArcRadius));
break;
}
case 'a': SS.tangentArcManual = !SS.tangentArcManual; break;
- case 'd': SS.tangentArcDeleteOld = !SS.tangentArcDeleteOld; break;
+ case 'm': SS.tangentArcModify = !SS.tangentArcModify; break;
}
}
-void TextWindow::ShowTangentArc(void) {
+void TextWindow::ShowTangentArc() {
Printf(true, "%FtTANGENT ARC PARAMETERS%E");
Printf(true, "%Ft radius of created arc%E");
Printf(false, " %Fd%f%La%s choose radius automatically%E",
&ScreenChangeTangentArc,
!SS.tangentArcManual ? CHECK_TRUE : CHECK_FALSE);
- Printf(false, " %Fd%f%Ld%s delete original entities afterward%E",
+ Printf(false, " %Fd%f%Lm%s modify original entities%E",
&ScreenChangeTangentArc,
- SS.tangentArcDeleteOld ? CHECK_TRUE : CHECK_FALSE);
+ SS.tangentArcModify ? CHECK_TRUE : CHECK_FALSE);
Printf(false, "");
Printf(false, "To create a tangent arc at a point,");
//-----------------------------------------------------------------------------
// The edit control is visible, and the user just pressed enter.
//-----------------------------------------------------------------------------
-void TextWindow::EditControlDone(const char *s) {
+void TextWindow::EditControlDone(std::string s) {
edit.showAgain = false;
switch(edit.meaning) {
- case EDIT_TIMES_REPEATED: {
- Expr *e = Expr::From(s, true);
- if(e) {
+ case Edit::TIMES_REPEATED:
+ if(Expr *e = Expr::From(s, /*popUpError=*/true)) {
SS.UndoRemember();
double ev = e->Eval();
if((int)ev < 1) {
- Error("Can't repeat fewer than 1 time.");
+ Error(_("Can't repeat fewer than 1 time."));
break;
}
if((int)ev > 999) {
- Error("Can't repeat more than 999 times.");
+ Error(_("Can't repeat more than 999 times."));
break;
}
Group *g = SK.GetGroup(edit.group);
g->valA = ev;
- if(g->type == Group::ROTATE) {
- int i, c = 0;
- for(i = 0; i < SK.constraint.n; i++) {
- if(SK.constraint.elem[i].group.v == g->h.v) c++;
- }
+ if(g->type == Group::Type::ROTATE) {
// If the group does not contain any constraints, then
// set the numerical guess to space the copies uniformly
// over one rotation. Don't touch the guess if we're
// already constrained, because that would break
// convergence.
- if(c == 0) {
+ if(g->GetNumConstraints() == 0) {
double copies = (g->skipFirst) ? (ev + 1) : ev;
SK.GetParam(g->h.param(3))->val = PI/(2*copies);
}
}
SS.MarkGroupDirty(g->h);
- SS.ScheduleGenerateAll();
}
break;
- }
- case EDIT_GROUP_NAME: {
- if(!*s) {
- Error("Group name cannot be empty");
+
+ case Edit::GROUP_NAME:
+ if(s.empty()) {
+ Error(_("Group name cannot be empty"));
} else {
SS.UndoRemember();
g->name = s;
}
break;
- }
- case EDIT_GROUP_SCALE: {
- Expr *e = Expr::From(s, true);
- if(e) {
+
+ case Edit::GROUP_SCALE:
+ if(Expr *e = Expr::From(s, /*popUpError=*/true)) {
double ev = e->Eval();
if(fabs(ev) < 1e-6) {
- Error("Scale cannot be zero.");
+ Error(_("Scale cannot be zero."));
} else {
Group *g = SK.GetGroup(edit.group);
g->scale = ev;
SS.MarkGroupDirty(g->h);
- SS.ScheduleGenerateAll();
}
}
break;
- }
- case EDIT_GROUP_COLOR: {
+
+ case Edit::GROUP_COLOR: {
Vector rgb;
- if(sscanf(s, "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
+ if(sscanf(s.c_str(), "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
rgb = rgb.ClampWithin(0, 1);
Group *g = SK.group.FindByIdNoOops(SS.TW.shown.group);
if(!g) break;
- g->color = RGBf(rgb.x, rgb.y, rgb.z);
+ g->color = RgbaColor::FromFloat((float)rgb.x, (float)rgb.y, (float)rgb.z,
+ g->color.alphaF());
SS.MarkGroupDirty(g->h);
- SS.ScheduleGenerateAll();
SS.GW.ClearSuper();
} else {
- Error("Bad format: specify color as r, g, b");
+ Error(_("Bad format: specify color as r, g, b"));
}
break;
}
- case EDIT_GROUP_OPACITY: {
- Expr *e = Expr::From(s, true);
- if(e) {
+ case Edit::GROUP_OPACITY:
+ if(Expr *e = Expr::From(s, /*popUpError=*/true)) {
double alpha = e->Eval();
if(alpha < 0 || alpha > 1) {
- Error("Opacity must be between zero and one.");
+ Error(_("Opacity must be between zero and one."));
} else {
Group *g = SK.GetGroup(edit.group);
g->color.alpha = (int)(255.1f * alpha);
SS.MarkGroupDirty(g->h);
- SS.ScheduleGenerateAll();
SS.GW.ClearSuper();
}
}
break;
- }
- case EDIT_TTF_TEXT: {
+
+ case Edit::TTF_TEXT:
SS.UndoRemember();
- Request *r = SK.request.FindByIdNoOops(edit.request);
- if(r) {
+ if(Request *r = SK.request.FindByIdNoOops(edit.request)) {
r->str = s;
SS.MarkGroupDirty(r->group);
- SS.ScheduleGenerateAll();
}
break;
- }
- case EDIT_STEP_DIM_FINISH: {
- Expr *e = Expr::From(s, true);
- if(!e) {
- break;
- }
- if(shown.dimIsDistance) {
- shown.dimFinish = SS.ExprToMm(e);
- } else {
- shown.dimFinish = e->Eval();
+
+ case Edit::STEP_DIM_FINISH:
+ if(Expr *e = Expr::From(s, /*popUpError=*/true)) {
+ if(stepDim.isDistance) {
+ stepDim.finish = SS.ExprToMm(e);
+ } else {
+ stepDim.finish = e->Eval();
+ }
}
break;
- }
- case EDIT_STEP_DIM_STEPS:
- shown.dimSteps = min(300, max(1, atoi(s)));
+
+ case Edit::STEP_DIM_STEPS:
+ stepDim.steps = min(300, max(1, atoi(s.c_str())));
break;
- case EDIT_TANGENT_ARC_RADIUS: {
- Expr *e = Expr::From(s, true);
- if(!e) break;
- if(e->Eval() < LENGTH_EPS) {
- Error("Radius cannot be zero or negative.");
- break;
+ case Edit::TANGENT_ARC_RADIUS:
+ if(Expr *e = Expr::From(s, /*popUpError=*/true)) {
+ if(e->Eval() < LENGTH_EPS) {
+ Error(_("Radius cannot be zero or negative."));
+ break;
+ }
+ SS.tangentArcRadius = SS.ExprToMm(e);
}
- SS.tangentArcRadius = SS.ExprToMm(e);
break;
- }
default: {
int cnt = 0;
if(EditControlDoneForConfiguration(s)) cnt++;
if(EditControlDoneForPaste(s)) cnt++;
if(EditControlDoneForView(s)) cnt++;
- if(cnt > 1) {
- // The identifiers were somehow assigned not uniquely?
- oops();
- }
+ ssassert(cnt == 1, "Expected exactly one parameter to be edited");
break;
}
}
- InvalidateGraphics();
+ SS.GW.Invalidate();
SS.ScheduleShowTW();
if(!edit.showAgain) {
HideEditControl();
- edit.meaning = EDIT_NOTHING;
+ edit.meaning = Edit::NOTHING;
}
}
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
#include "solvespace.h"
-#include "generated/icons.h"
+namespace SolveSpace {
+
+class Button {
+public:
+ virtual std::string Tooltip() = 0;
+ virtual void Draw(UiCanvas *uiCanvas, int x, int y, bool asHovered) = 0;
+ virtual int AdvanceWidth() = 0;
+ virtual void Click() = 0;
+};
+
+class SpacerButton : public Button {
+public:
+ std::string Tooltip() override { return ""; }
+
+ void Draw(UiCanvas *uiCanvas, int x, int y, bool asHovered) override {
+ // Draw a darker-grey spacer in between the groups of icons.
+ uiCanvas->DrawRect(x, x + 4, y, y - 24,
+ /*fillColor=*/{ 45, 45, 45, 255 },
+ /*outlineColor=*/{});
+ }
+
+ int AdvanceWidth() override { return 12; }
+
+ void Click() override {}
+};
+
+class ShowHideButton : public Button {
+public:
+ bool *variable;
+ std::string tooltip;
+ std::string iconName;
+ std::shared_ptr<Pixmap> icon;
+
+ ShowHideButton(bool *variable, std::string iconName, std::string tooltip)
+ : variable(variable), tooltip(tooltip), iconName(iconName) {}
+
+ std::string Tooltip() override {
+ return ((*variable) ? "Hide " : "Show ") + tooltip;
+ }
+
+ void Draw(UiCanvas *uiCanvas, int x, int y, bool asHovered) override {
+ if(icon == NULL) {
+ icon = LoadPng("icons/text-window/" + iconName + ".png");
+ }
+
+ uiCanvas->DrawPixmap(icon, x, y - 24);
+ if(asHovered) {
+ uiCanvas->DrawRect(x - 2, x + 26, y + 2, y - 26,
+ /*fillColor=*/{ 255, 255, 0, 75 },
+ /*outlineColor=*/{});
+ }
+ if(!*(variable)) {
+ int s = 0, f = 24;
+ RgbaColor color = { 255, 0, 0, 150 };
+ uiCanvas->DrawLine(x+s, y-s, x+f, y-f, color, 2);
+ uiCanvas->DrawLine(x+s, y-f, x+f, y-s, color, 2);
+ }
+ }
+
+ int AdvanceWidth() override { return 32; }
+
+ void Click() override { SS.GW.ToggleBool(variable); }
+};
+
+class FacesButton : public ShowHideButton {
+public:
+ FacesButton()
+ : ShowHideButton(&(SS.GW.showFaces), "faces", "") {}
+
+ std::string Tooltip() override {
+ if(*variable) {
+ return "Don't make faces selectable with mouse";
+ } else {
+ return "Make faces selectable with mouse";
+ }
+ }
+};
+
+class OccludedLinesButton : public Button {
+public:
+ std::shared_ptr<Pixmap> visibleIcon;
+ std::shared_ptr<Pixmap> stippledIcon;
+ std::shared_ptr<Pixmap> invisibleIcon;
+
+ std::string Tooltip() override {
+ switch(SS.GW.drawOccludedAs) {
+ case GraphicsWindow::DrawOccludedAs::INVISIBLE:
+ return "Stipple occluded lines";
+
+ case GraphicsWindow::DrawOccludedAs::STIPPLED:
+ return "Draw occluded lines";
+
+ case GraphicsWindow::DrawOccludedAs::VISIBLE:
+ return "Don't draw occluded lines";
+
+ default: ssassert(false, "Unexpected mode");
+ }
+ }
+
+ void Draw(UiCanvas *uiCanvas, int x, int y, bool asHovered) override {
+ if(visibleIcon == NULL) {
+ visibleIcon = LoadPng("icons/text-window/occluded-visible.png");
+ }
+ if(stippledIcon == NULL) {
+ stippledIcon = LoadPng("icons/text-window/occluded-stippled.png");
+ }
+ if(invisibleIcon == NULL) {
+ invisibleIcon = LoadPng("icons/text-window/occluded-invisible.png");
+ }
+
+ std::shared_ptr<Pixmap> icon;
+ switch(SS.GW.drawOccludedAs) {
+ case GraphicsWindow::DrawOccludedAs::INVISIBLE: icon = invisibleIcon; break;
+ case GraphicsWindow::DrawOccludedAs::STIPPLED: icon = stippledIcon; break;
+ case GraphicsWindow::DrawOccludedAs::VISIBLE: icon = visibleIcon; break;
+ }
+
+ uiCanvas->DrawPixmap(icon, x, y - 24);
+ if(asHovered) {
+ uiCanvas->DrawRect(x - 2, x + 26, y + 2, y - 26,
+ /*fillColor=*/{ 255, 255, 0, 75 },
+ /*outlineColor=*/{});
+ }
+ }
+
+ int AdvanceWidth() override { return 32; }
+
+ void Click() override {
+ switch(SS.GW.drawOccludedAs) {
+ case GraphicsWindow::DrawOccludedAs::INVISIBLE:
+ SS.GW.drawOccludedAs = GraphicsWindow::DrawOccludedAs::STIPPLED;
+ break;
+
+ case GraphicsWindow::DrawOccludedAs::STIPPLED:
+ SS.GW.drawOccludedAs = GraphicsWindow::DrawOccludedAs::VISIBLE;
+ break;
+
+ case GraphicsWindow::DrawOccludedAs::VISIBLE:
+ SS.GW.drawOccludedAs = GraphicsWindow::DrawOccludedAs::INVISIBLE;
+ break;
+ }
+
+ SS.GenerateAll();
+ SS.GW.Invalidate();
+ SS.ScheduleShowTW();
+ }
+};
+
+static SpacerButton spacerButton;
+
+static ShowHideButton workplanesButton =
+ { &(SS.GW.showWorkplanes), "workplane", "workplanes from inactive groups" };
+static ShowHideButton normalsButton =
+ { &(SS.GW.showNormals), "normal", "normals" };
+static ShowHideButton pointsButton =
+ { &(SS.GW.showPoints), "point", "points" };
+static ShowHideButton constructionButton =
+ { &(SS.GW.showConstruction), "construction", "construction entities" };
+static ShowHideButton constraintsButton =
+ { &(SS.GW.showConstraints), "constraint", "constraints and dimensions" };
+static FacesButton facesButton;
+static ShowHideButton shadedButton =
+ { &(SS.GW.showShaded), "shaded", "shaded view of solid model" };
+static ShowHideButton edgesButton =
+ { &(SS.GW.showEdges), "edges", "edges of solid model" };
+static ShowHideButton outlinesButton =
+ { &(SS.GW.showOutlines), "outlines", "outline of solid model" };
+static ShowHideButton meshButton =
+ { &(SS.GW.showMesh), "mesh", "triangle mesh of solid model" };
+static OccludedLinesButton occludedLinesButton;
+
+static Button *buttons[] = {
+ &workplanesButton,
+ &normalsButton,
+ &pointsButton,
+ &constructionButton,
+ &constraintsButton,
+ &facesButton,
+ &spacerButton,
+ &shadedButton,
+ &edgesButton,
+ &outlinesButton,
+ &meshButton,
+ &spacerButton,
+ &occludedLinesButton,
+};
+
+/** Foreground color codes. */
const TextWindow::Color TextWindow::fgColors[] = {
- { 'd', RGBi(255, 255, 255) },
- { 'l', RGBi(100, 100, 255) },
- { 't', RGBi(255, 200, 0) },
+ { 'd', RGBi(255, 255, 255) }, // Default : white
+ { 'l', RGBi(100, 200, 255) }, // links : blue
+ { 't', RGBi(255, 200, 100) }, // tree/text : yellow
{ 'h', RGBi( 90, 90, 90) },
- { 's', RGBi( 40, 255, 40) },
+ { 's', RGBi( 40, 255, 40) }, // Ok : green
{ 'm', RGBi(200, 200, 0) },
- { 'r', RGBi( 0, 0, 0) },
- { 'x', RGBi(255, 20, 20) },
- { 'i', RGBi( 0, 255, 255) },
+ { 'r', RGBi( 0, 0, 0) }, // Reverse : black
+ { 'x', RGBi(255, 20, 20) }, // Error : red
+ { 'i', RGBi( 0, 255, 255) }, // Info : cyan
{ 'g', RGBi(160, 160, 160) },
{ 'b', RGBi(200, 200, 200) },
{ 0, RGBi( 0, 0, 0) }
};
+/** Background color codes. */
const TextWindow::Color TextWindow::bgColors[] = {
- { 'd', RGBi( 0, 0, 0) },
+ { 'd', RGBi( 0, 0, 0) }, // Default : black
{ 't', RGBi( 34, 15, 15) },
- { 'a', RGBi( 25, 25, 25) },
- { 'r', RGBi(255, 255, 255) },
+ { 'a', RGBi( 25, 25, 25) }, // Alternate : dark gray
+ { 'r', RGBi(255, 255, 255) }, // Reverse : white
{ 0, RGBi( 0, 0, 0) }
};
-bool TextWindow::SPACER = false;
-TextWindow::HideShowIcon TextWindow::hideShowIcons[] = {
- { &(SS.GW.showWorkplanes), Icon_workplane, "workplanes from inactive groups"},
- { &(SS.GW.showNormals), Icon_normal, "normals" },
- { &(SS.GW.showPoints), Icon_point, "points" },
- { &(SS.GW.showConstraints), Icon_constraint, "constraints and dimensions" },
- { &(SS.GW.showFaces), Icon_faces, "XXX - special cased" },
- { &SPACER, 0, 0 },
- { &(SS.GW.showShaded), Icon_shaded, "shaded view of solid model" },
- { &(SS.GW.showEdges), Icon_edges, "edges of solid model" },
- { &(SS.GW.showOutlines), Icon_outlines, "outline of solid model" },
- { &(SS.GW.showMesh), Icon_mesh, "triangle mesh of solid model" },
- { &SPACER, 0, 0 },
- { &(SS.GW.showHdnLines), Icon_hidden_lines, "hidden lines" },
- { 0, 0, 0 }
-};
-
void TextWindow::MakeColorTable(const Color *in, float *out) {
int i;
for(i = 0; in[i].c != 0; i++) {
int c = in[i].c;
- if(c < 0 || c > 255) oops();
+ ssassert(c >= 0 && c <= 255, "Unexpected color index");
out[c*3 + 0] = in[i].color.redF();
out[c*3 + 1] = in[i].color.greenF();
out[c*3 + 2] = in[i].color.blueF();
}
}
-void TextWindow::Init(void) {
+void TextWindow::Init() {
+ if(!window) {
+ window = Platform::CreateWindow(Platform::Window::Kind::TOOL, SS.GW.window);
+ if(window) {
+ canvas = CreateRenderer();
+
+ using namespace std::placeholders;
+ window->onClose = []() {
+ SS.GW.showTextWindow = false;
+ SS.GW.EnsureValidActives();
+ };
+ window->onMouseEvent = [this](Platform::MouseEvent event) {
+ using Platform::MouseEvent;
+
+ if(event.type == MouseEvent::Type::PRESS ||
+ event.type == MouseEvent::Type::DBL_PRESS ||
+ event.type == MouseEvent::Type::MOTION) {
+ bool isClick = (event.type != MouseEvent::Type::MOTION);
+ bool leftDown = (event.button == MouseEvent::Button::LEFT);
+ this->MouseEvent(isClick, leftDown, event.x, event.y);
+ return true;
+ } else if(event.type == MouseEvent::Type::LEAVE) {
+ MouseLeave();
+ return true;
+ } else if(event.type == MouseEvent::Type::SCROLL_VERT) {
+ ScrollbarEvent(window->GetScrollbarPosition() -
+ LINE_HEIGHT / 2 * event.scrollDelta);
+ }
+ return false;
+ };
+ window->onKeyboardEvent = SS.GW.window->onKeyboardEvent;
+ window->onRender = std::bind(&TextWindow::Paint, this);
+ window->onEditingDone = std::bind(&TextWindow::EditControlDone, this, _1);
+ window->onScrollbarAdjusted = std::bind(&TextWindow::ScrollbarEvent, this, _1);
+ window->SetMinContentSize(370, 370);
+ }
+ }
+
ClearSuper();
}
-void TextWindow::ClearSuper(void) {
- HideEditControl();
+void TextWindow::ClearSuper() {
+ // Ugly hack, but not so ugly as the next line
+ Platform::WindowRef oldWindow = std::move(window);
+ std::shared_ptr<ViewportCanvas> oldCanvas = canvas;
// Cannot use *this = {} here because TextWindow instances
// are 2.4MB long; this causes stack overflows in prologue
// when built with MSVC, even with optimizations.
memset(this, 0, sizeof(*this));
+ // Return old canvas
+ window = std::move(oldWindow);
+ canvas = oldCanvas;
+
+ HideEditControl();
+
MakeColorTable(fgColors, fgColorTable);
MakeColorTable(bgColors, bgColorTable);
Show();
}
-void TextWindow::HideEditControl(void) {
+void TextWindow::HideEditControl() {
editControl.colorPicker.show = false;
- HideTextEditControl();
+ if(window) {
+ window->HideEditor();
+ window->Invalidate();
+ }
}
void TextWindow::ShowEditControl(int col, const std::string &str, int halfRow) {
editControl.halfRow = halfRow;
editControl.col = col;
- int x = LEFT_MARGIN + CHAR_WIDTH*col;
+ int x = LEFT_MARGIN + CHAR_WIDTH_*col;
int y = (halfRow - SS.TW.scrollPos)*(LINE_HEIGHT/2);
- ShowTextEditControl(x, y + 18, str);
+ double width, height;
+ window->GetContentSize(&width, &height);
+ window->ShowEditor(x, y + LINE_HEIGHT - 2, LINE_HEIGHT - 4,
+ width - x, /*isMonospace=*/true, str);
}
-void TextWindow::ShowEditControlWithColorPicker(int col, RgbaColor rgb)
-{
+void TextWindow::ShowEditControlWithColorPicker(int col, RgbaColor rgb) {
SS.ScheduleShowTW();
editControl.colorPicker.show = true;
ShowEditControl(col, ssprintf("%.2f, %.2f, %.2f", rgb.redF(), rgb.greenF(), rgb.blueF()));
}
-void TextWindow::ClearScreen(void) {
+void TextWindow::ClearScreen() {
int i, j;
for(i = 0; i < MAX_ROWS; i++) {
for(j = 0; j < MAX_COLS; j++) {
rows = 0;
}
+// This message was addded when someone had too many fonts for the text window
+// Scrolling seemed to be broken, but was actaully at the MAX_ROWS.
+static const char* endString = " **** End of Text Screen ****";
+
void TextWindow::Printf(bool halfLine, const char *fmt, ...) {
- va_list vl;
- va_start(vl, fmt);
+ if(!canvas) return;
if(rows >= MAX_ROWS) return;
+ if(rows >= MAX_ROWS-2 && (fmt != endString)) {
+ // twice due to some half-row issues on resizing
+ Printf(halfLine, endString);
+ Printf(halfLine, endString);
+ return;
+ }
+
+ va_list vl;
+ va_start(vl, fmt);
+
int r, c;
r = rows;
top[r] = (r == 0) ? 0 : (top[r-1] + (halfLine ? 3 : 2));
}
for(utf8_iterator it(buf); *it; ++it) {
- for(int i = 0; i < ssglBitmapCharWidth(*it); i++) {
+ for(size_t i = 0; i < canvas->GetBitmapFont()->GetWidth(*it); i++) {
if(c >= MAX_COLS) goto done;
text[r][c] = (i == 0) ? *it : ' ';
meta[r][c].fg = fg;
}
}
- fmt++;
+ utf8_iterator it(fmt);
+ it++;
+ fmt = it.ptr();
}
while(c < MAX_COLS) {
meta[r][c].fg = fg;
va_end(vl);
}
-#define gs (SS.GW.gs)
-void TextWindow::Show(void) {
- if(!(SS.GW.pending.operation)) SS.GW.ClearPending();
+void TextWindow::Show() {
+ if(SS.GW.pending.operation == GraphicsWindow::Pending::NONE) {
+ SS.GW.ClearPending(/*scheduleShowTW=*/false);
+ }
SS.GW.GroupSelection();
+ auto const &gs = SS.GW.gs;
// Make sure these tests agree with test used to draw indicator line on
// main list of groups screen.
Printf(true, "%Fl%f%Ll(cancel operation)%E",
&TextWindow::ScreenUnselectAll);
} else if((gs.n > 0 || gs.constraints > 0) &&
- shown.screen != SCREEN_PASTE_TRANSFORMED)
+ shown.screen != Screen::PASTE_TRANSFORMED)
{
- if(edit.meaning != EDIT_TTF_TEXT) HideEditControl();
+ if(edit.meaning != Edit::TTF_TEXT) HideEditControl();
ShowHeader(false);
DescribeSelection();
} else {
- if(edit.meaning == EDIT_TTF_TEXT) HideEditControl();
+ if(edit.meaning == Edit::TTF_TEXT) HideEditControl();
ShowHeader(true);
switch(shown.screen) {
default:
- shown.screen = SCREEN_LIST_OF_GROUPS;
+ shown.screen = Screen::LIST_OF_GROUPS;
// fall through
- case SCREEN_LIST_OF_GROUPS: ShowListOfGroups(); break;
- case SCREEN_GROUP_INFO: ShowGroupInfo(); break;
- case SCREEN_GROUP_SOLVE_INFO: ShowGroupSolveInfo(); break;
- case SCREEN_CONFIGURATION: ShowConfiguration(); break;
- case SCREEN_STEP_DIMENSION: ShowStepDimension(); break;
- case SCREEN_LIST_OF_STYLES: ShowListOfStyles(); break;
- case SCREEN_STYLE_INFO: ShowStyleInfo(); break;
- case SCREEN_PASTE_TRANSFORMED: ShowPasteTransformed(); break;
- case SCREEN_EDIT_VIEW: ShowEditView(); break;
- case SCREEN_TANGENT_ARC: ShowTangentArc(); break;
+ case Screen::LIST_OF_GROUPS: ShowListOfGroups(); break;
+ case Screen::GROUP_INFO: ShowGroupInfo(); break;
+ case Screen::GROUP_SOLVE_INFO: ShowGroupSolveInfo(); break;
+ case Screen::CONFIGURATION: ShowConfiguration(); break;
+ case Screen::STEP_DIMENSION: ShowStepDimension(); break;
+ case Screen::LIST_OF_STYLES: ShowListOfStyles(); break;
+ case Screen::STYLE_INFO: ShowStyleInfo(); break;
+ case Screen::PASTE_TRANSFORMED: ShowPasteTransformed(); break;
+ case Screen::EDIT_VIEW: ShowEditView(); break;
+ case Screen::TANGENT_ARC: ShowTangentArc(); break;
}
}
Printf(false, "");
}
}
- InvalidateText();
+ if(window) Resize();
}
-void TextWindow::TimerCallback(void)
+void TextWindow::Resize()
{
- tooltippedIcon = hoveredIcon;
- InvalidateText();
+ double width, height;
+ window->GetContentSize(&width, &height);
+
+ halfRows = (int)height / (LINE_HEIGHT/2);
+
+ int bottom = top[rows-1] + 2;
+ scrollPos = min(scrollPos, bottom - halfRows);
+ scrollPos = max(scrollPos, 0);
+
+ window->ConfigureScrollbar(0, top[rows - 1] + 1, halfRows);
+ window->SetScrollbarPosition(scrollPos);
+ window->SetScrollbarVisible(top[rows - 1] + 1 > halfRows);
+ window->Invalidate();
}
-void TextWindow::DrawOrHitTestIcons(int how, double mx, double my)
+void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow how,
+ double mx, double my)
{
- int width, height;
- GetTextWindowSize(&width, &height);
+ double width, height;
+ window->GetContentSize(&width, &height);
int x = 20, y = 33 + LINE_HEIGHT;
y -= scrollPos*(LINE_HEIGHT/2);
if(how == PAINT) {
- double grey = 30.0/255;
- double top = y - 28, bot = y + 4;
- glColor4d(grey, grey, grey, 1.0);
- ssglAxisAlignedQuad(0, width, top, bot);
+ int top = y - 28, bot = y + 4;
+ uiCanvas->DrawRect(0, (int)width, top, bot,
+ /*fillColor=*/{ 30, 30, 30, 255 }, /*outlineColor=*/{});
}
- HideShowIcon *oldHovered = hoveredIcon;
+ Button *oldHovered = hoveredButton;
if(how != PAINT) {
- hoveredIcon = NULL;
+ hoveredButton = NULL;
}
- HideShowIcon *hsi;
- for(hsi = &(hideShowIcons[0]); hsi->var; hsi++) {
- if(hsi->var == &SPACER) {
- // Draw a darker-grey spacer in between the groups of icons.
- if(how == PAINT) {
- int l = x, r = l + 4,
- t = y, b = t - 24;
- glColor4d(0.17, 0.17, 0.17, 1);
- ssglAxisAlignedQuad(l, r, t, b);
- }
- x += 12;
- continue;
- }
-
+ double hoveredX, hoveredY;
+ for(Button *button : buttons) {
if(how == PAINT) {
- glPushMatrix();
- glTranslated(x, y-24, 0);
- // Only thing that matters about the color is the alpha,
- // should be one for no transparency
- glColor3d(0, 0, 0);
- ssglDrawPixelsWithTexture(hsi->icon, 24, 24);
- glPopMatrix();
-
- if(hsi == hoveredIcon) {
- glColor4d(1, 1, 0, 0.3);
- ssglAxisAlignedQuad(x - 2, x + 26, y + 2, y - 26);
- }
- if(!*(hsi->var)) {
- glColor4d(1, 0, 0, 0.6);
- glLineWidth(2);
- int s = 0, f = 24;
- glBegin(GL_LINES);
- glVertex2d(x+s, y-s);
- glVertex2d(x+f, y-f);
- glVertex2d(x+s, y-f);
- glVertex2d(x+f, y-s);
- glEnd();
- }
- } else {
- if(mx > x - 2 && mx < x + 26 &&
- my < y + 2 && my > y - 26)
- {
- // The mouse is hovered over this icon, so do the tooltip
- // stuff.
- if(hsi != tooltippedIcon) {
- oldMousePos = Point2d::From(mx, my);
- }
- if(hsi != oldHovered || how == CLICK) {
- SetTimerFor(1000);
- }
- hoveredIcon = hsi;
- if(how == CLICK) {
- SS.GW.ToggleBool(hsi->var);
- }
+ button->Draw(uiCanvas, x, y, (button == hoveredButton));
+ } else if(mx > x - 2 && mx < x + 26 &&
+ my < y + 2 && my > y - 26) {
+ hoveredButton = button;
+ hoveredX = x - 2;
+ hoveredY = y - 26;
+ if(how == CLICK) {
+ button->Click();
}
}
- x += 32;
+ x += button->AdvanceWidth();
}
- if(how != PAINT && hoveredIcon != oldHovered) {
- InvalidateText();
- }
-
- if(tooltippedIcon) {
- if(how == PAINT) {
- std::string str;
-
- if(tooltippedIcon->icon == Icon_faces) {
- if(SS.GW.showFaces) {
- str = "Don't make faces selectable with mouse";
- } else {
- str = "Make faces selectable with mouse";
- }
- } else {
- str = ssprintf("%s %s", *(tooltippedIcon->var) ? "Hide" : "Show",
- tooltippedIcon->tip);
- }
-
- double ox = oldMousePos.x, oy = oldMousePos.y - LINE_HEIGHT;
- ox += 3;
- oy -= 3;
- int tw = (str.length() + 1)*(CHAR_WIDTH - 1);
- ox = min(ox, (double) (width - 25) - tw);
- oy = max(oy, 5.0);
-
- ssglInitializeBitmapFont();
- glLineWidth(1);
- glColor4d(1.0, 1.0, 0.6, 1.0);
- ssglAxisAlignedQuad(ox, ox+tw, oy, oy+LINE_HEIGHT);
- glColor4d(0.0, 0.0, 0.0, 1.0);
- ssglAxisAlignedLineLoop(ox, ox+tw, oy, oy+LINE_HEIGHT);
-
- glColor4d(0, 0, 0, 1);
- ssglBitmapText(str, Vector::From(ox+5, oy-3+LINE_HEIGHT, 0));
+ if(how != PAINT && hoveredButton != oldHovered) {
+ if(hoveredButton == NULL) {
+ window->SetTooltip("", 0, 0, 0, 0);
} else {
- if(!hoveredIcon ||
- (hoveredIcon != tooltippedIcon))
- {
- tooltippedIcon = NULL;
- InvalidateGraphics();
- }
- // And if we're hovered, then we've set a timer that will cause
- // us to show the tool tip later.
+ window->SetTooltip(hoveredButton->Tooltip(), hoveredX, hoveredY, 28, 28);
}
+ window->Invalidate();
}
}
return rgb;
}
-uint8_t *TextWindow::HsvPattern2d(void) {
- static uint8_t Texture[256*256*3];
- static bool Init;
-
- if(!Init) {
- int i, j, p;
- p = 0;
- for(i = 0; i < 256; i++) {
- for(j = 0; j < 256; j++) {
- Vector hsv = Vector::From(6.0*i/255.0, 1.0*j/255.0, 1);
- Vector rgb = HsvToRgb(hsv);
- rgb = rgb.ScaledBy(255);
- Texture[p++] = (uint8_t)rgb.x;
- Texture[p++] = (uint8_t)rgb.y;
- Texture[p++] = (uint8_t)rgb.z;
- }
+std::shared_ptr<Pixmap> TextWindow::HsvPattern2d(int w, int h) {
+ std::shared_ptr<Pixmap> pixmap = Pixmap::Create(Pixmap::Format::RGB, w, h);
+ for(size_t j = 0; j < pixmap->height; j++) {
+ size_t p = pixmap->stride * j;
+ for(size_t i = 0; i < pixmap->width; i++) {
+ Vector hsv = Vector::From(6.0*i/(pixmap->width-1), 1.0*j/(pixmap->height-1), 1);
+ Vector rgb = HsvToRgb(hsv);
+ rgb = rgb.ScaledBy(255);
+ pixmap->data[p++] = (uint8_t)rgb.x;
+ pixmap->data[p++] = (uint8_t)rgb.y;
+ pixmap->data[p++] = (uint8_t)rgb.z;
}
- Init = true;
}
- return Texture;
+ return pixmap;
}
-uint8_t *TextWindow::HsvPattern1d(double h, double s) {
- static uint8_t Texture[256*4];
-
- int i, p;
- p = 0;
- for(i = 0; i < 256; i++) {
- Vector hsv = Vector::From(6*h, s, 1.0*(255 - i)/255.0);
- Vector rgb = HsvToRgb(hsv);
- rgb = rgb.ScaledBy(255);
- Texture[p++] = (uint8_t)rgb.x;
- Texture[p++] = (uint8_t)rgb.y;
- Texture[p++] = (uint8_t)rgb.z;
- // Needs a padding byte, to make things four-aligned
- p++;
+std::shared_ptr<Pixmap> TextWindow::HsvPattern1d(double hue, double sat, int w, int h) {
+ std::shared_ptr<Pixmap> pixmap = Pixmap::Create(Pixmap::Format::RGB, w, h);
+ for(size_t i = 0; i < pixmap->height; i++) {
+ size_t p = i * pixmap->stride;
+ for(size_t j = 0; j < pixmap->width; j++) {
+ Vector hsv = Vector::From(6*hue, sat, 1.0*(pixmap->width - 1 - j)/pixmap->width);
+ Vector rgb = HsvToRgb(hsv);
+ rgb = rgb.ScaledBy(255);
+ pixmap->data[p++] = (uint8_t)rgb.x;
+ pixmap->data[p++] = (uint8_t)rgb.y;
+ pixmap->data[p++] = (uint8_t)rgb.z;
+ }
}
- return Texture;
+ return pixmap;
}
-void TextWindow::ColorPickerDone(void) {
+void TextWindow::ColorPickerDone() {
RgbaColor rgb = editControl.colorPicker.rgb;
- EditControlDone(ssprintf("%.2f, %.2f, %.3f", rgb.redF(), rgb.greenF(), rgb.blueF()).c_str());
+ EditControlDone(ssprintf("%.2f, %.2f, %.3f", rgb.redF(), rgb.greenF(), rgb.blueF()));
}
-bool TextWindow::DrawOrHitTestColorPicker(int how, bool leftDown,
+bool TextWindow::DrawOrHitTestColorPicker(UiCanvas *uiCanvas, DrawOrHitHow how, bool leftDown,
double x, double y)
{
+ using Platform::Window;
+
bool mousePointerAsHand = false;
if(how == HOVER && !leftDown) {
}
if(!editControl.colorPicker.show) return false;
- if(how == CLICK || (how == HOVER && leftDown)) InvalidateText();
+ if(how == CLICK || (how == HOVER && leftDown)) window->Invalidate();
static const RgbaColor BaseColor[12] = {
RGBi(255, 0, 0),
RGBi( 0, 127, 255),
};
- int width, height;
- GetTextWindowSize(&width, &height);
+ double width, height;
+ window->GetContentSize(&width, &height);
- int px = LEFT_MARGIN + CHAR_WIDTH*editControl.col;
+ int px = LEFT_MARGIN + CHAR_WIDTH_*editControl.col;
int py = (editControl.halfRow - SS.TW.scrollPos)*(LINE_HEIGHT/2);
py += LINE_HEIGHT + 5;
static const int WIDTH = 16, HEIGHT = 12;
static const int PITCH = 18, SIZE = 15;
- px = min(px, width - (WIDTH*PITCH + 40));
+ px = min(px, (int)width - (WIDTH*PITCH + 40));
int pxm = px + WIDTH*PITCH + 11,
pym = py + HEIGHT*PITCH + 7;
int bw = 6;
if(how == PAINT) {
- glColor4d(0.2, 0.2, 0.2, 1);
- ssglAxisAlignedQuad(px, pxm+bw, py, pym+bw);
- glColor4d(0.0, 0.0, 0.0, 1);
- ssglAxisAlignedQuad(px+(bw/2), pxm+(bw/2), py+(bw/2), pym+(bw/2));
+ uiCanvas->DrawRect(px, pxm+bw, py, pym+bw,
+ /*fillColor=*/{ 50, 50, 50, 255 },
+ /*outlineColor=*/{},
+ /*zIndex=*/1);
+ uiCanvas->DrawRect(px+(bw/2), pxm+(bw/2), py+(bw/2), pym+(bw/2),
+ /*fillColor=*/{ 0, 0, 0, 255 },
+ /*outlineColor=*/{},
+ /*zIndex=*/1);
} else {
if(x < px || x > pxm+(bw/2) ||
y < py || y > pym+(bw/2))
int sx = px + 5 + PITCH*(i + 8) + 4, sy = py + 5 + PITCH*j;
if(how == PAINT) {
- glColor4d(CO(rgb), 1);
- ssglAxisAlignedQuad(sx, sx+SIZE, sy, sy+SIZE);
+ uiCanvas->DrawRect(sx, sx+SIZE, sy, sy+SIZE,
+ /*fillColor=*/RGBf(rgb.x, rgb.y, rgb.z),
+ /*outlineColor=*/{},
+ /*zIndex=*/2);
} else if(how == CLICK) {
if(x >= sx && x <= sx+SIZE && y >= sy && y <= sy+SIZE) {
editControl.colorPicker.rgb = RGBf(rgb.x, rgb.y, rgb.z);
hxm = hx + PITCH*7 + SIZE;
hym = hy + PITCH*2 + SIZE;
if(how == PAINT) {
- ssglColorRGB(editControl.colorPicker.rgb);
- ssglAxisAlignedQuad(hx, hxm, hy, hym);
+ uiCanvas->DrawRect(hx, hxm, hy, hym,
+ /*fillColor=*/editControl.colorPicker.rgb,
+ /*outlineColor=*/{},
+ /*zIndex=*/2);
} else if(how == CLICK) {
if(x >= hx && x <= hxm && y >= hy && y <= hym) {
ColorPickerDone();
hym = hy + PITCH*1 + SIZE;
// The one-dimensional thing to pick the color's value
if(how == PAINT) {
- glBindTexture(GL_TEXTURE_2D, TEXTURE_COLOR_PICKER_1D);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 256, 0,
- GL_RGB, GL_UNSIGNED_BYTE,
- HsvPattern1d(editControl.colorPicker.h,
- editControl.colorPicker.s));
-
- glEnable(GL_TEXTURE_2D);
- glBegin(GL_QUADS);
- glTexCoord2d(0, 0);
- glVertex2d(hx, hy);
-
- glTexCoord2d(1, 0);
- glVertex2d(hx, hym);
-
- glTexCoord2d(1, 1);
- glVertex2d(hxm, hym);
-
- glTexCoord2d(0, 1);
- glVertex2d(hxm, hy);
- glEnd();
- glDisable(GL_TEXTURE_2D);
-
- double cx = hx+(hxm-hx)*(1 - editControl.colorPicker.v);
- glColor4d(0, 0, 0, 1);
- glLineWidth(1);
- glBegin(GL_LINES);
- glVertex2d(cx, hy);
- glVertex2d(cx, hym);
- glEnd();
+ uiCanvas->DrawPixmap(HsvPattern1d(editControl.colorPicker.h,
+ editControl.colorPicker.s,
+ hxm-hx, hym-hy),
+ hx, hy, /*zIndex=*/2);
+
+ int cx = hx+(int)((hxm-hx)*(1.0 - editControl.colorPicker.v));
+ uiCanvas->DrawLine(cx, hy, cx, hym,
+ /*fillColor=*/{ 0, 0, 0, 255 },
+ /*outlineColor=*/{},
+ /*zIndex=*/3);
} else if(how == CLICK ||
(how == HOVER && leftDown && editControl.colorPicker.picker1dActive))
{
hym = hy + PITCH*6 + SIZE;
// Two-dimensional thing to pick a color by hue and saturation
if(how == PAINT) {
- glBindTexture(GL_TEXTURE_2D, TEXTURE_COLOR_PICKER_2D);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0,
- GL_RGB, GL_UNSIGNED_BYTE, HsvPattern2d());
-
- glEnable(GL_TEXTURE_2D);
- glBegin(GL_QUADS);
- glTexCoord2d(0, 0);
- glVertex2d(hx, hy);
-
- glTexCoord2d(1, 0);
- glVertex2d(hx, hym);
-
- glTexCoord2d(1, 1);
- glVertex2d(hxm, hym);
-
- glTexCoord2d(0, 1);
- glVertex2d(hxm, hy);
- glEnd();
- glDisable(GL_TEXTURE_2D);
-
- glColor4d(1, 1, 1, 1);
- glLineWidth(1);
- double cx = hx+(hxm-hx)*editControl.colorPicker.h,
- cy = hy+(hym-hy)*editControl.colorPicker.s;
- glBegin(GL_LINES);
- glVertex2d(cx - 5, cy);
- glVertex2d(cx + 4, cy);
- glVertex2d(cx, cy - 5);
- glVertex2d(cx, cy + 4);
- glEnd();
+ uiCanvas->DrawPixmap(HsvPattern2d(hxm-hx, hym-hy), hx, hy,
+ /*zIndex=*/2);
+
+ int cx = hx+(int)((hxm-hx)*editControl.colorPicker.h),
+ cy = hy+(int)((hym-hy)*editControl.colorPicker.s);
+ uiCanvas->DrawLine(cx - 5, cy, cx + 5, cy,
+ /*fillColor=*/{ 255, 255, 255, 255 },
+ /*outlineColor=*/{},
+ /*zIndex=*/3);
+ uiCanvas->DrawLine(cx, cy - 5, cx, cy + 5,
+ /*fillColor=*/{ 255, 255, 255, 255 },
+ /*outlineColor=*/{},
+ /*zIndex=*/3);
} else if(how == CLICK ||
(how == HOVER && leftDown && editControl.colorPicker.picker2dActive))
{
}
}
- SetMousePointerToHand(mousePointerAsHand);
+ window->SetCursor(mousePointerAsHand ?
+ Window::Cursor::HAND :
+ Window::Cursor::POINTER);
return true;
}
-void TextWindow::Paint(void) {
- int width, height;
- GetTextWindowSize(&width, &height);
-
- // We would like things pixel-exact, to avoid shimmering.
- glViewport(0, 0, width, height);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glClearColor(0, 0, 0, 1);
- glClear(GL_COLOR_BUFFER_BIT);
- glColor3d(1, 1, 1);
+void TextWindow::Paint() {
+ if (!canvas) return;
- glTranslated(-1, 1, 0);
- glScaled(2.0/width, -2.0/height, 1);
- // Make things round consistently, avoiding exact integer boundary
- glTranslated(-0.1, -0.1, 0);
+ double width, height;
+ window->GetContentSize(&width, &height);
+ if(halfRows != (int)height / (LINE_HEIGHT/2))
+ Resize();
- halfRows = height / (LINE_HEIGHT/2);
+ Camera camera = {};
+ camera.width = width;
+ camera.height = height;
+ camera.pixelRatio = window->GetDevicePixelRatio();
+ camera.gridFit = (window->GetDevicePixelRatio() == 1);
+ camera.LoadIdentity();
+ camera.offset.x = -camera.width / 2.0;
+ camera.offset.y = -camera.height / 2.0;
- int bottom = top[rows-1] + 2;
- scrollPos = min(scrollPos, bottom - halfRows);
- scrollPos = max(scrollPos, 0);
+ Lighting lighting = {};
+ lighting.backgroundColor = RGBi(0, 0, 0);
- // Let's set up the scroll bar first
- MoveTextScrollbarTo(scrollPos, top[rows - 1] + 1, halfRows);
+ canvas->SetLighting(lighting);
+ canvas->SetCamera(camera);
+ canvas->StartFrame();
- // Create the bitmap font that we're going to use.
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_BLEND);
+ UiCanvas uiCanvas = {};
+ uiCanvas.canvas = canvas;
+ uiCanvas.flip = true;
- // Now paint the window.
int r, c, a;
for(a = 0; a < 2; a++) {
- if(a == 0) {
- glBegin(GL_QUADS);
- } else if(a == 1) {
- glEnable(GL_TEXTURE_2D);
- ssglInitializeBitmapFont();
- glBegin(GL_QUADS);
- }
-
for(r = 0; r < rows; r++) {
int ltop = top[r];
if(ltop < (scrollPos-1)) continue;
if(ltop > scrollPos+halfRows) break;
- for(c = 0; c < min((width/CHAR_WIDTH)+1, (int) MAX_COLS); c++) {
- int x = LEFT_MARGIN + c*CHAR_WIDTH;
+ for(c = 0; c < min(((int)width/CHAR_WIDTH_)+1, (int) MAX_COLS); c++) {
+ int x = LEFT_MARGIN + c*CHAR_WIDTH_;
int y = (ltop-scrollPos)*(LINE_HEIGHT/2) + 4;
int fg = meta[r][c].fg;
int bg = meta[r][c].bg;
- RgbaColor bgRgb = meta[r][c].bgRgb;
// On the first pass, all the background quads; on the next
// pass, all the foreground (i.e., font) quads.
if(a == 0) {
- int bh = LINE_HEIGHT, adj = -2;
+ RgbaColor bgRgb = meta[r][c].bgRgb;
+ int bh = LINE_HEIGHT, adj = 0;
if(bg == 'z') {
- glColor3f(bgRgb.redF(), bgRgb.greenF(), bgRgb.blueF());
bh = CHAR_HEIGHT;
adj += 2;
} else {
- glColor3fv(&(bgColorTable[bg*3]));
+ bgRgb = RgbaColor::FromFloat(bgColorTable[bg*3+0],
+ bgColorTable[bg*3+1],
+ bgColorTable[bg*3+2]);
}
if(bg != 'd') {
// Move the quad down a bit, so that the descenders
// still have the correct background.
- y += adj;
- ssglAxisAlignedQuad(x, x + CHAR_WIDTH, y, y + bh, false);
- y -= adj;
+ uiCanvas.DrawRect(x, x + CHAR_WIDTH_, y + adj, y + adj + bh,
+ /*fillColor=*/bgRgb, /*outlineColor=*/{});
}
} else if(a == 1) {
- glColor3fv(&(fgColorTable[fg*3]));
- ssglBitmapCharQuad(text[r][c], x, y + CHAR_HEIGHT);
+ RgbaColor fgRgb = RgbaColor::FromFloat(fgColorTable[fg*3+0],
+ fgColorTable[fg*3+1],
+ fgColorTable[fg*3+2]);
+ if(text[r][c] != ' ') {
+ uiCanvas.DrawBitmapChar(text[r][c], x, y + CHAR_HEIGHT, fgRgb);
+ }
// If this is a link and it's hovered, then draw the
// underline
cs++;
}
- glEnd();
-
// Always use the color of the rightmost character
// in the link, so that underline is consistent color
fg = meta[r][cf-1].fg;
- glColor3fv(&(fgColorTable[fg*3]));
- glDisable(GL_TEXTURE_2D);
- glLineWidth(1);
- glBegin(GL_LINES);
- int yp = y + CHAR_HEIGHT;
- glVertex2d(LEFT_MARGIN + cs*CHAR_WIDTH, yp);
- glVertex2d(LEFT_MARGIN + cf*CHAR_WIDTH, yp);
- glEnd();
-
- glEnable(GL_TEXTURE_2D);
- glBegin(GL_QUADS);
+ fgRgb = RgbaColor::FromFloat(fgColorTable[fg*3+0],
+ fgColorTable[fg*3+1],
+ fgColorTable[fg*3+2]);
+ int yp = y + CHAR_HEIGHT;
+ uiCanvas.DrawLine(LEFT_MARGIN + cs*CHAR_WIDTH_, yp,
+ LEFT_MARGIN + cf*CHAR_WIDTH_, yp,
+ fgRgb);
}
}
}
}
-
- glEnd();
- glDisable(GL_TEXTURE_2D);
}
// The line to indicate the column of radio buttons that indicates the
// active group.
SS.GW.GroupSelection();
+ auto const &gs = SS.GW.gs;
// Make sure this test agrees with test to determine which screen is drawn
if(!SS.GW.pending.description && gs.n == 0 && gs.constraints == 0 &&
- shown.screen == SCREEN_LIST_OF_GROUPS)
+ shown.screen == Screen::LIST_OF_GROUPS)
{
int x = 29, y = 70 + LINE_HEIGHT;
y -= scrollPos*(LINE_HEIGHT/2);
- glLineWidth(1);
- glColor3fv(&(fgColorTable['t'*3]));
- glBegin(GL_LINES);
- glVertex2d(x, y);
- glVertex2d(x, y+40);
- glEnd();
+ RgbaColor color = RgbaColor::FromFloat(fgColorTable['t'*3+0],
+ fgColorTable['t'*3+1],
+ fgColorTable['t'*3+2]);
+ uiCanvas.DrawLine(x, y, x, y+40, color);
}
// The header has some icons that are drawn separately from the text
- DrawOrHitTestIcons(PAINT, 0, 0);
+ DrawOrHitTestIcons(&uiCanvas, PAINT, 0, 0);
// And we may show a color picker for certain editable fields
- DrawOrHitTestColorPicker(PAINT, false, 0, 0);
+ DrawOrHitTestColorPicker(&uiCanvas, PAINT, false, 0, 0);
+
+ canvas->FlushFrame();
+ canvas->FinishFrame();
+ canvas->Clear();
}
void TextWindow::MouseEvent(bool leftClick, bool leftDown, double x, double y) {
- if(TextEditControlIsVisible() || GraphicsEditControlIsVisible()) {
- if(DrawOrHitTestColorPicker(leftClick ? CLICK : HOVER, leftDown, x, y))
- {
+ using Platform::Window;
+
+ if(SS.TW.window->IsEditorVisible() || SS.GW.window->IsEditorVisible()) {
+ if(DrawOrHitTestColorPicker(NULL, leftClick ? CLICK : HOVER, leftDown, x, y)) {
return;
}
if(leftClick) {
HideEditControl();
- HideGraphicsEditControl();
+ SS.GW.window->HideEditor();
} else {
- SetMousePointerToHand(false);
+ window->SetCursor(Window::Cursor::POINTER);
}
return;
}
- DrawOrHitTestIcons(leftClick ? CLICK : HOVER, x, y);
+ DrawOrHitTestIcons(NULL, leftClick ? CLICK : HOVER, x, y);
GraphicsWindow::Selection ps = SS.GW.hover;
SS.GW.hover.Clear();
hoveredCol = 0;
// Find the corresponding character in the text buffer
- int c = (int)((x - LEFT_MARGIN) / CHAR_WIDTH);
+ int c = (int)((x - LEFT_MARGIN) / CHAR_WIDTH_);
int hh = (LINE_HEIGHT)/2;
y += scrollPos*hh;
int r;
break;
}
}
- if(r < 0 || c < 0 || r >= rows || c >= MAX_COLS) {
- SetMousePointerToHand(false);
- goto done;
- }
+ if(r >= 0 && c >= 0 && r < rows && c < MAX_COLS) {
+ window->SetCursor(Window::Cursor::POINTER);
- hoveredRow = r;
- hoveredCol = c;
+ hoveredRow = r;
+ hoveredCol = c;
-#define META (meta[r][c])
- if(leftClick) {
- if(META.link && META.f) {
- (META.f)(META.link, META.data);
- Show();
- InvalidateGraphics();
- }
- } else {
- if(META.link) {
- SetMousePointerToHand(true);
- if(META.h) {
- (META.h)(META.link, META.data);
+ const auto &item = meta[r][c];
+ if(leftClick) {
+ if(item.link && item.f) {
+ (item.f)(item.link, item.data);
+ Show();
+ SS.GW.Invalidate();
}
} else {
- SetMousePointerToHand(false);
+ if(item.link) {
+ window->SetCursor(Window::Cursor::HAND);
+ if(item.h) {
+ (item.h)(item.link, item.data);
+ }
+ } else {
+ window->SetCursor(Window::Cursor::POINTER);
+ }
}
}
-#undef META
-done:
if((!ps.Equals(&(SS.GW.hover))) ||
prevHoveredRow != hoveredRow ||
prevHoveredCol != hoveredCol)
{
- InvalidateGraphics();
- InvalidateText();
+ SS.GW.Invalidate();
+ window->Invalidate();
}
}
-void TextWindow::MouseLeave(void) {
- tooltippedIcon = NULL;
- hoveredIcon = NULL;
+void TextWindow::MouseLeave() {
+ hoveredButton = NULL;
hoveredRow = 0;
hoveredCol = 0;
- InvalidateText();
+ window->Invalidate();
}
-void TextWindow::ScrollbarEvent(int newPos) {
- if(TextEditControlIsVisible())
+void TextWindow::ScrollbarEvent(double newPos) {
+ if(window->IsEditorVisible()) {
+ // An edit field is active. Do not move the scrollbar.
return;
+ }
int bottom = top[rows-1] + 2;
- newPos = min(newPos, bottom - halfRows);
- newPos = max(newPos, 0);
+ newPos = min((int)newPos, bottom - halfRows);
+ newPos = max((int)newPos, 0);
if(newPos != scrollPos) {
- scrollPos = newPos;
- MoveTextScrollbarTo(scrollPos, top[rows - 1] + 1, halfRows);
- InvalidateText();
+ scrollPos = (int)newPos;
+ window->SetScrollbarPosition(scrollPos);
+ window->Invalidate();
}
}
+}
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
#include "solvespace.h"
-#include "generated/icons.h"
-
-static uint8_t SPACER[1];
-static const struct {
- uint8_t *image;
- int menu;
- const char *tip;
-} Toolbar[] = {
- { Icon_line, GraphicsWindow::MNU_LINE_SEGMENT, "Sketch line segment" },
- { Icon_rectangle, GraphicsWindow::MNU_RECTANGLE, "Sketch rectangle" },
- { Icon_circle, GraphicsWindow::MNU_CIRCLE, "Sketch circle" },
- { Icon_arc, GraphicsWindow::MNU_ARC, "Sketch arc of a circle" },
- { Icon_text, GraphicsWindow::MNU_TTF_TEXT, "Sketch curves from text in a TrueType font" },
- { Icon_tangent_arc, GraphicsWindow::MNU_TANGENT_ARC, "Create tangent arc at selected point" },
- { Icon_bezier, GraphicsWindow::MNU_CUBIC, "Sketch cubic Bezier spline" },
- { Icon_point, GraphicsWindow::MNU_DATUM_POINT, "Sketch datum point" },
- { Icon_construction, GraphicsWindow::MNU_CONSTRUCTION, "Toggle construction" },
- { Icon_trim, GraphicsWindow::MNU_SPLIT_CURVES, "Split lines / curves where they intersect" },
- { SPACER, 0, 0 },
-
- { Icon_length, GraphicsWindow::MNU_DISTANCE_DIA, "Constrain distance / diameter / length" },
- { Icon_angle, GraphicsWindow::MNU_ANGLE, "Constrain angle" },
- { Icon_horiz, GraphicsWindow::MNU_HORIZONTAL, "Constrain to be horizontal" },
- { Icon_vert, GraphicsWindow::MNU_VERTICAL, "Constrain to be vertical" },
- { Icon_parallel, GraphicsWindow::MNU_PARALLEL, "Constrain to be parallel or tangent" },
- { Icon_perpendicular, GraphicsWindow::MNU_PERPENDICULAR, "Constrain to be perpendicular" },
- { Icon_pointonx, GraphicsWindow::MNU_ON_ENTITY, "Constrain point on line / curve / plane / point" },
- { Icon_symmetric, GraphicsWindow::MNU_SYMMETRIC, "Constrain symmetric" },
- { Icon_equal, GraphicsWindow::MNU_EQUAL, "Constrain equal length / radius / angle" },
- { Icon_same_orientation,GraphicsWindow::MNU_ORIENTED_SAME, "Constrain normals in same orientation" },
- { Icon_other_supp, GraphicsWindow::MNU_OTHER_ANGLE, "Other supplementary angle" },
- { Icon_ref, GraphicsWindow::MNU_REFERENCE, "Toggle reference dimension" },
- { SPACER, 0, 0 },
-
- { Icon_extrude, GraphicsWindow::MNU_GROUP_EXTRUDE, "New group extruding active sketch" },
- { Icon_lathe, GraphicsWindow::MNU_GROUP_LATHE, "New group rotating active sketch" },
- { Icon_step_rotate, GraphicsWindow::MNU_GROUP_ROT, "New group step and repeat rotating" },
- { Icon_step_translate, GraphicsWindow::MNU_GROUP_TRANS, "New group step and repeat translating" },
- { Icon_sketch_in_plane, GraphicsWindow::MNU_GROUP_WRKPL, "New group in new workplane (thru given entities)" },
- { Icon_sketch_in_3d, GraphicsWindow::MNU_GROUP_3D, "New group in 3d" },
- { Icon_assemble, GraphicsWindow::MNU_GROUP_LINK, "New group linking / assembling file" },
- { SPACER, 0, 0 },
-
- { Icon_in3d, GraphicsWindow::MNU_NEAREST_ISO, "Nearest isometric view" },
- { Icon_ontoworkplane, GraphicsWindow::MNU_ONTO_WORKPLANE, "Align view to active workplane" },
- { NULL, 0, 0 }
+
+struct ToolIcon {
+ std::string name;
+ Command command;
+ const char *tooltip;
+ std::shared_ptr<Pixmap> pixmap;
+};
+static ToolIcon Toolbar[] = {
+ { "line", Command::LINE_SEGMENT,
+ N_("Sketch line segment"), {} },
+ { "rectangle", Command::RECTANGLE,
+ N_("Sketch rectangle"), {} },
+ { "circle", Command::CIRCLE,
+ N_("Sketch circle"), {} },
+ { "arc", Command::ARC,
+ N_("Sketch arc of a circle"), {} },
+ { "text", Command::TTF_TEXT,
+ N_("Sketch curves from text in a TrueType font"), {} },
+ { "image", Command::IMAGE,
+ N_("Sketch image from a file"), {} },
+ { "tangent-arc", Command::TANGENT_ARC,
+ N_("Create tangent arc at selected point"), {} },
+ { "bezier", Command::CUBIC,
+ N_("Sketch cubic Bezier spline"), {} },
+ { "point", Command::DATUM_POINT,
+ N_("Sketch datum point"), {} },
+ { "construction", Command::CONSTRUCTION,
+ N_("Toggle construction"), {} },
+ { "trim", Command::SPLIT_CURVES,
+ N_("Split lines / curves where they intersect"), {} },
+ { "", Command::NONE, "", {} },
+
+ { "length", Command::DISTANCE_DIA,
+ N_("Constrain distance / diameter / length"), {} },
+ { "angle", Command::ANGLE,
+ N_("Constrain angle"), {} },
+ { "horiz", Command::HORIZONTAL,
+ N_("Constrain to be horizontal"), {} },
+ { "vert", Command::VERTICAL,
+ N_("Constrain to be vertical"), {} },
+ { "parallel", Command::PARALLEL,
+ N_("Constrain to be parallel or tangent"), {} },
+ { "perpendicular", Command::PERPENDICULAR,
+ N_("Constrain to be perpendicular"), {} },
+ { "pointonx", Command::ON_ENTITY,
+ N_("Constrain point on line / curve / plane / point"), {} },
+ { "symmetric", Command::SYMMETRIC,
+ N_("Constrain symmetric"), {} },
+ { "equal", Command::EQUAL,
+ N_("Constrain equal length / radius / angle"), {} },
+ { "same-orientation",Command::ORIENTED_SAME,
+ N_("Constrain normals in same orientation"), {} },
+ { "other-supp", Command::OTHER_ANGLE,
+ N_("Other supplementary angle"), {} },
+ { "ref", Command::REFERENCE,
+ N_("Toggle reference dimension"), {} },
+ { "", Command::NONE, "", {} },
+
+ { "extrude", Command::GROUP_EXTRUDE,
+ N_("New group extruding active sketch"), {} },
+ { "lathe", Command::GROUP_LATHE,
+ N_("New group rotating active sketch"), {} },
+ { "step-rotate", Command::GROUP_ROT,
+ N_("New group step and repeat rotating"), {} },
+ { "step-translate", Command::GROUP_TRANS,
+ N_("New group step and repeat translating"), {} },
+ { "sketch-in-plane", Command::GROUP_WRKPL,
+ N_("New group in new workplane (thru given entities)"), {} },
+ { "sketch-in-3d", Command::GROUP_3D,
+ N_("New group in 3d"), {} },
+ { "assemble", Command::GROUP_LINK,
+ N_("New group linking / assembling file"), {} },
+ { "", Command::NONE, "", {} },
+
+ { "in3d", Command::NEAREST_ISO,
+ N_("Nearest isometric view"), {} },
+ { "ontoworkplane", Command::ONTO_WORKPLANE,
+ N_("Align view to active workplane"), {} },
};
-void GraphicsWindow::ToolbarDraw(void) {
- ToolbarDrawOrHitTest(0, 0, true, NULL);
+void GraphicsWindow::ToolbarDraw(UiCanvas *canvas) {
+ ToolbarDrawOrHitTest(0, 0, canvas, NULL, NULL, NULL);
}
bool GraphicsWindow::ToolbarMouseMoved(int x, int y) {
+ double width, height;
+ window->GetContentSize(&width, &height);
+
x += ((int)width/2);
y += ((int)height/2);
- int nh = 0;
- bool withinToolbar = ToolbarDrawOrHitTest(x, y, false, &nh);
- if(!withinToolbar) nh = 0;
+ Command hitCommand;
+ int hitX, hitY;
+ bool withinToolbar = ToolbarDrawOrHitTest(x, y, NULL, &hitCommand, &hitX, &hitY);
- if(nh != toolbarTooltipped) {
- // Don't let the tool tip move around if the mouse moves within the
- // same item.
- toolbarMouseX = x;
- toolbarMouseY = y;
- toolbarTooltipped = 0;
+ if(hitCommand != toolbarHovered) {
+ toolbarHovered = hitCommand;
+ Invalidate();
}
- if(nh != toolbarHovered) {
- toolbarHovered = nh;
- SetTimerFor(1000);
- PaintGraphics();
+ if(toolbarHovered != Command::NONE) {
+ std::string tooltip;
+ for(ToolIcon &icon : Toolbar) {
+ if(toolbarHovered == icon.command) {
+ tooltip = Translate(icon.tooltip);
+ }
+ }
+
+ Platform::KeyboardEvent accel = SS.GW.AcceleratorForCommand(toolbarHovered);
+ std::string accelDesc = Platform::AcceleratorDescription(accel);
+ if(!accelDesc.empty()) {
+ tooltip += ssprintf(" (%s)", accelDesc.c_str());
+ }
+
+ window->SetTooltip(tooltip, hitX, hitY, 32, 32);
+ } else {
+ window->SetTooltip("", 0, 0, 0, 0);
}
- // So if we moved off the toolbar, then toolbarHovered is now equal to
- // zero, so it doesn't matter if the tool tip timer expires. And if
- // we moved from one item to another, we reset the timer, so also okay.
+
return withinToolbar;
}
bool GraphicsWindow::ToolbarMouseDown(int x, int y) {
+ double width, height;
+ window->GetContentSize(&width, &height);
+
x += ((int)width/2);
y += ((int)height/2);
- int nh = -1;
- bool withinToolbar = ToolbarDrawOrHitTest(x, y, false, &nh);
- // They might have clicked within the toolbar, but not on a button.
- if(withinToolbar && nh >= 0) {
- for(int i = 0; SS.GW.menu[i].level >= 0; i++) {
- if(nh == SS.GW.menu[i].id) {
- (SS.GW.menu[i].fn)((GraphicsWindow::MenuId)SS.GW.menu[i].id);
- break;
- }
- }
+ Command hitCommand;
+ bool withinToolbar = ToolbarDrawOrHitTest(x, y, NULL, &hitCommand, NULL, NULL);
+ if(hitCommand != Command::NONE) {
+ SS.GW.ActivateCommand(hitCommand);
}
return withinToolbar;
}
-bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
- bool paint, int *menuHit)
+bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my, UiCanvas *canvas,
+ Command *hitCommand, int *hitX, int *hitY)
{
- int i;
+ double width, height;
+ window->GetContentSize(&width, &height);
+
int x = 17, y = (int)(height - 52);
+ // When changing these values, also change the asReference drawing code in drawentity.cpp.
int fudge = 8;
- int h = 32*16 + 3*16 + fudge;
+ int h = 34*16 + 3*16 + fudge;
int aleft = 0, aright = 66, atop = y+16+fudge/2, abot = y+16-h;
bool withinToolbar =
(mx >= aleft && mx <= aright && my <= atop && my >= abot);
- if(!paint && !withinToolbar) {
+ // Initialize/clear hitCommand.
+ if(hitCommand) *hitCommand = Command::NONE;
+
+ if(!canvas && !withinToolbar) {
// This gets called every MouseMove event, so return quickly.
return false;
}
- if(paint) {
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glTranslated(-1, -1, 0);
- glScaled(2.0/width, 2.0/height, 0);
- glDisable(GL_LIGHTING);
-
- double c = 30.0/255;
- glColor4d(c, c, c, 1.0);
- ssglAxisAlignedQuad(aleft, aright, atop, abot);
+ if(canvas) {
+ canvas->DrawRect(aleft, aright, atop, abot,
+ /*fillColor=*/{ 30, 30, 30, 255 },
+ /*outlineColor=*/{});
}
- struct {
- bool show;
- const char *str;
- } toolTip = { false, NULL };
-
bool leftpos = true;
- for(i = 0; Toolbar[i].image; i++) {
- if(Toolbar[i].image == SPACER) {
+ for(ToolIcon &icon : Toolbar) {
+ if(icon.name.empty()) { // spacer
if(!leftpos) {
leftpos = true;
y -= 32;
}
y -= 16;
- if(paint) {
+ if(canvas) {
// Draw a separator bar in a slightly different color.
int divw = 30, divh = 2;
- glColor4d(0.17, 0.17, 0.17, 1);
- x += 16;
- y += 24;
- ssglAxisAlignedQuad(x+divw, x-divw, y+divh, y-divh);
- x -= 16;
- y -= 24;
+ canvas->DrawRect(x+16+divw, x+16-divw, y+24+divh, y+24-divh,
+ /*fillColor=*/{ 45, 45, 45, 255 },
+ /*outlineColor=*/{});
}
continue;
}
- if(paint) {
- glRasterPos2i(x - 12, y - 12);
- glDrawPixels(24, 24, GL_RGB, GL_UNSIGNED_BYTE, Toolbar[i].image);
+ if(icon.pixmap == nullptr) {
+ icon.pixmap = LoadPng("icons/graphics-window/" + icon.name + ".png");
+ }
- if(toolbarHovered == Toolbar[i].menu ||
- pending.operation == Toolbar[i].menu) {
- // Highlight the hovered or pending item.
- glColor4d(1, 1, 0, 0.3);
- int boxhw = 15;
- ssglAxisAlignedQuad(x+boxhw, x-boxhw, y+boxhw, y-boxhw);
- }
+ if(canvas) {
+ canvas->DrawPixmap(icon.pixmap,
+ x - (int)icon.pixmap->width / 2,
+ y - (int)icon.pixmap->height / 2);
- if(toolbarTooltipped == Toolbar[i].menu) {
- // Display the tool tip for this item; postpone till later
- // so that no one draws over us. Don't need position since
- // that's just wherever the mouse is.
- toolTip.show = true;
- toolTip.str = Toolbar[i].tip;
+ if(toolbarHovered == icon.command ||
+ (pending.operation == Pending::COMMAND &&
+ pending.command == icon.command)) {
+ // Highlight the hovered or pending item.
+ const int boxhw = 15;
+ canvas->DrawRect(x+boxhw, x-boxhw, y+boxhw, y-boxhw,
+ /*fillColor=*/{ 255, 255, 0, 75 },
+ /*outlineColor=*/{});
}
} else {
- int boxhw = 16;
+ const int boxhw = 16;
if(mx < (x+boxhw) && mx > (x - boxhw) &&
my < (y+boxhw) && my > (y - boxhw))
{
- if(menuHit) *menuHit = Toolbar[i].menu;
+ if(hitCommand) *hitCommand = icon.command;
+ if(hitX) *hitX = x - boxhw;
+ if(hitY) *hitY = (int)height - (y + boxhw);
}
}
}
}
- if(paint) {
- // Do this last so that nothing can draw over it.
- if(toolTip.show) {
- ssglInitializeBitmapFont();
- std::string str = toolTip.str;
-
- for(i = 0; SS.GW.menu[i].level >= 0; i++) {
- if(toolbarTooltipped == SS.GW.menu[i].id) {
- std::string accel = MakeAcceleratorLabel(SS.GW.menu[i].accel);
- if(!accel.empty()) {
- str += ssprintf(" (%s)", accel.c_str());
- }
- break;
- }
- }
-
- int tw = str.length() * (SS.TW.CHAR_WIDTH - 1) + 10,
- th = SS.TW.LINE_HEIGHT + 2;
-
- double ox = toolbarMouseX + 3, oy = toolbarMouseY + 3;
- glLineWidth(1);
- glColor4d(1.0, 1.0, 0.6, 1.0);
- ssglAxisAlignedQuad(ox, ox+tw, oy, oy+th);
- glColor4d(0.0, 0.0, 0.0, 1.0);
- ssglAxisAlignedLineLoop(ox, ox+tw, oy, oy+th);
-
- glColor4d(0, 0, 0, 1);
- glPushMatrix();
- glTranslated(ox+5, oy+3, 0);
- glScaled(1, -1, 1);
- ssglBitmapText(str, Vector::From(0, 0, 0));
- glPopMatrix();
- }
- ssglDepthRangeLockToFront(false);
- }
-
return withinToolbar;
}
-
-void GraphicsWindow::TimerCallback(void) {
- SS.GW.toolbarTooltipped = SS.GW.toolbarHovered;
- PaintGraphics();
-}
-
void TtfFontList::LoadAll() {
if(loaded) return;
- for(const std::string &font : GetFontFiles()) {
+ for(const Platform::Path &font : Platform::GetFontFiles()) {
TtfFont tf = {};
tf.fontFile = font;
if(tf.LoadFromFile(fontLibrary))
l.Add(&tf);
}
+ // Add builtin font to end of font list so it is displayed first in the UI
+ {
+ TtfFont tf = {};
+ tf.SetResourceID("fonts/BitstreamVeraSans-Roman-builtin.ttf");
+ if(tf.LoadFromResource(fontLibrary))
+ l.Add(&tf);
+ }
+
// Sort fonts according to their actual name, not filename.
- std::sort(&l.elem[0], &l.elem[l.n],
+ std::sort(l.begin(), l.end(),
[](const TtfFont &a, const TtfFont &b) { return a.name < b.name; });
// Filter out fonts with the same family and style name. This is not
// strictly necessarily the exact same font, but it will almost always be.
- TtfFont *it = std::unique(&l.elem[0], &l.elem[l.n],
- [](const TtfFont &a, const TtfFont &b) { return a.name == b.name; });
- l.RemoveLast(&l.elem[l.n] - it);
+ TtfFont *it = std::unique(l.begin(), l.end(),
+ [](const TtfFont &a, const TtfFont &b) { return a.name == b.name; });
+ l.RemoveLast(&l[l.n] - it);
- // TODO: identify fonts by their name and not filename, which may change
- // between OSes.
+ //! @todo identify fonts by their name and not filename, which may change
+ //! between OSes.
loaded = true;
}
-void TtfFontList::PlotString(const std::string &font, const std::string &str,
- SBezierList *sbl, Vector origin, Vector u, Vector v)
+TtfFont *TtfFontList::LoadFont(const std::string &font)
{
LoadAll();
- TtfFont *tf = std::find_if(&l.elem[0], &l.elem[l.n],
- [&](const TtfFont &tf) { return tf.FontFileBaseName() == font; });
+ TtfFont *tf = std::find_if(l.begin(), l.end(),
+ [&font](const TtfFont &tf) { return tf.FontFileBaseName() == font; });
- if(!str.empty() && tf != &l.elem[l.n]) {
+ if(tf != l.end()) {
if(tf->fontFace == NULL) {
- tf->LoadFromFile(fontLibrary, /*nameOnly=*/false);
+ if(tf->IsResource())
+ tf->LoadFromResource(fontLibrary, /*keepOpen=*/true);
+ else
+ tf->LoadFromFile(fontLibrary, /*keepOpen=*/true);
}
+ return tf;
+ } else {
+ return NULL;
+ }
+}
+
+void TtfFontList::PlotString(const std::string &font, const std::string &str,
+ SBezierList *sbl, Vector origin, Vector u, Vector v)
+{
+ TtfFont *tf = LoadFont(font);
+ if(!str.empty() && tf != NULL) {
tf->PlotString(str, sbl, origin, u, v);
} else {
// No text or no font; so draw a big X for an error marker.
}
}
+double TtfFontList::AspectRatio(const std::string &font, const std::string &str)
+{
+ TtfFont *tf = LoadFont(font);
+ if(tf != NULL) {
+ return tf->AspectRatio(str);
+ }
+
+ return 0.0;
+}
+
//-----------------------------------------------------------------------------
// Return the basename of our font filename; that's how the requests and
// entities that reference us will store it.
//-----------------------------------------------------------------------------
std::string TtfFont::FontFileBaseName() const {
- std::string baseName = fontFile;
- size_t pos = baseName.rfind(PATH_SEP);
- if(pos != std::string::npos)
- return baseName.erase(0, pos + 1);
- return "";
+ return fontFile.FileName();
}
//-----------------------------------------------------------------------------
-// Load a TrueType font into memory. We care about the curves that define
-// the letter shapes, and about the mappings that determine which glyph goes
-// with which character.
+// Convenience method to set fontFile for resource-loaded fonts as res://<path>
+//-----------------------------------------------------------------------------
+void TtfFont::SetResourceID(const std::string &resource) {
+ fontFile = { "res://" + resource };
+}
+
+bool TtfFont::IsResource() const {
+ return fontFile.raw.compare(0, 6, "res://") == 0;
+}
+
//-----------------------------------------------------------------------------
-bool TtfFont::LoadFromFile(FT_Library fontLibrary, bool nameOnly) {
+// Load a TrueType font into memory.
+//-----------------------------------------------------------------------------
+bool TtfFont::LoadFromFile(FT_Library fontLibrary, bool keepOpen) {
+ ssassert(!IsResource(), "Cannot load a font provided by a resource as a file.");
+
FT_Open_Args args = {};
args.flags = FT_OPEN_PATHNAME;
- args.pathname = &fontFile[0]; // FT_String is char* for historical reasons
+ args.pathname = &fontFile.raw[0]; // FT_String is char* for historical reasons
- // We don't use ssfopen() here to let freetype do its own memory management.
+ // We don't use OpenFile() here to let freetype do its own memory management.
// This is OK because on Linux/OS X we just delegate to fopen and on Windows
// we only look into C:\Windows\Fonts, which has a known short path.
if(int fterr = FT_Open_Face(fontLibrary, &args, 0, &fontFace)) {
dbp("freetype: loading font from file '%s' failed: %s",
- fontFile.c_str(), ft_error_string(fterr));
+ fontFile.raw.c_str(), ft_error_string(fterr));
return false;
}
+ return ExtractTTFData(keepOpen);
+}
+
+//-----------------------------------------------------------------------------
+// Load a TrueType from resource in memory. Implemented to load bundled fonts
+// through theresource system.
+//-----------------------------------------------------------------------------
+bool TtfFont::LoadFromResource(FT_Library fontLibrary, bool keepOpen) {
+ ssassert(IsResource(), "Font to be loaded as resource is not provided by a resource "
+ "or does not have the 'res://' prefix.");
+
+ size_t _size;
+ // substr to cut off 'res://' (length: 6)
+ const void *_buffer = Platform::LoadResource(fontFile.raw.substr(6, fontFile.raw.size()),
+ &_size);
+
+ FT_Long size = static_cast<FT_Long>(_size);
+ const FT_Byte *buffer = reinterpret_cast<const FT_Byte*>(_buffer);
+
+ if(int fterr = FT_New_Memory_Face(fontLibrary, buffer, size, 0, &fontFace)) {
+ dbp("freetype: loading font '%s' from memory failed: %s",
+ fontFile.raw.c_str(), ft_error_string(fterr));
+ return false;
+ }
+
+ return ExtractTTFData(keepOpen);
+}
+
+//-----------------------------------------------------------------------------
+// Extract font information. We care about the font name and unit size.
+//-----------------------------------------------------------------------------
+bool TtfFont::ExtractTTFData(bool keepOpen) {
if(int fterr = FT_Select_Charmap(fontFace, FT_ENCODING_UNICODE)) {
dbp("freetype: loading unicode CMap for file '%s' failed: %s",
- fontFile.c_str(), ft_error_string(fterr));
+ fontFile.raw.c_str(), ft_error_string(fterr));
FT_Done_Face(fontFace);
fontFace = NULL;
return false;
name = std::string(fontFace->family_name) +
" (" + std::string(fontFace->style_name) + ")";
- if(nameOnly) {
- FT_Done_Face(fontFace);
- fontFace = NULL;
- return true;
- }
-
// We always ask Freetype to give us a unit size character.
// It uses fixed point; put the unit size somewhere in the middle of the dynamic
// range of its 26.6 fixed point type, and adjust the factors so that the unit
sizeRequest.horiResolution = 128;
sizeRequest.vertResolution = 128;
if(int fterr = FT_Request_Size(fontFace, &sizeRequest)) {
- dbp("freetype: cannot set character size: %s",
- ft_error_string(fterr));
+ dbp("freetype: size request for file '%s' failed: %s",
+ fontFile.raw.c_str(), ft_error_string(fterr));
FT_Done_Face(fontFace);
fontFace = NULL;
return false;
char chr = 'A';
uint32_t gid = FT_Get_Char_Index(fontFace, 'A');
if (gid == 0) {
- dbp("freetype: CID-to-GID mapping for CID 0x%04x failed: %s; using CID as GID",
- chr, ft_error_string(gid));
- gid = chr;
+ dbp("freetype: CID-to-GID mapping for CID 0x%04x in file '%s' failed: %s; "
+ "using CID as GID",
+ chr, fontFile.raw.c_str(), ft_error_string(gid));
+ dbp("Assuming cap height is the same as requested height (this is likely wrong).");
+ capHeight = (double)sizeRequest.height;
}
if(gid) {
if(int fterr = FT_Load_Glyph(fontFace, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) {
- dbp("freetype: cannot load glyph for GID 0x%04x: %s",
- gid, ft_error_string(fterr));
+ dbp("freetype: cannot load glyph for GID 0x%04x in file '%s': %s",
+ gid, fontFile.raw.c_str(), ft_error_string(fterr));
FT_Done_Face(fontFace);
fontFace = NULL;
return false;
capHeight = (double)bbox.yMax;
}
+ // If we just wanted to get the font's name and figure out if it's actually usable, close
+ // it now. If we don't do this, and there are a lot of fonts, we can bump into the file
+ // descriptor limit (especially on Windows), breaking all file operations.
+ if(!keepOpen) {
+ FT_Done_Face(fontFace);
+ fontFace = NULL;
+ return true;
+ }
+
return true;
}
void TtfFont::PlotString(const std::string &str,
SBezierList *sbl, Vector origin, Vector u, Vector v)
{
- if(fontFace == NULL) oops();
+ ssassert(fontFace != NULL, "Expected font face to be loaded");
FT_Outline_Funcs outlineFuncs;
outlineFuncs.move_to = MoveTo;
for(char32_t cid : ReadUTF8(str)) {
uint32_t gid = FT_Get_Char_Index(fontFace, cid);
if (gid == 0) {
- dbp("freetype: CID-to-GID mapping for CID 0x%04x failed: %s; using CID as GID",
- cid, ft_error_string(gid));
+ dbp("freetype: CID-to-GID mapping for CID 0x%04x in file '%s' failed: %s; "
+ "using CID as GID",
+ cid, fontFile.raw.c_str(), ft_error_string(gid));
gid = cid;
}
* ones, antialiasing mitigates this considerably though.
*/
if(int fterr = FT_Load_Glyph(fontFace, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) {
- dbp("freetype: cannot load glyph for GID 0x%04x: %s",
- gid, ft_error_string(fterr));
+ dbp("freetype: cannot load glyph for GID 0x%04x in file '%s': %s",
+ gid, fontFile.raw.c_str(), ft_error_string(fterr));
return;
}
data.u = u;
data.v = v;
data.beziers = sbl;
- data.factor = 1.0 / capHeight;
+ data.factor = (float)(1.0 / capHeight);
data.bx = bx;
if(int fterr = FT_Outline_Decompose(&fontFace->glyph->outline, &outlineFuncs, &data)) {
- dbp("freetype: bezier decomposition failed (gid %d): %s",
- gid, ft_error_string(fterr));
+ dbp("freetype: bezier decomposition failed for GID 0x%4x in file '%s': %s",
+ gid, fontFile.raw.c_str(), ft_error_string(fterr));
}
// And we're done, so advance our position by the requested advance
dx += fontFace->glyph->advance.x;
}
}
+
+double TtfFont::AspectRatio(const std::string &str) {
+ ssassert(fontFace != NULL, "Expected font face to be loaded");
+
+ // We always request a unit size character, so the aspect ratio is the same as advance length.
+ double dx = 0;
+ for(char32_t chr : ReadUTF8(str)) {
+ uint32_t gid = FT_Get_Char_Index(fontFace, chr);
+ if (gid == 0) {
+ dbp("freetype: CID-to-GID mapping for CID 0x%04x in file '%s' failed: %s; "
+ "using CID as GID",
+ chr, fontFile.raw.c_str(), ft_error_string(gid));
+ }
+
+ if(int fterr = FT_Load_Glyph(fontFace, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) {
+ dbp("freetype: cannot load glyph for GID 0x%04x in file '%s': %s",
+ gid, fontFile.raw.c_str(), ft_error_string(fterr));
+ break;
+ }
+
+ dx += (double)fontFace->glyph->advance.x / capHeight;
+ }
+
+ return dx;
+}
// Copyright 2016 whitequark, Peter Barfuss.
//-----------------------------------------------------------------------------
-#ifndef __TTF_H
-#define __TTF_H
+#ifndef SOLVESPACE_TTF_H
+#define SOLVESPACE_TTF_H
class TtfFont {
public:
- std::string fontFile;
+ Platform::Path fontFile; // or resource path/name as res://<path>
std::string name;
FT_FaceRec_ *fontFace;
double capHeight;
+ void SetResourceID(const std::string &resource);
+ bool IsResource() const;
+
std::string FontFileBaseName() const;
- bool LoadFromFile(FT_LibraryRec_ *fontLibrary, bool nameOnly = true);
+ bool LoadFromFile(FT_LibraryRec_ *fontLibrary, bool keepOpen = false);
+ bool LoadFromResource(FT_LibraryRec_ *fontLibrary, bool keepOpen = false);
void PlotString(const std::string &str,
SBezierList *sbl, Vector origin, Vector u, Vector v);
+ double AspectRatio(const std::string &str);
+
+ bool ExtractTTFData(bool keepOpen);
};
class TtfFontList {
~TtfFontList();
void LoadAll();
+ TtfFont *LoadFont(const std::string &font);
void PlotString(const std::string &font, const std::string &str,
SBezierList *sbl, Vector origin, Vector u, Vector v);
+ double AspectRatio(const std::string &font, const std::string &str);
};
#endif
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
-#ifndef __UI_H
-#define __UI_H
+#ifndef SOLVESPACE_UI_H
+#define SOLVESPACE_UI_H
+
+class Locale {
+public:
+ std::string language;
+ std::string region;
+ uint16_t lcid;
+ std::string displayName;
+
+ std::string Culture() const {
+ return language + "-" + region;
+ }
+};
+
+struct LocaleLess {
+ bool operator()(const Locale &a, const Locale &b) const {
+ return a.language < b.language ||
+ (a.language == b.language && a.region < b.region);
+ }
+};
+
+const std::set<Locale, LocaleLess> &Locales();
+bool SetLocale(const std::string &name);
+bool SetLocale(uint16_t lcid);
+
+const std::string &Translate(const char *msgid);
+const std::string &Translate(const char *msgctxt, const char *msgid);
+const std::string &TranslatePlural(const char *msgid, unsigned n);
+const std::string &TranslatePlural(const char *msgctxt, const char *msgid, unsigned n);
+
+inline const char *N_(const char *msgid) {
+ return msgid;
+}
+inline const char *CN_(const char *msgctxt, const char *msgid) {
+ return msgid;
+}
+#if defined(LIBRARY)
+inline const char *_(const char *msgid) {
+ return msgid;
+}
+inline const char *C_(const char *msgctxt, const char *msgid) {
+ return msgid;
+}
+#else
+inline const char *_(const char *msgid) {
+ return Translate(msgid).c_str();
+}
+inline const char *C_(const char *msgctxt, const char *msgid) {
+ return Translate(msgctxt, msgid).c_str();
+}
+#endif
+
+// This table describes the top-level menus in the graphics window.
+enum class Command : uint32_t {
+ NONE = 0,
+ // File
+ NEW = 100,
+ OPEN,
+ OPEN_RECENT,
+ SAVE,
+ SAVE_AS,
+ EXPORT_IMAGE,
+ EXPORT_MESH,
+ EXPORT_SURFACES,
+ EXPORT_VIEW,
+ EXPORT_SECTION,
+ EXPORT_WIREFRAME,
+ IMPORT,
+ EXIT,
+ // View
+ ZOOM_IN,
+ ZOOM_OUT,
+ ZOOM_TO_FIT,
+ SHOW_GRID,
+ PERSPECTIVE_PROJ,
+ ONTO_WORKPLANE,
+ NEAREST_ORTHO,
+ NEAREST_ISO,
+ CENTER_VIEW,
+ SHOW_TOOLBAR,
+ SHOW_TEXT_WND,
+ UNITS_INCHES,
+ UNITS_MM,
+ UNITS_METERS,
+ FULL_SCREEN,
+ // Edit
+ UNDO,
+ REDO,
+ CUT,
+ COPY,
+ PASTE,
+ PASTE_TRANSFORM,
+ DELETE,
+ SELECT_CHAIN,
+ SELECT_ALL,
+ SNAP_TO_GRID,
+ ROTATE_90,
+ UNSELECT_ALL,
+ REGEN_ALL,
+ EDIT_LINE_STYLES,
+ VIEW_PROJECTION,
+ CONFIGURATION,
+ // Request
+ SEL_WORKPLANE,
+ FREE_IN_3D,
+ DATUM_POINT,
+ WORKPLANE,
+ LINE_SEGMENT,
+ CONSTR_SEGMENT,
+ CIRCLE,
+ ARC,
+ RECTANGLE,
+ CUBIC,
+ TTF_TEXT,
+ IMAGE,
+ SPLIT_CURVES,
+ TANGENT_ARC,
+ CONSTRUCTION,
+ // Group
+ GROUP_3D,
+ GROUP_WRKPL,
+ GROUP_EXTRUDE,
+ GROUP_HELIX,
+ GROUP_LATHE,
+ GROUP_REVOLVE,
+ GROUP_ROT,
+ GROUP_TRANS,
+ GROUP_LINK,
+ GROUP_RECENT,
+ // Constrain
+ DISTANCE_DIA,
+ REF_DISTANCE,
+ ANGLE,
+ REF_ANGLE,
+ OTHER_ANGLE,
+ REFERENCE,
+ EQUAL,
+ RATIO,
+ DIFFERENCE,
+ ON_ENTITY,
+ SYMMETRIC,
+ AT_MIDPOINT,
+ HORIZONTAL,
+ VERTICAL,
+ PARALLEL,
+ PERPENDICULAR,
+ ORIENTED_SAME,
+ WHERE_DRAGGED,
+ COMMENT,
+ // Analyze
+ VOLUME,
+ AREA,
+ PERIMETER,
+ INTERFERENCE,
+ NAKED_EDGES,
+ SHOW_DOF,
+ CENTER_OF_MASS,
+ TRACE_PT,
+ STOP_TRACING,
+ STEP_DIM,
+ // Help
+ LOCALE,
+ WEBSITE,
+ ABOUT,
+};
+
+class Button;
class TextWindow {
public:
enum {
MAX_COLS = 100,
MIN_COLS = 45,
- MAX_ROWS = 2000
+ MAX_ROWS = 4000
};
typedef struct {
float fgColorTable[256*3];
enum {
- CHAR_WIDTH = 9,
+ CHAR_WIDTH_ = 9,
CHAR_HEIGHT = 16,
LINE_HEIGHT = 20,
LEFT_MARGIN = 6,
} meta[MAX_ROWS][MAX_COLS];
int hoveredRow, hoveredCol;
-
int top[MAX_ROWS]; // in half-line units, or -1 for unused
int rows;
- // The row of icons at the top of the text window, to hide/show things
- typedef struct {
- bool *var;
- uint8_t *icon;
- const char *tip;
- } HideShowIcon;
- static HideShowIcon hideShowIcons[];
- static bool SPACER;
-
- // These are called by the platform-specific code.
- void Paint(void);
+ Platform::WindowRef window;
+ std::shared_ptr<ViewportCanvas> canvas;
+
+ void Draw(Canvas *canvas);
+
+ void Paint();
void MouseEvent(bool isClick, bool leftDown, double x, double y);
- void MouseScroll(double x, double y, int delta);
- void MouseLeave(void);
- void ScrollbarEvent(int newPos);
+ void MouseLeave();
+ void ScrollbarEvent(double newPos);
- enum {
+ enum DrawOrHitHow : uint32_t {
PAINT = 0,
HOVER = 1,
CLICK = 2
};
- void DrawOrHitTestIcons(int how, double mx, double my);
- void TimerCallback(void);
- Point2d oldMousePos;
- HideShowIcon *hoveredIcon, *tooltippedIcon;
+ void DrawOrHitTestIcons(UiCanvas *canvas, DrawOrHitHow how,
+ double mx, double my);
+ Button *hoveredButton;
Vector HsvToRgb(Vector hsv);
- uint8_t *HsvPattern2d(void);
- uint8_t *HsvPattern1d(double h, double s);
- void ColorPickerDone(void);
- bool DrawOrHitTestColorPicker(int how, bool leftDown, double x, double y);
+ std::shared_ptr<Pixmap> HsvPattern2d(int w, int h);
+ std::shared_ptr<Pixmap> HsvPattern1d(double hue, double sat, int w, int h);
+ void ColorPickerDone();
+ bool DrawOrHitTestColorPicker(UiCanvas *canvas, DrawOrHitHow how,
+ bool leftDown, double x, double y);
- void Init(void);
+ void Init();
void MakeColorTable(const Color *in, float *out);
void Printf(bool half, const char *fmt, ...);
- void ClearScreen(void);
+ void ClearScreen();
- void Show(void);
+ void Show();
+ void Resize();
// State for the screen that we are showing in the text window.
- enum {
- SCREEN_LIST_OF_GROUPS = 0,
- SCREEN_GROUP_INFO = 1,
- SCREEN_GROUP_SOLVE_INFO = 2,
- SCREEN_CONFIGURATION = 3,
- SCREEN_STEP_DIMENSION = 4,
- SCREEN_LIST_OF_STYLES = 5,
- SCREEN_STYLE_INFO = 6,
- SCREEN_PASTE_TRANSFORMED = 7,
- SCREEN_EDIT_VIEW = 8,
- SCREEN_TANGENT_ARC = 9
+ enum class Screen : uint32_t {
+ LIST_OF_GROUPS = 0,
+ GROUP_INFO = 1,
+ GROUP_SOLVE_INFO = 2,
+ CONFIGURATION = 3,
+ STEP_DIMENSION = 4,
+ LIST_OF_STYLES = 5,
+ STYLE_INFO = 6,
+ PASTE_TRANSFORMED = 7,
+ EDIT_VIEW = 8,
+ TANGENT_ARC = 9
};
typedef struct {
- int screen;
+ Screen screen;
hGroup group;
hStyle style;
hConstraint constraint;
- bool dimIsDistance;
- double dimFinish;
- int dimSteps;
struct {
int times;
} ShownState;
ShownState shown;
- enum {
- EDIT_NOTHING = 0,
+ enum class Edit : uint32_t {
+ NOTHING = 0,
// For multiple groups
- EDIT_TIMES_REPEATED = 1,
- EDIT_GROUP_NAME = 2,
- EDIT_GROUP_SCALE = 3,
- EDIT_GROUP_COLOR = 4,
- EDIT_GROUP_OPACITY = 5,
- // For the configuraiton screen
- EDIT_LIGHT_DIRECTION = 100,
- EDIT_LIGHT_INTENSITY = 101,
- EDIT_COLOR = 102,
- EDIT_CHORD_TOLERANCE = 103,
- EDIT_MAX_SEGMENTS = 104,
- EDIT_CAMERA_TANGENT = 105,
- EDIT_GRID_SPACING = 106,
- EDIT_DIGITS_AFTER_DECIMAL = 107,
- EDIT_EXPORT_SCALE = 108,
- EDIT_EXPORT_OFFSET = 109,
- EDIT_CANVAS_SIZE = 110,
- EDIT_G_CODE_DEPTH = 120,
- EDIT_G_CODE_PASSES = 121,
- EDIT_G_CODE_FEED = 122,
- EDIT_G_CODE_PLUNGE_FEED = 123,
- EDIT_AUTOSAVE_INTERVAL = 124,
+ TIMES_REPEATED = 1,
+ GROUP_NAME = 2,
+ GROUP_SCALE = 3,
+ GROUP_COLOR = 4,
+ GROUP_OPACITY = 5,
+ // For the configuration screen
+ LIGHT_DIRECTION = 100,
+ LIGHT_INTENSITY = 101,
+ COLOR = 102,
+ CHORD_TOLERANCE = 103,
+ MAX_SEGMENTS = 104,
+ CAMERA_TANGENT = 105,
+ GRID_SPACING = 106,
+ DIGITS_AFTER_DECIMAL = 107,
+ DIGITS_AFTER_DECIMAL_DEGREE = 108,
+ EXPORT_SCALE = 109,
+ EXPORT_OFFSET = 110,
+ CANVAS_SIZE = 111,
+ G_CODE_DEPTH = 112,
+ G_CODE_PASSES = 113,
+ G_CODE_FEED = 114,
+ G_CODE_PLUNGE_FEED = 115,
+ AUTOSAVE_INTERVAL = 116,
+ LIGHT_AMBIENT = 117,
+ FIND_CONSTRAINT_TIMEOUT = 118,
// For TTF text
- EDIT_TTF_TEXT = 300,
+ TTF_TEXT = 300,
// For the step dimension screen
- EDIT_STEP_DIM_FINISH = 400,
- EDIT_STEP_DIM_STEPS = 401,
+ STEP_DIM_FINISH = 400,
+ STEP_DIM_STEPS = 401,
// For the styles stuff
- EDIT_STYLE_WIDTH = 500,
- EDIT_STYLE_TEXT_HEIGHT = 501,
- EDIT_STYLE_TEXT_ANGLE = 502,
- EDIT_STYLE_COLOR = 503,
- EDIT_STYLE_FILL_COLOR = 504,
- EDIT_STYLE_NAME = 505,
- EDIT_BACKGROUND_COLOR = 506,
- EDIT_BACKGROUND_IMG_SCALE = 507,
- EDIT_STYLE_STIPPLE_PERIOD = 508,
+ STYLE_WIDTH = 500,
+ STYLE_TEXT_HEIGHT = 501,
+ STYLE_TEXT_ANGLE = 502,
+ STYLE_COLOR = 503,
+ STYLE_FILL_COLOR = 504,
+ STYLE_NAME = 505,
+ BACKGROUND_COLOR = 506,
+ STYLE_STIPPLE_PERIOD = 508,
// For paste transforming
- EDIT_PASTE_TIMES_REPEATED = 600,
- EDIT_PASTE_ANGLE = 601,
- EDIT_PASTE_SCALE = 602,
+ PASTE_TIMES_REPEATED = 600,
+ PASTE_ANGLE = 601,
+ PASTE_SCALE = 602,
// For view
- EDIT_VIEW_SCALE = 700,
- EDIT_VIEW_ORIGIN = 701,
- EDIT_VIEW_PROJ_RIGHT = 702,
- EDIT_VIEW_PROJ_UP = 703,
+ VIEW_SCALE = 700,
+ VIEW_ORIGIN = 701,
+ VIEW_PROJ_RIGHT = 702,
+ VIEW_PROJ_UP = 703,
// For tangent arc
- EDIT_TANGENT_ARC_RADIUS = 800
+ TANGENT_ARC_RADIUS = 800
};
struct {
bool showAgain;
- int meaning;
+ Edit meaning;
int i;
hGroup group;
hRequest request;
} colorPicker;
} editControl;
- void HideEditControl(void);
+ void HideEditControl();
void ShowEditControl(int col, const std::string &str, int halfRow = -1);
void ShowEditControlWithColorPicker(int col, RgbaColor rgb);
- void ClearSuper(void);
+ void ClearSuper();
void ShowHeader(bool withNav);
// These are self-contained screens, that show some information about
// the sketch.
- void ShowListOfGroups(void);
- void ShowGroupInfo(void);
- void ShowGroupSolveInfo(void);
- void ShowConfiguration(void);
- void ShowListOfStyles(void);
- void ShowStyleInfo(void);
- void ShowStepDimension(void);
- void ShowPasteTransformed(void);
- void ShowEditView(void);
- void ShowTangentArc(void);
+ void ShowListOfGroups();
+ void ShowGroupInfo();
+ void ShowGroupSolveInfo();
+ void ShowConfiguration();
+ void ShowListOfStyles();
+ void ShowStyleInfo();
+ void ShowStepDimension();
+ void ShowPasteTransformed();
+ void ShowEditView();
+ void ShowTangentArc();
// Special screen, based on selection
- void DescribeSelection(void);
+ void DescribeSelection();
- void GoToScreen(int screen);
+ void GoToScreen(Screen screen);
// All of these are callbacks from the GUI code; first from when
// we're describing an entity
static void ScreenUnselectAll(int link, uint32_t v);
// when we're describing a constraint
+ static void ScreenConstraintToggleReference(int link, uint32_t v);
static void ScreenConstraintShowAsRadius(int link, uint32_t v);
// and the rest from the stuff in textscreens.cpp
static void ScreenShowGroupsSpecial(int link, uint32_t v);
static void ScreenDeleteGroup(int link, uint32_t v);
- static void ScreenHoverConstraint(int link, uint32_t v);
+ static void ScreenHoverGroupWorkplane(int link, uint32_t v);
static void ScreenHoverRequest(int link, uint32_t v);
+ static void ScreenHoverEntity(int link, uint32_t v);
+ static void ScreenHoverConstraint(int link, uint32_t v);
static void ScreenSelectRequest(int link, uint32_t v);
+ static void ScreenSelectEntity(int link, uint32_t v);
static void ScreenSelectConstraint(int link, uint32_t v);
static void ScreenChangeGroupOption(int link, uint32_t v);
static void ScreenCreateCustomStyle(int link, uint32_t v);
static void ScreenLoadFactoryDefaultStyles(int link, uint32_t v);
static void ScreenAssignSelectionToStyle(int link, uint32_t v);
- static void ScreenBackgroundImage(int link, uint32_t v);
static void ScreenShowConfiguration(int link, uint32_t v);
static void ScreenShowEditView(int link, uint32_t v);
static void ScreenGoToWebsite(int link, uint32_t v);
static void ScreenChangeFixExportColors(int link, uint32_t v);
+ static void ScreenChangeExportBackgroundColor(int link, uint32_t v);
static void ScreenChangeBackFaces(int link, uint32_t v);
+ static void ScreenChangeShowContourAreas(int link, uint32_t v);
static void ScreenChangeCheckClosedContour(int link, uint32_t v);
+ static void ScreenChangeTurntableNav(int link, uint32_t v);
+ static void ScreenChangeImmediatelyEditDimension(int link, uint32_t v);
+ static void ScreenChangeAutomaticLineConstraints(int link, uint32_t v);
static void ScreenChangePwlCurves(int link, uint32_t v);
static void ScreenChangeCanvasSizeAuto(int link, uint32_t v);
static void ScreenChangeCanvasSize(int link, uint32_t v);
static void ScreenAllowRedundant(int link, uint32_t v);
+ struct {
+ bool isDistance;
+ double finish;
+ int steps;
+
+ Platform::TimerRef timer;
+ int64_t time;
+ int step;
+ } stepDim;
static void ScreenStepDimSteps(int link, uint32_t v);
static void ScreenStepDimFinish(int link, uint32_t v);
static void ScreenStepDimGo(int link, uint32_t v);
static void ScreenChangeGroupScale(int link, uint32_t v);
static void ScreenChangeLightDirection(int link, uint32_t v);
static void ScreenChangeLightIntensity(int link, uint32_t v);
+ static void ScreenChangeLightAmbient(int link, uint32_t v);
static void ScreenChangeColor(int link, uint32_t v);
static void ScreenChangeChordTolerance(int link, uint32_t v);
static void ScreenChangeMaxSegments(int link, uint32_t v);
static void ScreenChangeCameraTangent(int link, uint32_t v);
static void ScreenChangeGridSpacing(int link, uint32_t v);
static void ScreenChangeDigitsAfterDecimal(int link, uint32_t v);
+ static void ScreenChangeDigitsAfterDecimalDegree(int link, uint32_t v);
+ static void ScreenChangeUseSIPrefixes(int link, uint32_t v);
static void ScreenChangeExportScale(int link, uint32_t v);
static void ScreenChangeExportOffset(int link, uint32_t v);
static void ScreenChangeGCodeParameter(int link, uint32_t v);
static void ScreenChangeAutosaveInterval(int link, uint32_t v);
+ static void ScreenChangeFindConstraintTimeout(int link, uint32_t v);
static void ScreenChangeStyleName(int link, uint32_t v);
static void ScreenChangeStyleMetric(int link, uint32_t v);
static void ScreenChangeStyleTextAngle(int link, uint32_t v);
static void ScreenChangeStyleColor(int link, uint32_t v);
static void ScreenChangeBackgroundColor(int link, uint32_t v);
- static void ScreenChangeBackgroundImageScale(int link, uint32_t v);
static void ScreenChangePasteTransformed(int link, uint32_t v);
static void ScreenChangeViewScale(int link, uint32_t v);
+ static void ScreenChangeViewToFullScale(int link, uint32_t v);
static void ScreenChangeViewOrigin(int link, uint32_t v);
static void ScreenChangeViewProjection(int link, uint32_t v);
- bool EditControlDoneForStyles(const char *s);
- bool EditControlDoneForConfiguration(const char *s);
- bool EditControlDoneForPaste(const char *s);
- bool EditControlDoneForView(const char *s);
- void EditControlDone(const char *s);
+ bool EditControlDoneForStyles(const std::string &s);
+ bool EditControlDoneForConfiguration(const std::string &s);
+ bool EditControlDoneForPaste(const std::string &s);
+ bool EditControlDoneForView(const std::string &s);
+ void EditControlDone(std::string s);
};
-#define SELECTION_RADIUS 10.0
-
class GraphicsWindow {
public:
- void Init(void);
-
- // This table describes the top-level menus in the graphics winodw.
- typedef enum {
- // File
- MNU_NEW = 100,
- MNU_OPEN,
- MNU_OPEN_RECENT,
- MNU_SAVE,
- MNU_SAVE_AS,
- MNU_EXPORT_PNG,
- MNU_EXPORT_MESH,
- MNU_EXPORT_SURFACES,
- MNU_EXPORT_VIEW,
- MNU_EXPORT_SECTION,
- MNU_EXPORT_WIREFRAME,
- MNU_IMPORT,
- MNU_EXIT,
- // View
- MNU_ZOOM_IN,
- MNU_ZOOM_OUT,
- MNU_ZOOM_TO_FIT,
- MNU_SHOW_GRID,
- MNU_PERSPECTIVE_PROJ,
- MNU_ONTO_WORKPLANE,
- MNU_NEAREST_ORTHO,
- MNU_NEAREST_ISO,
- MNU_CENTER_VIEW,
- MNU_SHOW_MENU_BAR,
- MNU_SHOW_TOOLBAR,
- MNU_SHOW_TEXT_WND,
- MNU_UNITS_INCHES,
- MNU_UNITS_MM,
- MNU_FULL_SCREEN,
- // Edit
- MNU_UNDO,
- MNU_REDO,
- MNU_CUT,
- MNU_COPY,
- MNU_PASTE,
- MNU_PASTE_TRANSFORM,
- MNU_DELETE,
- MNU_SELECT_CHAIN,
- MNU_SELECT_ALL,
- MNU_SNAP_TO_GRID,
- MNU_ROTATE_90,
- MNU_UNSELECT_ALL,
- MNU_REGEN_ALL,
- // Request
- MNU_SEL_WORKPLANE,
- MNU_FREE_IN_3D,
- MNU_DATUM_POINT,
- MNU_WORKPLANE,
- MNU_LINE_SEGMENT,
- MNU_CONSTR_SEGMENT,
- MNU_CIRCLE,
- MNU_ARC,
- MNU_RECTANGLE,
- MNU_CUBIC,
- MNU_TTF_TEXT,
- MNU_SPLIT_CURVES,
- MNU_TANGENT_ARC,
- MNU_CONSTRUCTION,
- // Group
- MNU_GROUP_3D,
- MNU_GROUP_WRKPL,
- MNU_GROUP_EXTRUDE,
- MNU_GROUP_LATHE,
- MNU_GROUP_ROT,
- MNU_GROUP_TRANS,
- MNU_GROUP_LINK,
- MNU_GROUP_RECENT,
- // Constrain
- MNU_DISTANCE_DIA,
- MNU_REF_DISTANCE,
- MNU_ANGLE,
- MNU_REF_ANGLE,
- MNU_OTHER_ANGLE,
- MNU_REFERENCE,
- MNU_EQUAL,
- MNU_RATIO,
- MNU_DIFFERENCE,
- MNU_ON_ENTITY,
- MNU_SYMMETRIC,
- MNU_AT_MIDPOINT,
- MNU_HORIZONTAL,
- MNU_VERTICAL,
- MNU_PARALLEL,
- MNU_PERPENDICULAR,
- MNU_ORIENTED_SAME,
- MNU_WHERE_DRAGGED,
- MNU_COMMENT,
- // Analyze
- MNU_VOLUME,
- MNU_AREA,
- MNU_INTERFERENCE,
- MNU_NAKED_EDGES,
- MNU_SHOW_DOF,
- MNU_TRACE_PT,
- MNU_STOP_TRACING,
- MNU_STEP_DIM,
- // Help,
- MNU_WEBSITE,
- MNU_ABOUT
- } MenuId;
- typedef void MenuHandler(int id);
- enum {
- ESCAPE_KEY = 27,
- DELETE_KEY = 127,
- FUNCTION_KEY_BASE = 0xf0
- };
- enum {
- SHIFT_MASK = 0x100,
- CTRL_MASK = 0x200
- };
- enum MenuItemKind {
- MENU_ITEM_NORMAL = 0,
- MENU_ITEM_CHECK,
- MENU_ITEM_RADIO
- };
- typedef struct {
- int level; // 0 == on menu bar, 1 == one level down
- const char *label; // or NULL for a separator
- int id; // unique ID
- int accel; // keyboard accelerator
- MenuItemKind kind;
- MenuHandler *fn;
- } MenuEntry;
- static const MenuEntry menu[];
- static void MenuView(int id);
- static void MenuEdit(int id);
- static void MenuRequest(int id);
- void DeleteSelection(void);
- void CopySelection(void);
+ void Init();
+
+ Platform::WindowRef window;
+
+ void PopulateMainMenu();
+ void PopulateRecentFiles();
+
+ Platform::KeyboardEvent AcceleratorForCommand(Command id);
+ void ActivateCommand(Command id);
+
+ static void MenuView(Command id);
+ static void MenuEdit(Command id);
+ static void MenuRequest(Command id);
+ void DeleteSelection();
+ void CopySelection();
void PasteClipboard(Vector trans, double theta, double scale);
- static void MenuClipboard(int id);
+ static void MenuClipboard(Command id);
+
+ Platform::MenuRef openRecentMenu;
+ Platform::MenuRef linkRecentMenu;
+
+ Platform::MenuItemRef showGridMenuItem;
+ Platform::MenuItemRef perspectiveProjMenuItem;
+ Platform::MenuItemRef showToolbarMenuItem;
+ Platform::MenuItemRef showTextWndMenuItem;
+ Platform::MenuItemRef fullScreenMenuItem;
+
+ Platform::MenuItemRef unitsMmMenuItem;
+ Platform::MenuItemRef unitsMetersMenuItem;
+ Platform::MenuItemRef unitsInchesMenuItem;
+
+ Platform::MenuItemRef inWorkplaneMenuItem;
+ Platform::MenuItemRef in3dMenuItem;
+
+ Platform::MenuItemRef undoMenuItem;
+ Platform::MenuItemRef redoMenuItem;
+
+ std::shared_ptr<ViewportCanvas> canvas;
+ std::shared_ptr<BatchCanvas> persistentCanvas;
+ bool persistentDirty;
- // The width and height (in pixels) of the window.
- double width, height;
// These parameters define the map from 2d screen coordinates to the
// coordinates of the 3d sketch points. We will use an axonometric
// projection.
// allowing a paint in between. The extra solves are wasted if they're
// not displayed.
bool havePainted;
- // Similarly, don't draw edges and outlines, since that's too slow
- // for real-time dragging.
- bool isDegraded;
// Some state for the context menu.
struct {
bool active;
} context;
- void NormalizeProjectionVectors(void);
+ Camera GetCamera() const;
+ Lighting GetLighting() const;
+
+ void NormalizeProjectionVectors();
Point2d ProjectPoint(Vector p);
Vector ProjectPoint3(Vector p);
Vector ProjectPoint4(Vector p, double *w);
Vector UnProjectPoint(Point2d p);
Vector UnProjectPoint3(Vector p);
+
+ Platform::TimerRef animateTimer;
void AnimateOnto(Quaternion quatf, Vector offsetf);
- void AnimateOntoWorkplane(void);
+ void AnimateOntoWorkplane();
+
Vector VectorFromProjs(Vector rightUpForward);
void HandlePointForZoomToFit(Vector p, Point2d *pmax, Point2d *pmin,
- double *wmin, bool usePerspective);
- void LoopOverPoints(const std::vector<Entity *> &entity, const std::vector<hEntity> &faces, Point2d *pmax, Point2d *pmin,
- double *wmin, bool usePerspective, bool includeMesh);
- void ZoomToFit(bool includingInvisibles, bool useSelection = false);
+ double *wmin, bool usePerspective,
+ const Camera &camera);
+ void LoopOverPoints(const std::vector<Entity *> &entities,
+ const std::vector<Constraint *> &constraints,
+ const std::vector<hEntity> &faces,
+ Point2d *pmax, Point2d *pmin,
+ double *wmin, bool usePerspective, bool includeMesh,
+ const Camera &camera);
+ void ZoomToFit(bool includingInvisibles = false, bool useSelection = false);
+ double ZoomToFit(const Camera &camera,
+ bool includingInvisibles = false, bool useSelection = false);
hGroup activeGroup;
- void EnsureValidActives(void);
- bool LockedInWorkplane(void);
- void SetWorkplaneFreeIn3d(void);
- hEntity ActiveWorkplane(void);
- void ForceTextWindowShown(void);
+ void EnsureValidActives();
+ bool LockedInWorkplane();
+ void SetWorkplaneFreeIn3d();
+ hEntity ActiveWorkplane();
+ void ForceTextWindowShown();
// Operations that must be completed by doing something with the mouse
- // are noted here. These occupy the same space as the menu ids.
- enum {
- FIRST_PENDING = 0x0f000000,
- DRAGGING_POINTS = 0x0f000000,
- DRAGGING_NEW_POINT = 0x0f000001,
- DRAGGING_NEW_LINE_POINT = 0x0f000002,
- DRAGGING_NEW_CUBIC_POINT = 0x0f000003,
- DRAGGING_NEW_ARC_POINT = 0x0f000004,
- DRAGGING_CONSTRAINT = 0x0f000005,
- DRAGGING_RADIUS = 0x0f000006,
- DRAGGING_NORMAL = 0x0f000007,
- DRAGGING_NEW_RADIUS = 0x0f000008,
- DRAGGING_MARQUEE = 0x0f000009
- };
-
- enum SuggestedConstraint {
- SUGGESTED_NONE = 0,
- SUGGESTED_HORIZONTAL = Constraint::HORIZONTAL,
- SUGGESTED_VERTICAL = Constraint::VERTICAL,
+ // are noted here.
+ enum class Pending : uint32_t {
+ NONE = 0,
+ COMMAND = 1,
+ DRAGGING_POINTS = 2,
+ DRAGGING_NEW_POINT = 3,
+ DRAGGING_NEW_LINE_POINT = 4,
+ DRAGGING_NEW_CUBIC_POINT = 5,
+ DRAGGING_NEW_ARC_POINT = 6,
+ DRAGGING_CONSTRAINT = 7,
+ DRAGGING_RADIUS = 8,
+ DRAGGING_NORMAL = 9,
+ DRAGGING_NEW_RADIUS = 10,
+ DRAGGING_MARQUEE = 11,
};
struct {
- int operation;
+ Pending operation;
+ Command command;
hRequest request;
hEntity point;
List<hEntity> points;
+ List<hRequest> requests;
hEntity circle;
hEntity normal;
hConstraint constraint;
const char *description;
+ Platform::Path filename;
- SuggestedConstraint suggestion;
+ bool hasSuggestion;
+ Constraint::Type suggestion;
} pending;
- void ClearPending(void);
+ void ClearPending(bool scheduleShowTW = true);
+ bool IsFromPending(hRequest r);
+ void AddToPending(hRequest r);
+ void ReplacePending(hRequest before, hRequest after);
+
// The constraint that is being edited with the on-screen textbox.
hConstraint constraintBeingEdited;
- SuggestedConstraint SuggestLineConstraint(hRequest lineSegment);
+ bool SuggestLineConstraint(hRequest lineSegment, ConstraintBase::Type *type);
Vector SnapToGrid(Vector p);
- bool ConstrainPointByHovered(hEntity pt);
- void DeleteTaggedRequests(void);
- hRequest AddRequest(int type, bool rememberForUndo);
- hRequest AddRequest(int type);
+ Vector SnapToEntityByScreenPoint(Point2d pp, hEntity he);
+ bool ConstrainPointByHovered(hEntity pt, const Point2d *projected = NULL);
+ void DeleteTaggedRequests();
+ hRequest AddRequest(Request::Type type, bool rememberForUndo);
+ hRequest AddRequest(Request::Type type);
class ParametricCurve {
public:
void MakeFromEntity(hEntity he, bool reverse);
Vector PointAt(double t);
Vector TangentAt(double t);
- double LengthForAuto(void);
+ double LengthForAuto();
- hRequest CreateRequestTrimmedTo(double t, bool extraConstraints,
- hEntity orig, hEntity arc, bool arcFinish);
+ void CreateRequestTrimmedTo(double t, bool reuseOrig,
+ hEntity orig, hEntity arc, bool arcFinish, bool pointf);
void ConstrainPointIfCoincident(hEntity hpt);
};
- void MakeTangentArc(void);
- void SplitLinesOrCurves(void);
+ void MakeTangentArc();
+ void SplitLinesOrCurves();
hEntity SplitEntity(hEntity he, Vector pinter);
hEntity SplitLine(hEntity he, Vector pinter);
hEntity SplitCircle(hEntity he, Vector pinter);
void RemoveConstraintsForPointBeingDeleted(hEntity hpt);
void FixConstraintsForRequestBeingDeleted(hRequest hr);
void FixConstraintsForPointBeingDeleted(hEntity hpt);
+ void EditConstraint(hConstraint constraint);
- // The current selection.
+ // A selected entity.
class Selection {
public:
int tag;
hConstraint constraint;
bool emphasized;
- void Draw(void);
+ void Draw(bool isHovered, Canvas *canvas);
- void Clear(void);
- bool IsEmpty(void);
+ void Clear();
+ bool IsEmpty();
bool Equals(Selection *b);
- bool HasEndpoints(void);
+ bool HasEndpoints();
};
+
+ // A hovered entity, with its location relative to the cursor.
+ class Hover {
+ public:
+ int zIndex;
+ double distance;
+ double depth;
+ Selection selection;
+ };
+
+ List<Hover> hoverList;
Selection hover;
bool hoverWasSelectedOnMousedown;
List<Selection> selection;
+
+ Selection ChooseFromHoverToSelect();
+ Selection ChooseFromHoverToDrag();
void HitTestMakeSelection(Point2d mp);
- void ClearSelection(void);
- void ClearNonexistentSelectionItems(void);
+ void ClearSelection();
+ void ClearNonexistentSelectionItems();
+ /// This structure is filled by a call to GroupSelection().
struct {
std::vector<hEntity> point;
std::vector<hEntity> entity;
int stylables;
int constraintLabels;
int withEndpoints;
- int n;
+ int n; ///< Number of selected items
} gs;
- void GroupSelection(void);
+ void GroupSelection();
bool IsSelected(Selection *s);
bool IsSelected(hEntity he);
void MakeSelected(hEntity he);
void MakeSelected(Selection *s);
void MakeUnselected(hEntity he, bool coincidentPointTrick);
void MakeUnselected(Selection *s, bool coincidentPointTrick);
- void SelectByMarquee(void);
- void ClearSuper(void);
-
- enum {
- CMNU_UNSELECT_ALL = 0x100,
- CMNU_UNSELECT_HOVERED = 0x101,
- CMNU_CUT_SEL = 0x102,
- CMNU_COPY_SEL = 0x103,
- CMNU_PASTE = 0x104,
- CMNU_PASTE_XFRM = 0x105,
- CMNU_DELETE_SEL = 0x106,
- CMNU_SELECT_CHAIN = 0x107,
- CMNU_NEW_CUSTOM_STYLE = 0x110,
- CMNU_NO_STYLE = 0x111,
- CMNU_GROUP_INFO = 0x120,
- CMNU_STYLE_INFO = 0x121,
- CMNU_REFERENCE_DIM = 0x130,
- CMNU_OTHER_ANGLE = 0x131,
- CMNU_DEL_COINCIDENT = 0x132,
- CMNU_SNAP_TO_GRID = 0x140,
- CMNU_REMOVE_SPLINE_PT = 0x141,
- CMNU_ADD_SPLINE_PT = 0x142,
- CMNU_FIRST_STYLE = 0x40000000
- };
- void ContextMenuListStyles(void);
- int64_t contextMenuCancelTime;
+ void SelectByMarquee();
+ void ClearSuper();
// The toolbar, in toolbar.cpp
- bool ToolbarDrawOrHitTest(int x, int y, bool paint, int *menuHit);
- void ToolbarDraw(void);
+ bool ToolbarDrawOrHitTest(int x, int y, UiCanvas *canvas,
+ Command *hitCommand, int *hitX, int *hitY);
+ void ToolbarDraw(UiCanvas *canvas);
bool ToolbarMouseMoved(int x, int y);
bool ToolbarMouseDown(int x, int y);
- static void TimerCallback(void);
- int toolbarHovered;
- int toolbarTooltipped;
- int toolbarMouseX, toolbarMouseY;
+ Command toolbarHovered;
// This sets what gets displayed.
bool showWorkplanes;
bool showNormals;
bool showPoints;
+ bool showConstruction;
bool showConstraints;
bool showTextWindow;
bool showShaded;
bool showOutlines;
bool showFaces;
bool showMesh;
- bool showHdnLines;
void ToggleBool(bool *v);
+ enum class DrawOccludedAs { INVISIBLE, STIPPLED, VISIBLE };
+ DrawOccludedAs drawOccludedAs;
+
bool showSnapGrid;
+ void DrawSnapGrid(Canvas *canvas);
void AddPointToDraggedList(hEntity hp);
void StartDraggingByEntity(hEntity he);
- void StartDraggingBySelection(void);
+ void StartDraggingBySelection();
void UpdateDraggedNum(Vector *pos, double mx, double my);
void UpdateDraggedPoint(hEntity hp, double mx, double my);
- // These are called by the platform-specific code.
- void Paint(void);
+ void Invalidate(bool clearPersistent = false);
+ void DrawEntities(Canvas *canvas, bool persistent);
+ void DrawPersistent(Canvas *canvas);
+ void Draw(Canvas *canvas);
+ void Paint();
+
+ bool MouseEvent(Platform::MouseEvent event);
void MouseMoved(double x, double y, bool leftDown, bool middleDown,
- bool rightDown, bool shiftDown, bool ctrlDown);
- void MouseLeftDown(double x, double y);
- void MouseLeftUp(double x, double y);
+ bool rightDown, bool shiftDown, bool ctrlDown);
+ void MouseLeftDown(double x, double y, bool shiftDown, bool ctrlDown);
+ void MouseLeftUp(double x, double y, bool shiftDown, bool ctrlDown);
void MouseLeftDoubleClick(double x, double y);
void MouseMiddleOrRightDown(double x, double y);
void MouseRightUp(double x, double y);
void MouseScroll(double x, double y, int delta);
- void MouseLeave(void);
- bool KeyDown(int c);
- void EditControlDone(const char *s);
-
- int64_t lastSpaceNavigatorTime;
- hGroup lastSpaceNavigatorGroup;
- void SpaceNavigatorMoved(double tx, double ty, double tz,
- double rx, double ry, double rz, bool shiftDown);
- void SpaceNavigatorButtonUp(void);
+ void MouseLeave();
+ bool KeyboardEvent(Platform::KeyboardEvent event);
+ void EditControlDone(const std::string &s);
+
+ int64_t last6DofTime;
+ hGroup last6DofGroup;
+ void SixDofEvent(Platform::SixDofEvent event);
};
//-----------------------------------------------------------------------------
#include "solvespace.h"
-void SolveSpaceUI::UndoRemember(void) {
+void SolveSpaceUI::UndoRemember() {
unsaved = true;
PushFromCurrentOnto(&undo);
UndoClearStack(&redo);
UndoEnableMenus();
}
-void SolveSpaceUI::UndoUndo(void) {
+void SolveSpaceUI::UndoUndo() {
if(undo.cnt <= 0) return;
PushFromCurrentOnto(&redo);
UndoEnableMenus();
}
-void SolveSpaceUI::UndoRedo(void) {
+void SolveSpaceUI::UndoRedo() {
if(redo.cnt <= 0) return;
PushFromCurrentOnto(&undo);
UndoEnableMenus();
}
-void SolveSpaceUI::UndoEnableMenus(void) {
- EnableMenuById(GraphicsWindow::MNU_UNDO, undo.cnt > 0);
- EnableMenuById(GraphicsWindow::MNU_REDO, redo.cnt > 0);
+void SolveSpaceUI::UndoEnableMenus() {
+ SS.GW.undoMenuItem->SetEnabled(undo.cnt > 0);
+ SS.GW.redoMenuItem->SetEnabled(redo.cnt > 0);
}
void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
- int i;
-
if(uk->cnt == MAX_UNDO) {
UndoClearState(&(uk->d[uk->write]));
// And then write in to this one again
UndoState *ut = &(uk->d[uk->write]);
*ut = {};
- for(i = 0; i < SK.group.n; i++) {
- Group *src = &(SK.group.elem[i]);
- Group dest = *src;
+ ut->group.ReserveMore(SK.group.n);
+ for(Group &src : SK.group) {
+ // Shallow copy
+ Group dest(src);
// And then clean up all the stuff that needs to be a deep copy,
// and zero out all the dynamic stuff that will get regenerated.
dest.clean = false;
dest.thisShell = {};
dest.runningShell = {};
dest.displayMesh = {};
- dest.displayEdges = {};
dest.displayOutlines = {};
- dest.remap = {};
- src->remap.DeepCopyInto(&(dest.remap));
+ dest.remap = src.remap;
dest.impMesh = {};
dest.impShell = {};
dest.impEntity = {};
ut->group.Add(&dest);
}
- for(i = 0; i < SK.groupOrder.n; i++) {
- ut->groupOrder.Add(&(SK.groupOrder.elem[i]));
- }
- for(i = 0; i < SK.request.n; i++) {
- ut->request.Add(&(SK.request.elem[i]));
- }
- for(i = 0; i < SK.constraint.n; i++) {
- Constraint *src = &(SK.constraint.elem[i]);
- Constraint dest = *src;
- dest.dogd = {};
+ for(auto &src : SK.groupOrder) { ut->groupOrder.Add(&src); }
+ ut->request.ReserveMore(SK.request.n);
+ for(auto &src : SK.request) { ut->request.Add(&src); }
+ ut->constraint.ReserveMore(SK.constraint.n);
+ for(auto &src : SK.constraint) {
+ // Shallow copy
+ Constraint dest(src);
ut->constraint.Add(&dest);
}
- for(i = 0; i < SK.param.n; i++) {
- ut->param.Add(&(SK.param.elem[i]));
- }
- for(i = 0; i < SK.style.n; i++) {
- ut->style.Add(&(SK.style.elem[i]));
- }
+ ut->param.ReserveMore(SK.param.n);
+ for(auto &src : SK.param) { ut->param.Add(&src); }
+ ut->style.ReserveMore(SK.style.n);
+ for(auto &src : SK.style) { ut->style.Add(&src); }
ut->activeGroup = SS.GW.activeGroup;
uk->write = WRAP(uk->write + 1, MAX_UNDO);
}
void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
- int i;
-
- if(uk->cnt <= 0) oops();
+ ssassert(uk->cnt > 0, "Cannot pop from empty undo stack");
(uk->cnt)--;
uk->write = WRAP(uk->write - 1, MAX_UNDO);
UndoState *ut = &(uk->d[uk->write]);
// Free everything in the main copy of the program before replacing it
- for(i = 0; i < SK.groupOrder.n; i++) {
- Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
+ for(hGroup hg : SK.groupOrder) {
+ Group *g = SK.GetGroup(hg);
g->Clear();
}
SK.group.Clear();
// And then do a shallow copy of the state from the undo list
ut->group.MoveSelfInto(&(SK.group));
- for(i = 0; i < ut->groupOrder.n; i++)
- SK.groupOrder.Add(&ut->groupOrder.elem[i]);
+ for(auto &gh : ut->groupOrder) { SK.groupOrder.Add(&gh); }
ut->request.MoveSelfInto(&(SK.request));
ut->constraint.MoveSelfInto(&(SK.constraint));
ut->param.MoveSelfInto(&(SK.param));
// sketch just changed a lot.
SS.GW.ClearSuper();
SS.TW.ClearSuper();
- SS.ReloadAllImported();
- SS.GenerateAll(SolveSpaceUI::GENERATE_ALL);
+ SS.ReloadAllLinked(SS.saveFile);
+ SS.GenerateAll(SolveSpaceUI::Generate::ALL);
SS.ScheduleShowTW();
// Activate the group that was active before.
}
void SolveSpaceUI::UndoClearState(UndoState *ut) {
- int i;
- for(i = 0; i < ut->group.n; i++) {
- Group *g = &(ut->group.elem[i]);
-
- g->remap.Clear();
- }
+ for(auto &g : ut->group) { g.remap.clear(); }
ut->group.Clear();
ut->request.Clear();
ut->constraint.Clear();
+++ /dev/null
-//-----------------------------------------------------------------------------
-// Offscreen rendering in OpenGL using framebuffer objects.
-//
-// Copyright 2015 <whitequark@whitequark.org>
-//-----------------------------------------------------------------------------
-#ifdef __APPLE__
-#include <OpenGL/GL.h>
-#else
-#include <GL/glew.h>
-#endif
-
-#include "gloffscreen.h"
-#include "solvespace.h"
-
-GLOffscreen::GLOffscreen() : _pixels(NULL), _pixels_inv(NULL), _width(0), _height(0) {
-#ifndef __APPLE__
- if(glewInit() != GLEW_OK)
- oops();
-#endif
-
- if(!GL_EXT_framebuffer_object)
- oops();
-
- glGenFramebuffersEXT(1, &_framebuffer);
- glGenRenderbuffersEXT(1, &_color_renderbuffer);
- glGenRenderbuffersEXT(1, &_depth_renderbuffer);
-}
-
-GLOffscreen::~GLOffscreen() {
- delete[] _pixels;
- delete[] _pixels_inv;
- glDeleteRenderbuffersEXT(1, &_depth_renderbuffer);
- glDeleteRenderbuffersEXT(1, &_color_renderbuffer);
- glDeleteFramebuffersEXT(1, &_framebuffer);
-}
-
-bool GLOffscreen::begin(int width, int height) {
- if(_width != width || _height != height) {
- delete[] _pixels;
- delete[] _pixels_inv;
-
- _pixels = new uint32_t[width * height * 4];
- _pixels_inv = new uint32_t[width * height * 4];
-
- _width = width;
- _height = height;
- }
-
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _framebuffer);
-
- glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _color_renderbuffer);
- glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, _width, _height);
- glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
- GL_RENDERBUFFER_EXT, _color_renderbuffer);
-
- glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _depth_renderbuffer);
- glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, _width, _height);
- glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
- GL_RENDERBUFFER_EXT, _depth_renderbuffer);
-
- if(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT)
- return true;
-
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
- return false;
-}
-
-uint8_t *GLOffscreen::end(bool flip) {
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- glReadPixels(0, 0, _width, _height,
- GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, _pixels_inv);
-#else
- glReadPixels(0, 0, _width, _height,
- GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, _pixels_inv);
-#endif
-
- if(flip) {
- /* in OpenGL coordinates, bottom is zero Y */
- for(int i = 0; i < _height; i++)
- memcpy(&_pixels[_width * i], &_pixels_inv[_width * (_height - i - 1)], _width * 4);
- }
-
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
-
- return (uint8_t*) (flip ? _pixels : _pixels_inv);
-}
+++ /dev/null
-//-----------------------------------------------------------------------------
-// Offscreen rendering in OpenGL using framebuffer objects.
-//
-// Copyright 2015 <whitequark@whitequark.org>
-//-----------------------------------------------------------------------------
-#ifndef __GLOFFSCREEN_H
-#define __GLOFFSCREEN_H
-
-#include <stdint.h>
-
-class GLOffscreen {
-public:
- /* these allocate and deallocate OpenGL resources.
- an OpenGL context /must/ be current. */
- GLOffscreen();
- ~GLOffscreen();
-
- /* prepare for drawing a frame of specified size.
- returns true if OpenGL likes our configuration, false
- otherwise. if it returns false, the OpenGL state is restored. */
- bool begin(int width, int height);
-
- /* get pixels out of the frame and restore OpenGL state.
- the pixel format is ARGB32 with top row at index 0 if
- flip is true and bottom row at index 0 if flip is false.
- the returned array is valid until the next call to begin() */
- uint8_t *end(bool flip = true);
-
-private:
- unsigned int _framebuffer;
- unsigned int _color_renderbuffer, _depth_renderbuffer;
- uint32_t *_pixels, *_pixels_inv;
- int _width, _height;
-};
-
-#endif
+++ /dev/null
-/* XPM */
-static char *solvespace_16x16[] = {
-/* columns rows colors chars-per-pixel */
-"16 16 5 1 ",
-" c black",
-". c #1ED500",
-"X c #DE00D6",
-"o c #CBCBCB",
-"O c None",
-/* pixels */
-"OOO OOOOOOOOOOOO",
-"OOO OOOOOOOOOOOO",
-"OOO OOOOOOOOOOOO",
-"OOO OOOOOXOOOOOO",
-"OOO OOOOOXoOOOOO",
-"OOO OOOOOXoOOOOO",
-"OOO OOOOOXoOOOOO",
-"OOO OOOOOXoOOOOO",
-"OOO OOOOOXoOOOOO",
-"OOO OOXXXXXXXOOO",
-"OOO OOOoooooooOO",
-"OO...OOOOOOOOOOO",
-" ... ",
-"OO...OOOOOOOOOOO",
-"OOO OOOOOOOOOOOO",
-"OOO OOOOOOOOOOOO"
-};
+++ /dev/null
-/* XPM */
-static char *solvespace_24x24[] = {
-/* columns rows colors chars-per-pixel */
-"24 24 5 1 ",
-" c black",
-". c #1ED500",
-"X c #DE00D6",
-"o c #CBCBCB",
-"O c None",
-/* pixels */
-"OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOO OOOOOOOOOOOOOOOO",
-"OOOOOOO OOOOOOOOOOOOOOOO",
-"OOOOOOO OOOOOOOOOOOOOOOO",
-"OOOOOOO OOOOOXOOOOOOOOOO",
-"OOOOOOO OOOOOXoOOOOOOOOO",
-"OOOOOOO OOOOOXoOOOOOOOOO",
-"OOOOOOO OOOOOXoOOOOOOOOO",
-"OOOOOOO OOOOOXoOOOOOOOOO",
-"OOOOOOO OOOOOXoOOOOOOOOO",
-"OOOOOOO OOXXXXXXXOOOOOOO",
-"OOOOOOO OOOoooooooOOOOOO",
-"OOOOOO...OOOOOOOOOOOOOOO",
-"OOOO ... OOOO",
-"OOOOOO...OOOOOOOOOOOOOOO",
-"OOOOOOO OOOOOOOOOOOOOOOO",
-"OOOOOOO OOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOO"
-};
+++ /dev/null
-/* XPM */
-static char *solvespace_32x32[] = {
-/* columns rows colors chars-per-pixel */
-"32 32 5 1 ",
-" c black",
-". c #1ED500",
-"X c #DE00D6",
-"o c #CBCBCB",
-"O c None",
-/* pixels */
-"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOXXOOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
-"OOOOOO OOOOXXXXXXXXXXXXOOOOOOOO",
-"OOOOOO OOOOXXXXXXXXXXXXOOOOOOOO",
-"OOOOOO OOOOOooooooooooooOOOOOOO",
-"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
-"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
-" ...... ",
-" ...... ",
-"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
-"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO"
-};
+++ /dev/null
-/* XPM */
-static char *solvespace_48x48[] = {
-/* columns rows colors chars-per-pixel */
-"48 48 5 1 ",
-" c black",
-". c #1ED500",
-"X c #DE00D6",
-"o c #CBCBCB",
-"O c None",
-/* pixels */
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOXXOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOXXXXXXXXXXXXOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOXXXXXXXXXXXXOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOooooooooooooOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOO ...... OOOOOOOO",
-"OOOOOOOO ...... OOOOOOOO",
-"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
-"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO"
-};
+++ /dev/null
-[Desktop Entry]
-Version=1.0
-Name=SolveSpace
-Comment=A parametric 2d/3d CAD
-Exec=/usr/bin/solvespace
-Icon=solvespace
-Type=Application
-Categories=Graphics
-Keywords=parametric;cad;2d;3d;
+++ /dev/null
-//-----------------------------------------------------------------------------
-// Utility functions used by the Unix port. Notably, our memory allocation;
-// we use two separate allocators, one for long-lived stuff and one for
-// stuff that gets freed after every regeneration of the model, to save us
-// the trouble of freeing the latter explicitly.
-//
-// Copyright 2008-2013 Jonathan Westhues.
-// Copyright 2013 Daniel Richard G. <skunk@iSKUNK.ORG>
-//-----------------------------------------------------------------------------
-#include <time.h>
-
-#include "solvespace.h"
-
-namespace SolveSpace {
-
-void dbp(const char *str, ...)
-{
- va_list f;
- static char buf[1024*50];
- va_start(f, str);
- vsnprintf(buf, sizeof(buf), str, f);
- va_end(f);
-
- fputs(buf, stderr);
- fputc('\n', stderr);
-}
-
-FILE *ssfopen(const std::string &filename, const char *mode)
-{
- if(filename.length() != strlen(filename.c_str())) oops();
- return fopen(filename.c_str(), mode);
-}
-
-void ssremove(const std::string &filename)
-{
- if(filename.length() != strlen(filename.c_str())) oops();
- remove(filename.c_str());
-}
-
-int64_t GetUnixTime(void)
-{
- time_t ret;
- time(&ret);
- return (int64_t)ret;
-}
-
-//-----------------------------------------------------------------------------
-// A separate heap, on which we allocate expressions. Maybe a bit faster,
-// since fragmentation is less of a concern, and it also makes it possible
-// to be sloppy with our memory management, and just free everything at once
-// at the end.
-//-----------------------------------------------------------------------------
-
-typedef struct _AllocTempHeader AllocTempHeader;
-
-typedef struct _AllocTempHeader {
- AllocTempHeader *prev;
- AllocTempHeader *next;
-} AllocTempHeader;
-
-static AllocTempHeader *Head = NULL;
-
-void *AllocTemporary(size_t n)
-{
- AllocTempHeader *h =
- (AllocTempHeader *)malloc(n + sizeof(AllocTempHeader));
- h->prev = NULL;
- h->next = Head;
- if(Head) Head->prev = h;
- Head = h;
- memset(&h[1], 0, n);
- return (void *)&h[1];
-}
-
-void FreeTemporary(void *p)
-{
- AllocTempHeader *h = (AllocTempHeader *)p - 1;
- if(h->prev) {
- h->prev->next = h->next;
- } else {
- Head = h->next;
- }
- if(h->next) h->next->prev = h->prev;
- free(h);
-}
-
-void FreeAllTemporary(void)
-{
- AllocTempHeader *h = Head;
- while(h) {
- AllocTempHeader *f = h;
- h = h->next;
- free(f);
- }
- Head = NULL;
-}
-
-void *MemAlloc(size_t n) {
- void *p = malloc(n);
- if(!p) oops();
- return p;
-}
-
-void MemFree(void *p) {
- free(p);
-}
-
-void InitHeaps(void) {
- /* nothing to do */
-}
-
-};
//-----------------------------------------------------------------------------
#include "solvespace.h"
+void SolveSpace::AssertFailure(const char *file, unsigned line, const char *function,
+ const char *condition, const char *message) {
+ std::string formattedMsg;
+ formattedMsg += ssprintf("File %s, line %u, function %s:\n", file, line, function);
+ formattedMsg += ssprintf("Assertion failed: %s.\n", condition);
+ formattedMsg += ssprintf("Message: %s.\n", message);
+ SolveSpace::Platform::FatalError(formattedMsg);
+}
+
std::string SolveSpace::ssprintf(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
int size = vsnprintf(NULL, 0, fmt, va);
- if(size < 0) oops();
+ ssassert(size >= 0, "vsnprintf could not encode string");
va_end(va);
std::string result;
- result.resize(size);
+ result.resize(size + 1);
va_start(va, fmt);
vsnprintf(&result[0], size + 1, fmt, va);
va_end(va);
+ result.resize(size);
return result;
}
return result;
}
-bool SolveSpace::FilenameHasExtension(const std::string &str, const char *ext)
-{
- int i, ls = str.length(), le = strlen(ext);
-
- if(ls < le) return false;
-
- for(i = 0; i < le; i++) {
- if(tolower(ext[le-i-1]) != tolower(str[ls-i-1])) {
- return false;
- }
- }
- return true;
-}
-
-bool SolveSpace::ReadFile(const std::string &filename, std::string *data)
-{
- FILE *f = ssfopen(filename.c_str(), "rb");
- if(f == NULL)
- return false;
-
- fseek(f, 0, SEEK_END);
- data->resize(ftell(f));
- fseek(f, 0, SEEK_SET);
- fread(&(*data)[0], 1, data->size(), f);
- fclose(f);
-
- return true;
-}
-
-bool SolveSpace::WriteFile(const std::string &filename, const std::string &data)
+int64_t SolveSpace::GetMilliseconds()
{
- FILE *f = ssfopen(filename.c_str(), "wb");
- if(f == NULL)
- return false;
-
- fwrite(&data[0], 1, data.size(), f);
- fclose(f);
-
- return true;
+ auto timestamp = std::chrono::steady_clock::now().time_since_epoch();
+ return std::chrono::duration_cast<std::chrono::milliseconds>(timestamp).count();
}
void SolveSpace::MakeMatrix(double *mat,
mat[15] = a44;
}
+void SolveSpace::MultMatrix(double *mata, double *matb, double *matr) {
+ for(int i = 0; i < 4; i++) {
+ for(int j = 0; j < 4; j++) {
+ double s = 0.0;
+ for(int k = 0; k < 4; k++) {
+ s += mata[k * 4 + j] * matb[i * 4 + k];
+ }
+ matr[i * 4 + j] = s;
+ }
+ }
+}
+
//-----------------------------------------------------------------------------
-// Word-wrap the string for our message box appropriately, and then display
+// Format the string for our message box appropriately, and then display
// that string.
//-----------------------------------------------------------------------------
-static void DoStringForMessageBox(const char *str, va_list f, bool error)
+static void MessageBox(const char *fmt, va_list va, bool error,
+ std::function<void()> onDismiss = std::function<void()>())
{
- char inBuf[1024*50];
- vsprintf(inBuf, str, f);
-
- char outBuf[1024*50];
- int i = 0, j = 0, len = 0, longestLen = 47;
- int rows = 0, cols = 0;
-
- // Count the width of the longest line that starts with spaces; those
- // are list items, that should not be split in the middle.
- bool listLine = false;
- while(inBuf[i]) {
- if(inBuf[i] == '\r') {
- // ignore these
- } else if(inBuf[i] == ' ' && len == 0) {
- listLine = true;
- } else if(inBuf[i] == '\n') {
- if(listLine) longestLen = max(longestLen, len);
- len = 0;
- } else {
- len++;
+#ifndef LIBRARY
+ va_list va_size;
+ va_copy(va_size, va);
+ int size = vsnprintf(NULL, 0, fmt, va_size);
+ ssassert(size >= 0, "vsnprintf could not encode string");
+ va_end(va_size);
+
+ std::string text;
+ text.resize(size);
+
+ vsnprintf(&text[0], size + 1, fmt, va);
+
+ // Split message text using a heuristic for better presentation.
+ size_t separatorAt = 0;
+ while(separatorAt != std::string::npos) {
+ size_t dotAt = text.find('.', separatorAt + 1),
+ colonAt = text.find(':', separatorAt + 1);
+ separatorAt = min(dotAt, colonAt);
+ if(separatorAt == std::string::npos ||
+ (separatorAt + 1 < text.size() && isspace(text[separatorAt + 1]))) {
+ break;
+ }
+ }
+ std::string message = text;
+ std::string description;
+ if(separatorAt != std::string::npos) {
+ message = text.substr(0, separatorAt + 1);
+ if(separatorAt + 1 < text.size()) {
+ description = text.substr(separatorAt + 1);
}
- i++;
}
- if(listLine) longestLen = max(longestLen, len);
-
- // Word wrap according to our target line length longestLen.
- len = 0;
- i = 0;
- while(inBuf[i]) {
- if(inBuf[i] == '\r') {
- // ignore these
- } else if(inBuf[i] == '\n') {
- outBuf[j++] = '\n';
- if(len == 0) rows++;
- len = 0;
- } else if(inBuf[i] == ' ' && len > longestLen) {
- outBuf[j++] = '\n';
- len = 0;
+
+ if(description.length() > 0) {
+ std::string::iterator it = description.begin();
+ while(isspace(*it)) it++;
+ description = description.substr(it - description.begin());
+ }
+
+ Platform::MessageDialogRef dialog = CreateMessageDialog(SS.GW.window);
+ if (!dialog) {
+ if (error) {
+ fprintf(stderr, "Error: %s\n", message.c_str());
} else {
- outBuf[j++] = inBuf[i];
- // Count rows when we draw the first character; so an empty
- // row doesn't end up counting.
- if(len == 0) rows++;
- len++;
+ fprintf(stderr, "Message: %s\n", message.c_str());
}
- cols = max(cols, len);
- i++;
+ if(onDismiss) {
+ onDismiss();
+ }
+ return;
+ }
+ using Platform::MessageDialog;
+ if(error) {
+ dialog->SetType(MessageDialog::Type::ERROR);
+ } else {
+ dialog->SetType(MessageDialog::Type::INFORMATION);
+ }
+ dialog->SetTitle(error ? C_("title", "Error") : C_("title", "Message"));
+ dialog->SetMessage(message);
+ if(!description.empty()) {
+ dialog->SetDescription(description);
}
- outBuf[j++] = '\0';
+ dialog->AddButton(C_("button", "&OK"), MessageDialog::Response::OK,
+ /*isDefault=*/true);
- // And then display the text with our actual longest line length.
- DoMessageBox(outBuf, rows, cols, error);
+ dialog->onResponse = [=](MessageDialog::Response _response) {
+ if(onDismiss) {
+ onDismiss();
+ }
+ };
+ dialog->ShowModal();
+#endif
}
-void SolveSpace::Error(const char *str, ...)
+void SolveSpace::Error(const char *fmt, ...)
{
va_list f;
- va_start(f, str);
- DoStringForMessageBox(str, f, true);
+ va_start(f, fmt);
+ MessageBox(fmt, f, /*error=*/true);
va_end(f);
}
-void SolveSpace::Message(const char *str, ...)
+void SolveSpace::Message(const char *fmt, ...)
{
va_list f;
- va_start(f, str);
- DoStringForMessageBox(str, f, false);
+ va_start(f, fmt);
+ MessageBox(fmt, f, /*error=*/false);
+ va_end(f);
+}
+void SolveSpace::MessageAndRun(std::function<void()> onDismiss, const char *fmt, ...)
+{
+ va_list f;
+ va_start(f, fmt);
+ MessageBox(fmt, f, /*error=*/false, onDismiss);
va_end(f);
}
-
-void SolveSpace::CnfFreezeBool(bool v, const std::string &name)
- { CnfFreezeInt(v ? 1 : 0, name); }
-
-void SolveSpace::CnfFreezeColor(RgbaColor v, const std::string &name)
- { CnfFreezeInt(v.ToPackedInt(), name); }
-
-bool SolveSpace::CnfThawBool(bool v, const std::string &name)
- { return CnfThawInt(v ? 1 : 0, name) != 0; }
-
-RgbaColor SolveSpace::CnfThawColor(RgbaColor v, const std::string &name)
- { return RgbaColor::FromPackedInt(CnfThawInt(v.ToPackedInt(), name)); }
//-----------------------------------------------------------------------------
// Solve a mostly banded matrix. In a given row, there are LEFT_OF_DIAG
// There also may be elements in the last two columns of any row. We solve
// without pivoting.
//-----------------------------------------------------------------------------
-void BandedMatrix::Solve(void) {
+void BandedMatrix::Solve() {
int i, ip, j, jp;
double temp;
return q.WithMagnitude(1);
}
-Quaternion Quaternion::Plus(Quaternion b) {
+Quaternion Quaternion::Plus(Quaternion b) const {
Quaternion q;
q.w = w + b.w;
q.vx = vx + b.vx;
return q;
}
-Quaternion Quaternion::Minus(Quaternion b) {
+Quaternion Quaternion::Minus(Quaternion b) const {
Quaternion q;
q.w = w - b.w;
q.vx = vx - b.vx;
return q;
}
-Quaternion Quaternion::ScaledBy(double s) {
+Quaternion Quaternion::ScaledBy(double s) const {
Quaternion q;
q.w = w*s;
q.vx = vx*s;
return q;
}
-double Quaternion::Magnitude(void) {
+double Quaternion::Magnitude() const {
return sqrt(w*w + vx*vx + vy*vy + vz*vz);
}
-Quaternion Quaternion::WithMagnitude(double s) {
+Quaternion Quaternion::WithMagnitude(double s) const {
return ScaledBy(s/Magnitude());
}
-Vector Quaternion::RotationU(void) {
+Vector Quaternion::RotationU() const {
Vector v;
v.x = w*w + vx*vx - vy*vy - vz*vz;
v.y = 2*w *vz + 2*vx*vy;
return v;
}
-Vector Quaternion::RotationV(void) {
+Vector Quaternion::RotationV() const {
Vector v;
v.x = 2*vx*vy - 2*w*vz;
v.y = w*w - vx*vx + vy*vy - vz*vz;
return v;
}
-Vector Quaternion::RotationN(void) {
+Vector Quaternion::RotationN() const {
Vector v;
v.x = 2*w*vy + 2*vx*vz;
v.y = 2*vy*vz - 2*w*vx;
return v;
}
-Vector Quaternion::Rotate(Vector p) {
+Vector Quaternion::Rotate(Vector p) const {
// Express the point in the new basis
return (RotationU().ScaledBy(p.x)).Plus(
RotationV().ScaledBy(p.y)).Plus(
RotationN().ScaledBy(p.z));
}
-Quaternion Quaternion::Inverse(void) {
+Quaternion Quaternion::Inverse() const {
Quaternion r;
r.w = w;
r.vx = -vx;
return r.WithMagnitude(1); // not that the normalize should be reqd
}
-Quaternion Quaternion::ToThe(double p) {
+Quaternion Quaternion::ToThe(double p) const {
// Avoid division by zero, or arccos of something not in its domain
if(w >= (1 - 1e-6)) {
return From(1, 0, 0, 0);
return r;
}
-Quaternion Quaternion::Times(Quaternion b) {
+Quaternion Quaternion::Times(Quaternion b) const {
double sa = w, sb = b.w;
Vector va = { vx, vy, vz };
Vector vb = { b.vx, b.vy, b.vz };
return r;
}
-Quaternion Quaternion::Mirror(void) {
+Quaternion Quaternion::Mirror() const {
Vector u = RotationU(),
v = RotationV();
u = u.ScaledBy(-1);
}
-Vector Vector::From(double x, double y, double z) {
- Vector v;
- v.x = x; v.y = y; v.z = z;
- return v;
-}
-
Vector Vector::From(hParam x, hParam y, hParam z) {
Vector v;
v.x = SK.GetParam(x)->val;
return v;
}
-double Vector::Element(int i) {
- switch(i) {
- case 0: return x;
- case 1: return y;
- case 2: return z;
- default: oops();
- }
-}
-
-bool Vector::Equals(Vector v, double tol) {
- // Quick axis-aligned tests before going further
- double dx = v.x - x; if(dx < -tol || dx > tol) return false;
- double dy = v.y - y; if(dy < -tol || dy > tol) return false;
- double dz = v.z - z; if(dz < -tol || dz > tol) return false;
-
- return (this->Minus(v)).MagSquared() < tol*tol;
-}
-
-bool Vector::EqualsExactly(Vector v) {
+bool Vector::EqualsExactly(Vector v) const {
return EXACT(x == v.x &&
y == v.y &&
z == v.z);
}
-Vector Vector::Plus(Vector b) {
- Vector r;
-
- r.x = x + b.x;
- r.y = y + b.y;
- r.z = z + b.z;
-
- return r;
-}
-
-Vector Vector::Minus(Vector b) {
- Vector r;
-
- r.x = x - b.x;
- r.y = y - b.y;
- r.z = z - b.z;
-
- return r;
-}
-
-Vector Vector::Negated(void) {
- Vector r;
-
- r.x = -x;
- r.y = -y;
- r.z = -z;
-
- return r;
-}
-
-Vector Vector::Cross(Vector b) {
- Vector r;
-
- r.x = -(z*b.y) + (y*b.z);
- r.y = (z*b.x) - (x*b.z);
- r.z = -(y*b.x) + (x*b.y);
-
- return r;
-}
-
-double Vector::Dot(Vector b) {
- return (x*b.x + y*b.y + z*b.z);
-}
-
-double Vector::DirectionCosineWith(Vector b) {
+double Vector::DirectionCosineWith(Vector b) const {
Vector a = this->WithMagnitude(1);
b = b.WithMagnitude(1);
return a.Dot(b);
}
-Vector Vector::Normal(int which) {
+Vector Vector::Normal(int which) const {
Vector n;
// Arbitrarily choose one vector that's normal to us, pivoting
// That's the vector we return.
} else if(which == 1) {
n = this->Cross(n);
- } else oops();
+ } else ssassert(false, "Unexpected vector normal index");
n = n.WithMagnitude(1);
return n;
}
-Vector Vector::RotatedAbout(Vector orig, Vector axis, double theta) {
+Vector Vector::RotatedAbout(Vector orig, Vector axis, double theta) const {
Vector r = this->Minus(orig);
r = r.RotatedAbout(axis, theta);
return r.Plus(orig);
}
-Vector Vector::RotatedAbout(Vector axis, double theta) {
+Vector Vector::RotatedAbout(Vector axis, double theta) const {
double c = cos(theta);
double s = sin(theta);
return r;
}
-Vector Vector::DotInToCsys(Vector u, Vector v, Vector n) {
+Vector Vector::DotInToCsys(Vector u, Vector v, Vector n) const {
Vector r = {
this->Dot(u),
this->Dot(v),
return r;
}
-Vector Vector::ScaleOutOfCsys(Vector u, Vector v, Vector n) {
+Vector Vector::ScaleOutOfCsys(Vector u, Vector v, Vector n) const {
Vector r = u.ScaledBy(x).Plus(
v.ScaledBy(y).Plus(
n.ScaledBy(z)));
}
Vector Vector::InPerspective(Vector u, Vector v, Vector n,
- Vector origin, double cameraTan)
+ Vector origin, double cameraTan) const
{
Vector r = this->Minus(origin);
r = r.DotInToCsys(u, v, n);
return r;
}
-double Vector::DistanceToLine(Vector p0, Vector dp) {
+double Vector::DistanceToLine(Vector p0, Vector dp) const {
double m = dp.Magnitude();
return ((this->Minus(p0)).Cross(dp)).Magnitude() / m;
}
-bool Vector::OnLineSegment(Vector a, Vector b, double tol) {
+double Vector::DistanceToPlane(Vector normal, Vector origin) const {
+ return this->Dot(normal) - origin.Dot(normal);
+}
+
+bool Vector::OnLineSegment(Vector a, Vector b, double tol) const {
if(this->Equals(a, tol) || this->Equals(b, tol)) return true;
Vector d = b.Minus(a);
if(distsq >= tol*tol) return false;
- double t = (this->Minus(a)).DivPivoting(d);
+ double t = (this->Minus(a)).DivProjected(d);
// On-endpoint already tested
if(t < 0 || t > 1) return false;
return true;
}
-Vector Vector::ClosestPointOnLine(Vector p0, Vector dp) {
+Vector Vector::ClosestPointOnLine(Vector p0, Vector dp) const {
dp = dp.WithMagnitude(1);
// this, p0, and (p0+dp) define a plane; the min distance is in
// that plane, so calculate its normal
return this->Plus(n.WithMagnitude(d));
}
-double Vector::MagSquared(void) {
- return x*x + y*y + z*z;
-}
-
-double Vector::Magnitude(void) {
- return sqrt(x*x + y*y + z*z);
-}
-
-Vector Vector::ScaledBy(double v) {
- Vector r;
-
- r.x = x * v;
- r.y = y * v;
- r.z = z * v;
-
- return r;
-}
-
-Vector Vector::WithMagnitude(double v) {
+Vector Vector::WithMagnitude(double v) const {
double m = Magnitude();
if(EXACT(m == 0)) {
// We can do a zero vector with zero magnitude, but not any other cases.
}
}
-Vector Vector::ProjectVectorInto(hEntity wrkpl) {
+Vector Vector::ProjectVectorInto(hEntity wrkpl) const {
EntityBase *w = SK.GetEntity(wrkpl);
Vector u = w->Normal()->NormalU();
Vector v = w->Normal()->NormalV();
return (u.ScaledBy(up)).Plus(v.ScaledBy(vp));
}
-Vector Vector::ProjectInto(hEntity wrkpl) {
+Vector Vector::ProjectInto(hEntity wrkpl) const {
EntityBase *w = SK.GetEntity(wrkpl);
Vector p0 = w->WorkplaneGetOffset();
return p0.Plus(f.ProjectVectorInto(wrkpl));
}
-Point2d Vector::Project2d(Vector u, Vector v) {
+Point2d Vector::Project2d(Vector u, Vector v) const {
Point2d p;
p.x = this->Dot(u);
p.y = this->Dot(v);
return p;
}
-Point2d Vector::ProjectXy(void) {
+Point2d Vector::ProjectXy() const {
Point2d p;
p.x = x;
p.y = y;
return p;
}
-Vector4 Vector::Project4d(void) {
+Vector4 Vector::Project4d() const {
return Vector4::From(1, x, y, z);
}
-double Vector::DivPivoting(Vector delta) {
- double mx = fabs(delta.x), my = fabs(delta.y), mz = fabs(delta.z);
-
- if(mx > my && mx > mz) {
- return x/delta.x;
- } else if(my > mz) {
- return y/delta.y;
- } else {
- return z/delta.z;
- }
+double Vector::DivProjected(Vector delta) const {
+ return (x*delta.x + y*delta.y + z*delta.z)
+ / (delta.x*delta.x + delta.y*delta.y + delta.z*delta.z);
}
-Vector Vector::ClosestOrtho(void) {
+Vector Vector::ClosestOrtho() const {
double mx = fabs(x), my = fabs(y), mz = fabs(z);
if(mx > my && mx > mz) {
}
}
-Vector Vector::ClampWithin(double minv, double maxv) {
+Vector Vector::ClampWithin(double minv, double maxv) const {
Vector ret = *this;
if(ret.x < minv) ret.x = minv;
return ret;
}
-void Vector::MakeMaxMin(Vector *maxv, Vector *minv) {
- maxv->x = max(maxv->x, x);
- maxv->y = max(maxv->y, y);
- maxv->z = max(maxv->z, z);
-
- minv->x = min(minv->x, x);
- minv->y = min(minv->y, y);
- minv->z = min(minv->z, z);
-}
-
-bool Vector::OutsideAndNotOn(Vector maxv, Vector minv) {
+bool Vector::OutsideAndNotOn(Vector maxv, Vector minv) const {
return (x > maxv.x + LENGTH_EPS) || (x < minv.x - LENGTH_EPS) ||
(y > maxv.y + LENGTH_EPS) || (y < minv.y - LENGTH_EPS) ||
(z > maxv.z + LENGTH_EPS) || (z < minv.z - LENGTH_EPS);
}
bool Vector::BoundingBoxIntersectsLine(Vector amax, Vector amin,
- Vector p0, Vector p1, bool segment)
+ Vector p0, Vector p1, bool asSegment)
{
Vector dp = p1.Minus(p0);
double lp = dp.Magnitude();
double t = (d - p0.Element(i)) / dp.Element(i);
Vector p = p0.Plus(dp.ScaledBy(t));
- if(segment && (t < -LENGTH_EPS || t > (lp+LENGTH_EPS))) continue;
+ if(asSegment && (t < -LENGTH_EPS || t > (lp+LENGTH_EPS))) continue;
if(p.Element(j) > amax.Element(j) + LENGTH_EPS) continue;
if(p.Element(k) > amax.Element(k) + LENGTH_EPS) continue;
return Vector::From(detx/det, dety/det, detz/det);
}
+size_t VectorHash::operator()(const Vector &v) const {
+ const size_t size = (size_t)pow(std::numeric_limits<size_t>::max(), 1.0 / 3.0) - 1;
+ const double eps = 4.0 * LENGTH_EPS;
+
+ double x = fabs(v.x) / eps;
+ double y = fabs(v.y) / eps;
+ double z = fabs(v.y) / eps;
+
+ size_t xs = size_t(fmod(x, (double)size));
+ size_t ys = size_t(fmod(y, (double)size));
+ size_t zs = size_t(fmod(z, (double)size));
+
+ return (zs * size + ys) * size + xs;
+}
+
+bool VectorPred::operator()(Vector a, Vector b) const {
+ return a.Equals(b, LENGTH_EPS);
+}
+
Vector4 Vector4::From(double w, double x, double y, double z) {
Vector4 ret;
ret.w = w;
return (a.ScaledBy(1 - t)).Plus(b.ScaledBy(t));
}
-Vector4 Vector4::Plus(Vector4 b) {
+Vector4 Vector4::Plus(Vector4 b) const {
return Vector4::From(w + b.w, x + b.x, y + b.y, z + b.z);
}
-Vector4 Vector4::Minus(Vector4 b) {
+Vector4 Vector4::Minus(Vector4 b) const {
return Vector4::From(w - b.w, x - b.x, y - b.y, z - b.z);
}
-Vector4 Vector4::ScaledBy(double s) {
+Vector4 Vector4::ScaledBy(double s) const {
return Vector4::From(w*s, x*s, y*s, z*s);
}
-Vector Vector4::PerspectiveProject(void) {
+Vector Vector4::PerspectiveProject() const {
return Vector::From(x / w, y / w, z / w);
}
return { x, y };
}
+Point2d Point2d::FromPolar(double r, double a) {
+ return { r * cos(a), r * sin(a) };
+}
+
+double Point2d::Angle() const {
+ double a = atan2(y, x);
+ return M_PI + remainder(a - M_PI, 2 * M_PI);
+}
+
+double Point2d::AngleTo(const Point2d &p) const {
+ return p.Minus(*this).Angle();
+}
+
Point2d Point2d::Plus(const Point2d &b) const {
return { x + b.x, y + b.y };
}
return { x * s, y * s };
}
-double Point2d::DivPivoting(Point2d delta) const {
- if(fabs(delta.x) > fabs(delta.y)) {
- return x/delta.x;
- } else {
- return y/delta.y;
- }
+double Point2d::DivProjected(Point2d delta) const {
+ return (x*delta.x + y*delta.y) / (delta.x*delta.x + delta.y*delta.y);
}
-double Point2d::MagSquared(void) const {
+double Point2d::MagSquared() const {
return x*x + y*y;
}
-double Point2d::Magnitude(void) const {
+double Point2d::Magnitude() const {
return sqrt(x*x + y*y);
}
return x*p.x + y*p.y;
}
-double Point2d::DistanceToLine(const Point2d &p0, const Point2d &dp, bool segment) const {
+double Point2d::DistanceToLine(const Point2d &p0, const Point2d &dp, bool asSegment) const {
double m = dp.x*dp.x + dp.y*dp.y;
if(m < LENGTH_EPS*LENGTH_EPS) return VERY_POSITIVE;
// Let our line be p = p0 + t*dp, for a scalar t from 0 to 1
double t = (dp.x*(x - p0.x) + dp.y*(y - p0.y))/m;
- if((t < 0 || t > 1) && segment) {
- // The closest point is one of the endpoints; determine which.
- double d0 = DistanceTo(p0);
- double d1 = DistanceTo(p0.Plus(dp));
+ if(asSegment) {
+ if(t < 0.0) return DistanceTo(p0);
+ if(t > 1.0) return DistanceTo(p0.Plus(dp));
+ }
+ Point2d closest = p0.Plus(dp.ScaledBy(t));
+ return DistanceTo(closest);
+}
- return min(d1, d0);
- } else {
- Point2d closest = p0.Plus(dp.ScaledBy(t));
- return DistanceTo(closest);
+double Point2d::DistanceToLineSigned(const Point2d &p0, const Point2d &dp, bool asSegment) const {
+ double m = dp.x*dp.x + dp.y*dp.y;
+ if(m < LENGTH_EPS*LENGTH_EPS) return VERY_POSITIVE;
+
+ Point2d n = dp.Normal().WithMagnitude(1.0);
+ double dist = n.Dot(*this) - n.Dot(p0);
+ if(asSegment) {
+ // Let our line be p = p0 + t*dp, for a scalar t from 0 to 1
+ double t = (dp.x*(x - p0.x) + dp.y*(y - p0.y))/m;
+ double sign = (dist > 0.0) ? 1.0 : -1.0;
+ if(t < 0.0) return DistanceTo(p0) * sign;
+ if(t > 1.0) return DistanceTo(p0.Plus(dp)) * sign;
}
+
+ return dist;
}
-Point2d Point2d::Normal(void) const {
+Point2d Point2d::Normal() const {
return { y, -x };
}
return bbox;
}
-Vector BBox::GetOrigin() { return minp.Plus(maxp.Minus(minp)).ScaledBy(0.5); }
-Vector BBox::GetExtents() { return maxp.Minus(minp).ScaledBy(0.5); }
+Vector BBox::GetOrigin() const { return minp.Plus(maxp.Minus(minp).ScaledBy(0.5)); }
+Vector BBox::GetExtents() const { return maxp.Minus(minp).ScaledBy(0.5); }
void BBox::Include(const Vector &v, double r) {
minp.x = min(minp.x, v.x - r);
maxp.z = max(maxp.z, v.z + r);
}
-bool BBox::Overlaps(BBox &b1) {
-
+bool BBox::Overlaps(const BBox &b1) const {
Vector t = b1.GetOrigin().Minus(GetOrigin());
Vector e = b1.GetExtents().Plus(GetExtents());
return fabs(t.x) < e.x && fabs(t.y) < e.y && fabs(t.z) < e.z;
}
-bool BBox::Contains(const Point2d &p) {
- return p.x >= minp.x && p.y >= minp.y && p.x <= maxp.x && p.y <= maxp.y;
+bool BBox::Contains(const Point2d &p, double r) const {
+ return p.x >= (minp.x - r) &&
+ p.y >= (minp.y - r) &&
+ p.x <= (maxp.x + r) &&
+ p.y <= (maxp.y + r);
+}
+
+const std::vector<double>& SolveSpace::StipplePatternDashes(StipplePattern pattern) {
+ static bool initialized;
+ static std::vector<double> dashes[(size_t)StipplePattern::LAST + 1];
+ if(!initialized) {
+ // Inkscape ignores all elements that are exactly zero instead of drawing
+ // them as dots, so set those to 1e-6.
+ dashes[(size_t)StipplePattern::CONTINUOUS] =
+ {};
+ dashes[(size_t)StipplePattern::SHORT_DASH] =
+ { 1.0, 2.0 };
+ dashes[(size_t)StipplePattern::DASH] =
+ { 1.0, 1.0 };
+ dashes[(size_t)StipplePattern::DASH_DOT] =
+ { 1.0, 0.5, 1e-6, 0.5 };
+ dashes[(size_t)StipplePattern::DASH_DOT_DOT] =
+ { 1.0, 0.5, 1e-6, 0.5, 1e-6, 0.5 };
+ dashes[(size_t)StipplePattern::DOT] =
+ { 1e-6, 0.5 };
+ dashes[(size_t)StipplePattern::LONG_DASH] =
+ { 2.0, 0.5 };
+ dashes[(size_t)StipplePattern::FREEHAND] =
+ { 1.0, 2.0 };
+ dashes[(size_t)StipplePattern::ZIGZAG] =
+ { 1.0, 2.0 };
+ }
+
+ return dashes[(size_t)pattern];
+}
+
+double SolveSpace::StipplePatternLength(StipplePattern pattern) {
+ static bool initialized;
+ static double lengths[(size_t)StipplePattern::LAST + 1];
+ if(!initialized) {
+ for(size_t i = 0; i < (size_t)StipplePattern::LAST; i++) {
+ const std::vector<double> &dashes = StipplePatternDashes((StipplePattern)i);
+ double length = 0.0;
+ for(double dash : dashes) {
+ length += dash;
+ }
+ lengths[i] = length;
+ }
+ }
+
+ return lengths[(size_t)pattern];
}
//-----------------------------------------------------------------------------
#include "solvespace.h"
-void TextWindow::ShowEditView(void) {
+void TextWindow::ShowEditView() {
Printf(true, "%Ft3D VIEW PARAMETERS%E");
Printf(true, "%Bd %Ftoverall scale factor%E");
SS.GW.scale * SS.MmPerUnit(),
SS.UnitName(),
&ScreenChangeViewScale);
+ Printf(false, "%Bd %Fl%Ll%fset to full scale%E",
+ &ScreenChangeViewToFullScale);
Printf(false, "");
Printf(false, "%Bd %Ftorigin (maps to center of screen)%E");
}
void TextWindow::ScreenChangeViewScale(int link, uint32_t v) {
- SS.TW.edit.meaning = EDIT_VIEW_SCALE;
+ SS.TW.edit.meaning = Edit::VIEW_SCALE;
SS.TW.ShowEditControl(3, ssprintf("%.3f", SS.GW.scale * SS.MmPerUnit()));
}
+void TextWindow::ScreenChangeViewToFullScale(int link, uint32_t v) {
+ SS.GW.scale = SS.GW.window->GetPixelDensity() / 25.4;
+}
+
void TextWindow::ScreenChangeViewOrigin(int link, uint32_t v) {
std::string edit_value =
ssprintf("%s, %s, %s",
SS.MmToString(-SS.GW.offset.y).c_str(),
SS.MmToString(-SS.GW.offset.z).c_str());
- SS.TW.edit.meaning = EDIT_VIEW_ORIGIN;
+ SS.TW.edit.meaning = Edit::VIEW_ORIGIN;
SS.TW.ShowEditControl(3, edit_value);
}
void TextWindow::ScreenChangeViewProjection(int link, uint32_t v) {
std::string edit_value =
ssprintf("%.3f, %.3f, %.3f", CO(SS.GW.projRight));
- SS.TW.edit.meaning = EDIT_VIEW_PROJ_RIGHT;
+ SS.TW.edit.meaning = Edit::VIEW_PROJ_RIGHT;
SS.TW.ShowEditControl(10, edit_value);
}
-bool TextWindow::EditControlDoneForView(const char *s) {
+bool TextWindow::EditControlDoneForView(const std::string &s) {
switch(edit.meaning) {
- case EDIT_VIEW_SCALE: {
- Expr *e = Expr::From(s, true);
+ case Edit::VIEW_SCALE: {
+ Expr *e = Expr::From(s, /*popUpError=*/true);
if(e) {
double v = e->Eval() / SS.MmPerUnit();
if(v > LENGTH_EPS) {
SS.GW.scale = v;
} else {
- Error("Scale cannot be zero or negative.");
+ Error(_("Scale cannot be zero or negative."));
}
}
break;
}
- case EDIT_VIEW_ORIGIN: {
+ case Edit::VIEW_ORIGIN: {
Vector pt;
- if(sscanf(s, "%lf, %lf, %lf", &pt.x, &pt.y, &pt.z) == 3) {
+ if(sscanf(s.c_str(), "%lf, %lf, %lf", &pt.x, &pt.y, &pt.z) == 3) {
pt = pt.ScaledBy(SS.MmPerUnit());
SS.GW.offset = pt.ScaledBy(-1);
} else {
- Error("Bad format: specify x, y, z");
+ Error(_("Bad format: specify x, y, z"));
}
break;
}
- case EDIT_VIEW_PROJ_RIGHT:
- case EDIT_VIEW_PROJ_UP: {
+ case Edit::VIEW_PROJ_RIGHT:
+ case Edit::VIEW_PROJ_UP: {
Vector pt;
- if(sscanf(s, "%lf, %lf, %lf", &pt.x, &pt.y, &pt.z) != 3) {
- Error("Bad format: specify x, y, z");
+ if(sscanf(s.c_str(), "%lf, %lf, %lf", &pt.x, &pt.y, &pt.z) != 3) {
+ Error(_("Bad format: specify x, y, z"));
break;
}
- if(edit.meaning == EDIT_VIEW_PROJ_RIGHT) {
+ if(edit.meaning == Edit::VIEW_PROJ_RIGHT) {
SS.GW.projRight = pt;
SS.GW.NormalizeProjectionVectors();
- edit.meaning = EDIT_VIEW_PROJ_UP;
+ edit.meaning = Edit::VIEW_PROJ_UP;
HideEditControl();
ShowEditControl(10, ssprintf("%.3f, %.3f, %.3f", CO(SS.GW.projUp)),
editControl.halfRow + 2);
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
-<assemblyIdentity
- version="1.0.0.0"
- processorArchitecture="X86"
- name="JonathanWesthues.3dCAD.SolveSpace"
- type="win32"
-/>
-<description>Parametric 3d CAD tool.</description>
-<dependency>
- <dependentAssembly>
- <assemblyIdentity
- type="win32"
- name="Microsoft.Windows.Common-Controls"
- version="6.0.0.0"
- processorArchitecture="X86"
- publicKeyToken="6595b64144ccf1df"
- language="*"
- />
- </dependentAssembly>
-</dependency>
-</assembly>
+++ /dev/null
-
-// we need a manifest if we want visual styles; put in numbers since somethings a bit screwy
-// with my SDK install (I don't think I've got *.rh right)
-1 24 "manifest.xml"
-
-4000 ICON "icon.ico"
+++ /dev/null
-//-----------------------------------------------------------------------------
-// Our WinMain() functions, and Win32-specific stuff to set up our windows
-// and otherwise handle our interface to the operating system. Everything
-// outside win32/... should be standard C++ and gl.
-//
-// Copyright 2008-2013 Jonathan Westhues.
-//-----------------------------------------------------------------------------
-#include <time.h>
-#include <windows.h>
-#include <shellapi.h>
-#include <commctrl.h>
-#include <commdlg.h>
-
-#include "solvespace.h"
-#include "config.h"
-
-#ifdef HAVE_SPACEWARE
-# include <si.h>
-# include <siapp.h>
-# undef uint32_t // thanks but no thanks
-#endif
-
-HINSTANCE Instance;
-
-HWND TextWnd;
-HWND TextWndScrollBar;
-HWND TextEditControl;
-HGLRC TextGl;
-
-HWND GraphicsWnd;
-HGLRC GraphicsGl;
-HWND GraphicsEditControl;
-static struct {
- int x, y;
-} LastMousePos;
-
-HMENU SubMenus[100];
-HMENU RecentOpenMenu, RecentImportMenu;
-
-HMENU ContextMenu, ContextSubmenu;
-
-int ClientIsSmallerBy;
-
-HFONT FixedFont;
-
-#ifdef HAVE_SPACEWARE
-// The 6-DOF input device.
-SiHdl SpaceNavigator = SI_NO_HANDLE;
-#endif
-
-//-----------------------------------------------------------------------------
-// Routines to display message boxes on screen. Do our own, instead of using
-// MessageBox, because that is not consistent from version to version and
-// there's word wrap problems.
-//-----------------------------------------------------------------------------
-
-HWND MessageWnd, OkButton;
-bool MessageDone;
-int MessageWidth, MessageHeight;
-const char *MessageString;
-
-static LRESULT CALLBACK MessageProc(HWND hwnd, UINT msg, WPARAM wParam,
- LPARAM lParam)
-{
- switch (msg) {
- case WM_COMMAND:
- if((HWND)lParam == OkButton && wParam == BN_CLICKED) {
- MessageDone = true;
- }
- break;
-
- case WM_CLOSE:
- case WM_DESTROY:
- MessageDone = true;
- break;
-
- case WM_PAINT: {
- PAINTSTRUCT ps;
- HDC hdc = BeginPaint(hwnd, &ps);
- SelectObject(hdc, FixedFont);
- SetTextColor(hdc, 0x000000);
- SetBkMode(hdc, TRANSPARENT);
- RECT rc;
- SetRect(&rc, 10, 10, MessageWidth, MessageHeight);
- std::wstring text = Widen(MessageString);
- DrawText(hdc, text.c_str(), text.length(), &rc, DT_LEFT | DT_WORDBREAK);
- EndPaint(hwnd, &ps);
- break;
- }
-
- default:
- return DefWindowProc(hwnd, msg, wParam, lParam);
- }
-
- return 1;
-}
-
-HWND CreateWindowClient(DWORD exStyle, const wchar_t *className, const wchar_t *windowName,
- DWORD style, int x, int y, int width, int height, HWND parent,
- HMENU menu, HINSTANCE instance, void *param)
-{
- HWND h = CreateWindowExW(exStyle, className, windowName, style, x, y,
- width, height, parent, menu, instance, param);
-
- RECT r;
- GetClientRect(h, &r);
- width = width - (r.right - width);
- height = height - (r.bottom - height);
-
- SetWindowPos(h, HWND_TOP, x, y, width, height, 0);
-
- return h;
-}
-
-void SolveSpace::DoMessageBox(const char *str, int rows, int cols, bool error)
-{
- EnableWindow(GraphicsWnd, false);
- EnableWindow(TextWnd, false);
-
- // Register the window class for our dialog.
- WNDCLASSEX wc = {};
- wc.cbSize = sizeof(wc);
- wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC;
- wc.lpfnWndProc = (WNDPROC)MessageProc;
- wc.hInstance = Instance;
- wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
- wc.lpszClassName = L"MessageWnd";
- wc.lpszMenuName = NULL;
- wc.hCursor = LoadCursor(NULL, IDC_ARROW);
- wc.hIcon = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
- IMAGE_ICON, 32, 32, 0);
- wc.hIconSm = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
- IMAGE_ICON, 16, 16, 0);
- RegisterClassEx(&wc);
-
- // Create the window.
- MessageString = str;
- RECT r;
- GetWindowRect(GraphicsWnd, &r);
- const char *title = error ? "SolveSpace - Error" : "SolveSpace - Message";
- int width = cols*SS.TW.CHAR_WIDTH + 20,
- height = rows*SS.TW.LINE_HEIGHT + 60;
- MessageWidth = width;
- MessageHeight = height;
- MessageWnd = CreateWindowClient(0, L"MessageWnd", Widen(title).c_str(),
- WS_OVERLAPPED | WS_SYSMENU,
- r.left + 100, r.top + 100, width, height, NULL, NULL, Instance, NULL);
-
- OkButton = CreateWindowExW(0, WC_BUTTON, L"OK",
- WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON,
- (width - 70)/2, rows*SS.TW.LINE_HEIGHT + 20,
- 70, 25, MessageWnd, NULL, Instance, NULL);
- SendMessage(OkButton, WM_SETFONT, (WPARAM)FixedFont, true);
-
- ShowWindow(MessageWnd, true);
- SetFocus(OkButton);
-
- MSG msg;
- DWORD ret;
- MessageDone = false;
- while((ret = GetMessage(&msg, NULL, 0, 0)) != 0 && !MessageDone) {
- if((msg.message == WM_KEYDOWN &&
- (msg.wParam == VK_RETURN ||
- msg.wParam == VK_ESCAPE)) ||
- (msg.message == WM_KEYUP &&
- (msg.wParam == VK_SPACE)))
- {
- MessageDone = true;
- break;
- }
-
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- MessageString = NULL;
- EnableWindow(TextWnd, true);
- EnableWindow(GraphicsWnd, true);
- SetForegroundWindow(GraphicsWnd);
- DestroyWindow(MessageWnd);
-}
-
-void SolveSpace::AddContextMenuItem(const char *label, int id)
-{
- if(!ContextMenu) ContextMenu = CreatePopupMenu();
-
- if(id == CONTEXT_SUBMENU) {
- AppendMenuW(ContextMenu, MF_STRING | MF_POPUP,
- (UINT_PTR)ContextSubmenu, Widen(label).c_str());
- ContextSubmenu = NULL;
- } else {
- HMENU m = ContextSubmenu ? ContextSubmenu : ContextMenu;
- if(id == CONTEXT_SEPARATOR) {
- AppendMenuW(m, MF_SEPARATOR, 0, L"");
- } else {
- AppendMenuW(m, MF_STRING, id, Widen(label).c_str());
- }
- }
-}
-
-void SolveSpace::CreateContextSubmenu(void)
-{
- ContextSubmenu = CreatePopupMenu();
-}
-
-int SolveSpace::ShowContextMenu(void)
-{
- POINT p;
- GetCursorPos(&p);
- int r = TrackPopupMenu(ContextMenu,
- TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_TOPALIGN,
- p.x, p.y, 0, GraphicsWnd, NULL);
-
- DestroyMenu(ContextMenu);
- ContextMenu = NULL;
- return r;
-}
-
-void CALLBACK TimerCallback(HWND hwnd, UINT msg, UINT_PTR id, DWORD time)
-{
- // The timer is periodic, so needs to be killed explicitly.
- KillTimer(GraphicsWnd, 1);
- SS.GW.TimerCallback();
- SS.TW.TimerCallback();
-}
-void SolveSpace::SetTimerFor(int milliseconds)
-{
- SetTimer(GraphicsWnd, 1, milliseconds, TimerCallback);
-}
-
-void SolveSpace::ScheduleLater()
-{
-}
-
-static void CALLBACK AutosaveCallback(HWND hwnd, UINT msg, UINT_PTR id, DWORD time)
-{
- KillTimer(GraphicsWnd, 1);
- SS.Autosave();
-}
-
-void SolveSpace::SetAutosaveTimerFor(int minutes)
-{
- SetTimer(GraphicsWnd, 2, minutes * 60 * 1000, AutosaveCallback);
-}
-
-static void GetWindowSize(HWND hwnd, int *w, int *h)
-{
- RECT r;
- GetClientRect(hwnd, &r);
- *w = r.right - r.left;
- *h = r.bottom - r.top;
-}
-void SolveSpace::GetGraphicsWindowSize(int *w, int *h)
-{
- GetWindowSize(GraphicsWnd, w, h);
-}
-void SolveSpace::GetTextWindowSize(int *w, int *h)
-{
- GetWindowSize(TextWnd, w, h);
-}
-
-void SolveSpace::OpenWebsite(const char *url) {
- ShellExecuteW(GraphicsWnd, L"open", Widen(url).c_str(), NULL, NULL, SW_SHOWNORMAL);
-}
-
-void SolveSpace::ExitNow(void) {
- PostQuitMessage(0);
-}
-
-//-----------------------------------------------------------------------------
-// Helpers so that we can read/write registry keys from the platform-
-// independent code.
-//-----------------------------------------------------------------------------
-inline int CLAMP(int v, int a, int b) {
- // Clamp it to the range [a, b]
- if(v <= a) return a;
- if(v >= b) return b;
- return v;
-}
-
-static HKEY GetRegistryKey()
-{
- HKEY Software;
- if(RegOpenKeyExW(HKEY_CURRENT_USER, L"Software", 0,
- KEY_ALL_ACCESS, &Software) != ERROR_SUCCESS)
- return NULL;
-
- HKEY SolveSpace;
- if(RegCreateKeyExW(Software, L"SolveSpace", 0, NULL, 0,
- KEY_ALL_ACCESS, NULL, &SolveSpace, NULL) != ERROR_SUCCESS)
- return NULL;
-
- RegCloseKey(Software);
-
- return SolveSpace;
-}
-
-void SolveSpace::CnfFreezeInt(uint32_t val, const std::string &name)
-{
- HKEY SolveSpace = GetRegistryKey();
- RegSetValueExW(SolveSpace, &Widen(name)[0], 0,
- REG_DWORD, (const BYTE*) &val, sizeof(DWORD));
- RegCloseKey(SolveSpace);
-}
-void SolveSpace::CnfFreezeFloat(float val, const std::string &name)
-{
- static_assert(sizeof(float) == sizeof(DWORD),
- "sizes of float and DWORD must match");
- HKEY SolveSpace = GetRegistryKey();
- RegSetValueExW(SolveSpace, &Widen(name)[0], 0,
- REG_DWORD, (const BYTE*) &val, sizeof(DWORD));
- RegCloseKey(SolveSpace);
-}
-void SolveSpace::CnfFreezeString(const std::string &str, const std::string &name)
-{
- HKEY SolveSpace = GetRegistryKey();
- std::wstring strW = Widen(str);
- RegSetValueExW(SolveSpace, &Widen(name)[0], 0,
- REG_SZ, (const BYTE*) &strW[0], (strW.length() + 1) * 2);
- RegCloseKey(SolveSpace);
-}
-static void FreezeWindowPos(HWND hwnd, const std::string &name)
-{
- RECT r;
- GetWindowRect(hwnd, &r);
- CnfFreezeInt(r.left, name + "_left");
- CnfFreezeInt(r.right, name + "_right");
- CnfFreezeInt(r.top, name + "_top");
- CnfFreezeInt(r.bottom, name + "_bottom");
-
- CnfFreezeInt(IsZoomed(hwnd), name + "_maximized");
-}
-
-uint32_t SolveSpace::CnfThawInt(uint32_t val, const std::string &name)
-{
- HKEY SolveSpace = GetRegistryKey();
- DWORD type, newval, len = sizeof(DWORD);
- LONG result = RegQueryValueEx(SolveSpace, &Widen(name)[0], NULL,
- &type, (BYTE*) &newval, &len);
- RegCloseKey(SolveSpace);
-
- if(result == ERROR_SUCCESS && type == REG_DWORD)
- return newval;
- else
- return val;
-}
-float SolveSpace::CnfThawFloat(float val, const std::string &name)
-{
- HKEY SolveSpace = GetRegistryKey();
- DWORD type, len = sizeof(DWORD);
- float newval;
- LONG result = RegQueryValueExW(SolveSpace, &Widen(name)[0], NULL,
- &type, (BYTE*) &newval, &len);
- RegCloseKey(SolveSpace);
-
- if(result == ERROR_SUCCESS && type == REG_DWORD)
- return newval;
- else
- return val;
-}
-std::string SolveSpace::CnfThawString(const std::string &val, const std::string &name)
-{
- HKEY SolveSpace = GetRegistryKey();
- DWORD type, len;
- if(RegQueryValueExW(SolveSpace, &Widen(name)[0], NULL,
- &type, NULL, &len) != ERROR_SUCCESS || type != REG_SZ) {
- RegCloseKey(SolveSpace);
- return val;
- }
-
- std::wstring newval;
- newval.resize(len / 2 - 1);
- if(RegQueryValueExW(SolveSpace, &Widen(name)[0], NULL,
- NULL, (BYTE*) &newval[0], &len) != ERROR_SUCCESS) {
- RegCloseKey(SolveSpace);
- return val;
- }
-
- RegCloseKey(SolveSpace);
- return Narrow(newval);
-}
-static void ThawWindowPos(HWND hwnd, const std::string &name)
-{
- RECT r;
- GetWindowRect(hwnd, &r);
- r.left = CnfThawInt(r.left, name + "_left");
- r.right = CnfThawInt(r.right, name + "_right");
- r.top = CnfThawInt(r.top, name + "_top");
- r.bottom = CnfThawInt(r.bottom, name + "_bottom");
-
- HMONITOR hMonitor = MonitorFromRect(&r, MONITOR_DEFAULTTONEAREST);;
- MONITORINFO mi;
- mi.cbSize = sizeof(mi);
- GetMonitorInfo(hMonitor, &mi);
-
- // If it somehow ended up off-screen, then put it back.
- RECT dr = mi.rcMonitor;
- r.left = CLAMP(r.left, dr.left, dr.right);
- r.right = CLAMP(r.right, dr.left, dr.right);
- r.top = CLAMP(r.top, dr.top, dr.bottom);
- r.bottom = CLAMP(r.bottom, dr.top, dr.bottom);
- MoveWindow(hwnd, r.left, r.top, r.right - r.left, r.bottom - r.top, TRUE);
-
- if(CnfThawInt(FALSE, name + "_maximized"))
- ShowWindow(hwnd, SW_MAXIMIZE);
-}
-
-void SolveSpace::SetCurrentFilename(const std::string &filename) {
- if(!filename.empty()) {
- SetWindowTextW(GraphicsWnd, Widen("SolveSpace - " + filename).c_str());
- } else {
- SetWindowTextW(GraphicsWnd, L"SolveSpace - (not yet saved)");
- }
-}
-
-void SolveSpace::SetMousePointerToHand(bool yes) {
- SetCursor(LoadCursor(NULL, yes ? IDC_HAND : IDC_ARROW));
-}
-
-static void PaintTextWnd(HDC hdc)
-{
- wglMakeCurrent(GetDC(TextWnd), TextGl);
-
- SS.TW.Paint();
- SwapBuffers(GetDC(TextWnd));
-
- // Leave the graphics window context active, except when we're painting
- // this text window.
- wglMakeCurrent(GetDC(GraphicsWnd), GraphicsGl);
-}
-
-void SolveSpace::MoveTextScrollbarTo(int pos, int maxPos, int page)
-{
- SCROLLINFO si = {};
- si.cbSize = sizeof(si);
- si.fMask = SIF_DISABLENOSCROLL | SIF_ALL;
- si.nMin = 0;
- si.nMax = maxPos;
- si.nPos = pos;
- si.nPage = page;
- SetScrollInfo(TextWndScrollBar, SB_CTL, &si, true);
-}
-
-void HandleTextWindowScrollBar(WPARAM wParam, LPARAM lParam)
-{
- int maxPos, minPos, pos;
- GetScrollRange(TextWndScrollBar, SB_CTL, &minPos, &maxPos);
- pos = GetScrollPos(TextWndScrollBar, SB_CTL);
-
- switch(LOWORD(wParam)) {
- case SB_LINEUP: pos--; break;
- case SB_PAGEUP: pos -= 4; break;
-
- case SB_LINEDOWN: pos++; break;
- case SB_PAGEDOWN: pos += 4; break;
-
- case SB_TOP: pos = 0; break;
-
- case SB_BOTTOM: pos = maxPos; break;
-
- case SB_THUMBTRACK:
- case SB_THUMBPOSITION: pos = HIWORD(wParam); break;
- }
-
- SS.TW.ScrollbarEvent(pos);
-}
-
-static void MouseWheel(int thisDelta) {
- static int DeltaAccum;
- int delta = 0;
- // Handle mouse deltas of less than 120 (like from an un-detented mouse
- // wheel) correctly, even though no one ever uses those.
- DeltaAccum += thisDelta;
- while(DeltaAccum >= 120) {
- DeltaAccum -= 120;
- delta += 120;
- }
- while(DeltaAccum <= -120) {
- DeltaAccum += 120;
- delta -= 120;
- }
- if(delta == 0) return;
-
- POINT pt;
- GetCursorPos(&pt);
- HWND hw = WindowFromPoint(pt);
-
- // Make the mousewheel work according to which window the mouse is
- // over, not according to which window is active.
- bool inTextWindow;
- if(hw == TextWnd) {
- inTextWindow = true;
- } else if(hw == GraphicsWnd) {
- inTextWindow = false;
- } else if(GetForegroundWindow() == TextWnd) {
- inTextWindow = true;
- } else {
- inTextWindow = false;
- }
-
- if(inTextWindow) {
- int i;
- for(i = 0; i < abs(delta/40); i++) {
- HandleTextWindowScrollBar(delta > 0 ? SB_LINEUP : SB_LINEDOWN, 0);
- }
- } else {
- SS.GW.MouseScroll(LastMousePos.x, LastMousePos.y, delta);
- }
-}
-
-LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- switch (msg) {
- case WM_ERASEBKGND:
- break;
-
- case WM_CLOSE:
- case WM_DESTROY:
- SolveSpaceUI::MenuFile(GraphicsWindow::MNU_EXIT);
- break;
-
- case WM_PAINT: {
- // Actually paint the text window, with gl.
- PaintTextWnd(GetDC(TextWnd));
- // And then just make Windows happy.
- PAINTSTRUCT ps;
- HDC hdc = BeginPaint(hwnd, &ps);
- EndPaint(hwnd, &ps);
- break;
- }
-
- case WM_SIZING: {
- RECT *r = (RECT *)lParam;
- int hc = (r->bottom - r->top) - ClientIsSmallerBy;
- int extra = hc % (SS.TW.LINE_HEIGHT/2);
- switch(wParam) {
- case WMSZ_BOTTOM:
- case WMSZ_BOTTOMLEFT:
- case WMSZ_BOTTOMRIGHT:
- r->bottom -= extra;
- break;
-
- case WMSZ_TOP:
- case WMSZ_TOPLEFT:
- case WMSZ_TOPRIGHT:
- r->top += extra;
- break;
- }
- int tooNarrow = (SS.TW.MIN_COLS*SS.TW.CHAR_WIDTH) -
- (r->right - r->left);
- if(tooNarrow >= 0) {
- switch(wParam) {
- case WMSZ_RIGHT:
- case WMSZ_BOTTOMRIGHT:
- case WMSZ_TOPRIGHT:
- r->right += tooNarrow;
- break;
-
- case WMSZ_LEFT:
- case WMSZ_BOTTOMLEFT:
- case WMSZ_TOPLEFT:
- r->left -= tooNarrow;
- break;
- }
- }
- break;
- }
-
- case WM_MOUSELEAVE:
- SS.TW.MouseLeave();
- break;
-
- case WM_LBUTTONDOWN:
- case WM_MOUSEMOVE: {
- // We need this in order to get the WM_MOUSELEAVE
- TRACKMOUSEEVENT tme = {};
- tme.cbSize = sizeof(tme);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = TextWnd;
- TrackMouseEvent(&tme);
-
- // And process the actual message
- int x = LOWORD(lParam);
- int y = HIWORD(lParam);
- SS.TW.MouseEvent(msg == WM_LBUTTONDOWN, wParam & MK_LBUTTON, x, y);
- break;
- }
-
- case WM_SIZE: {
- RECT r;
- GetWindowRect(TextWndScrollBar, &r);
- int sw = r.right - r.left;
- GetClientRect(hwnd, &r);
- MoveWindow(TextWndScrollBar, r.right - sw, r.top, sw,
- (r.bottom - r.top), true);
- // If the window is growing, then the scrollbar position may
- // be moving, so it's as if we're dragging the scrollbar.
- HandleTextWindowScrollBar((WPARAM)-1, -1);
- InvalidateRect(TextWnd, NULL, false);
- break;
- }
-
- case WM_MOUSEWHEEL:
- MouseWheel(GET_WHEEL_DELTA_WPARAM(wParam));
- break;
-
- case WM_VSCROLL:
- HandleTextWindowScrollBar(wParam, lParam);
- break;
-
- default:
- return DefWindowProc(hwnd, msg, wParam, lParam);
- }
-
- return 1;
-}
-
-static std::string EditControlText(HWND hwnd)
-{
- std::wstring result;
- result.resize(GetWindowTextLength(hwnd));
- GetWindowTextW(hwnd, &result[0], result.length() + 1);
- return Narrow(result);
-}
-
-static bool ProcessKeyDown(WPARAM wParam)
-{
- if(GraphicsEditControlIsVisible() && wParam != VK_ESCAPE) {
- if(wParam == VK_RETURN) {
- SS.GW.EditControlDone(EditControlText(GraphicsEditControl).c_str());
- return true;
- } else {
- return false;
- }
- }
- if(TextEditControlIsVisible() && wParam != VK_ESCAPE) {
- if(wParam == VK_RETURN) {
- SS.TW.EditControlDone(EditControlText(TextEditControl).c_str());
- } else {
- return false;
- }
- }
-
- int c;
- switch(wParam) {
- case VK_OEM_PLUS: c = '+'; break;
- case VK_OEM_MINUS: c = '-'; break;
- case VK_ESCAPE: c = 27; break;
- case VK_OEM_1: c = ';'; break;
- case VK_OEM_3: c = '`'; break;
- case VK_OEM_4: c = '['; break;
- case VK_OEM_6: c = ']'; break;
- case VK_OEM_5: c = '\\'; break;
- case VK_OEM_PERIOD: c = '.'; break;
- case VK_SPACE: c = ' '; break;
- case VK_DELETE: c = 127; break;
- case VK_TAB: c = '\t'; break;
-
- case VK_BROWSER_BACK:
- case VK_BACK: c = '\b'; break;
-
- case VK_F1:
- case VK_F2:
- case VK_F3:
- case VK_F4:
- case VK_F5:
- case VK_F6:
- case VK_F7:
- case VK_F8:
- case VK_F9:
- case VK_F10:
- case VK_F11:
- case VK_F12: c = ((int)wParam - VK_F1) + 0xf1; break;
-
- // These overlap with some character codes that I'm using, so
- // don't let them trigger by accident.
- case VK_F16:
- case VK_INSERT:
- case VK_EXECUTE:
- case VK_APPS:
- case VK_LWIN:
- case VK_RWIN: return false;
-
- default:
- c = (int)wParam;
- break;
- }
- if(GetAsyncKeyState(VK_SHIFT) & 0x8000) c |= GraphicsWindow::SHIFT_MASK;
- if(GetAsyncKeyState(VK_CONTROL) & 0x8000) c |= GraphicsWindow::CTRL_MASK;
-
- switch(c) {
- case GraphicsWindow::SHIFT_MASK | '.': c = '>'; break;
- }
-
- for(int i = 0; SS.GW.menu[i].level >= 0; i++) {
- if(c == SS.GW.menu[i].accel) {
- (SS.GW.menu[i].fn)((GraphicsWindow::MenuId)SS.GW.menu[i].id);
- break;
- }
- }
-
- if(SS.GW.KeyDown(c)) return true;
-
- // No accelerator; process the key as normal.
- return false;
-}
-
-void SolveSpace::ToggleMenuBar(void)
-{
- // Implement me
-}
-bool SolveSpace::MenuBarIsVisible(void)
-{
- // Implement me
- return true;
-}
-
-void SolveSpace::ShowTextWindow(bool visible)
-{
- ShowWindow(TextWnd, visible ? SW_SHOWNOACTIVATE : SW_HIDE);
-}
-
-static void CreateGlContext(HWND hwnd, HGLRC *glrc)
-{
- HDC hdc = GetDC(hwnd);
-
- PIXELFORMATDESCRIPTOR pfd = {};
- int pixelFormat;
-
- pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
- pfd.nVersion = 1;
- pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
- PFD_DOUBLEBUFFER;
- pfd.dwLayerMask = PFD_MAIN_PLANE;
- pfd.iPixelType = PFD_TYPE_RGBA;
- pfd.cColorBits = 32;
- pfd.cDepthBits = 24;
- pfd.cAccumBits = 0;
- pfd.cStencilBits = 0;
-
- pixelFormat = ChoosePixelFormat(hdc, &pfd);
- if(!pixelFormat) oops();
-
- if(!SetPixelFormat(hdc, pixelFormat, &pfd)) oops();
-
- *glrc = wglCreateContext(hdc);
- wglMakeCurrent(hdc, *glrc);
-}
-
-void SolveSpace::PaintGraphics(void)
-{
- SS.GW.Paint();
- SwapBuffers(GetDC(GraphicsWnd));
-}
-void SolveSpace::InvalidateGraphics(void)
-{
- InvalidateRect(GraphicsWnd, NULL, false);
-}
-
-void SolveSpace::ToggleFullScreen(void)
-{
- // Implement me
-}
-bool SolveSpace::FullScreenIsActive(void)
-{
- // Implement me
- return false;
-}
-
-int64_t SolveSpace::GetMilliseconds(void)
-{
- LARGE_INTEGER t, f;
- QueryPerformanceCounter(&t);
- QueryPerformanceFrequency(&f);
- LONGLONG d = t.QuadPart/(f.QuadPart/1000);
- return (int64_t)d;
-}
-
-int64_t SolveSpace::GetUnixTime(void)
-{
-#ifdef __MINGW32__
- time_t ret;
- time(&ret);
-#else
- __time64_t ret;
- _time64(&ret);
-#endif
- return (int64_t)ret;
-}
-
-void SolveSpace::InvalidateText(void)
-{
- InvalidateRect(TextWnd, NULL, false);
-}
-
-static void ShowEditControl(HWND h, int x, int y, int fontHeight, int minWidthChars,
- bool isMonospace, const std::wstring &s) {
- static HFONT hf;
- if(hf) DeleteObject(hf);
- hf = CreateFontW(-fontHeight, 0, 0, 0,
- FW_REGULAR, false, false, false, ANSI_CHARSET,
- OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
- DEFAULT_QUALITY, FF_DONTCARE, isMonospace ? L"Lucida Console" : L"Arial");
- if(hf) SendMessage(h, WM_SETFONT, (WPARAM)hf, false);
- else SendMessage(h, WM_SETFONT, (WPARAM)(HFONT)GetStockObject(SYSTEM_FONT), false);
- SendMessage(h, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, 0);
-
- HDC hdc = GetDC(h);
- TEXTMETRICW tm;
- SIZE ts;
- SelectObject(hdc, hf);
- GetTextMetrics(hdc, &tm);
- GetTextExtentPoint32W(hdc, s.c_str(), s.length(), &ts);
- ReleaseDC(h, hdc);
-
- RECT rc;
- rc.left = x;
- rc.top = y - tm.tmAscent;
- // Add one extra char width to avoid scrolling.
- rc.right = x + std::max(tm.tmAveCharWidth * minWidthChars,
- ts.cx + tm.tmAveCharWidth);
- rc.bottom = y + tm.tmDescent;
-
- AdjustWindowRectEx(&rc, 0, false, WS_EX_CLIENTEDGE);
- MoveWindow(h, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, true);
- ShowWindow(h, SW_SHOW);
- if(!s.empty()) {
- SendMessage(h, WM_SETTEXT, 0, (LPARAM)s.c_str());
- SendMessage(h, EM_SETSEL, 0, s.length());
- SetFocus(h);
- }
-}
-void SolveSpace::ShowTextEditControl(int x, int y, const std::string &str)
-{
- if(GraphicsEditControlIsVisible()) return;
-
- ShowEditControl(TextEditControl, x, y, TextWindow::CHAR_HEIGHT, 30,
- /*isMonospace=*/true, Widen(str));
-}
-void SolveSpace::HideTextEditControl(void)
-{
- ShowWindow(TextEditControl, SW_HIDE);
-}
-bool SolveSpace::TextEditControlIsVisible(void)
-{
- return IsWindowVisible(TextEditControl) ? true : false;
-}
-void SolveSpace::ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars,
- const std::string &str)
-{
- if(GraphicsEditControlIsVisible()) return;
-
- RECT r;
- GetClientRect(GraphicsWnd, &r);
- x = x + (r.right - r.left)/2;
- y = (r.bottom - r.top)/2 - y;
-
- ShowEditControl(GraphicsEditControl, x, y, fontHeight, minWidthChars,
- /*isMonospace=*/false, Widen(str));
-}
-void SolveSpace::HideGraphicsEditControl(void)
-{
- ShowWindow(GraphicsEditControl, SW_HIDE);
-}
-bool SolveSpace::GraphicsEditControlIsVisible(void)
-{
- return IsWindowVisible(GraphicsEditControl) ? true : false;
-}
-
-LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam,
- LPARAM lParam)
-{
- switch (msg) {
- case WM_ERASEBKGND:
- break;
-
- case WM_SIZE:
- InvalidateRect(GraphicsWnd, NULL, false);
- break;
-
- case WM_PAINT: {
- // Actually paint the window, with gl.
- PaintGraphics();
- // And make Windows happy.
- PAINTSTRUCT ps;
- HDC hdc = BeginPaint(hwnd, &ps);
- EndPaint(hwnd, &ps);
- break;
- }
-
- case WM_MOUSELEAVE:
- SS.GW.MouseLeave();
- break;
-
- case WM_MOUSEMOVE:
- case WM_LBUTTONDOWN:
- case WM_LBUTTONUP:
- case WM_LBUTTONDBLCLK:
- case WM_RBUTTONDOWN:
- case WM_RBUTTONUP:
- case WM_MBUTTONDOWN: {
- int x = LOWORD(lParam);
- int y = HIWORD(lParam);
-
- // We need this in order to get the WM_MOUSELEAVE
- TRACKMOUSEEVENT tme = {};
- tme.cbSize = sizeof(tme);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = GraphicsWnd;
- TrackMouseEvent(&tme);
-
- // Convert to xy (vs. ij) style coordinates, with (0, 0) at center
- RECT r;
- GetClientRect(GraphicsWnd, &r);
- x = x - (r.right - r.left)/2;
- y = (r.bottom - r.top)/2 - y;
-
- LastMousePos.x = x;
- LastMousePos.y = y;
-
- if(msg == WM_LBUTTONDOWN) {
- SS.GW.MouseLeftDown(x, y);
- } else if(msg == WM_LBUTTONUP) {
- SS.GW.MouseLeftUp(x, y);
- } else if(msg == WM_LBUTTONDBLCLK) {
- SS.GW.MouseLeftDoubleClick(x, y);
- } else if(msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) {
- SS.GW.MouseMiddleOrRightDown(x, y);
- } else if(msg == WM_RBUTTONUP) {
- SS.GW.MouseRightUp(x, y);
- } else if(msg == WM_MOUSEMOVE) {
- SS.GW.MouseMoved(x, y,
- !!(wParam & MK_LBUTTON),
- !!(wParam & MK_MBUTTON),
- !!(wParam & MK_RBUTTON),
- !!(wParam & MK_SHIFT),
- !!(wParam & MK_CONTROL));
- } else {
- oops();
- }
- break;
- }
- case WM_MOUSEWHEEL:
- MouseWheel(GET_WHEEL_DELTA_WPARAM(wParam));
- break;
-
- case WM_COMMAND: {
- if(HIWORD(wParam) == 0) {
- int id = LOWORD(wParam);
- if((id >= RECENT_OPEN && id < (RECENT_OPEN + MAX_RECENT))) {
- SolveSpaceUI::MenuFile(id);
- break;
- }
- if((id >= RECENT_LINK && id < (RECENT_LINK + MAX_RECENT))) {
- Group::MenuGroup(id);
- break;
- }
- int i;
- for(i = 0; SS.GW.menu[i].level >= 0; i++) {
- if(id == SS.GW.menu[i].id) {
- (SS.GW.menu[i].fn)((GraphicsWindow::MenuId)id);
- break;
- }
- }
- if(SS.GW.menu[i].level < 0) oops();
- }
- break;
- }
-
- case WM_CLOSE:
- case WM_DESTROY:
- SolveSpaceUI::MenuFile(GraphicsWindow::MNU_EXIT);
- return 1;
-
- default:
- return DefWindowProc(hwnd, msg, wParam, lParam);
- }
-
- return 1;
-}
-
-//-----------------------------------------------------------------------------
-// Common dialog routines, to open or save a file.
-//-----------------------------------------------------------------------------
-static std::string ConvertFilters(const FileFilter ssFilters[]) {
- std::string filter;
- for(const FileFilter *ssFilter = ssFilters; ssFilter->name; ssFilter++) {
- std::string desc, patterns;
- for(const char *const *ssPattern = ssFilter->patterns; *ssPattern; ssPattern++) {
- std::string pattern = "*." + std::string(*ssPattern);
- if(desc == "")
- desc = pattern;
- else
- desc += ", " + pattern;
- if(patterns == "")
- patterns = pattern;
- else
- patterns += ";" + pattern;
- }
- filter += std::string(ssFilter->name) + " (" + desc + ")" + '\0';
- filter += patterns + '\0';
- }
- filter += '\0';
- return filter;
-}
-
-static bool OpenSaveFile(bool isOpen, std::string *filename, const std::string &defExtension,
- const FileFilter filters[]) {
- // UNC paths may be as long as 32767 characters.
- // Unfortunately, the Get*FileName API does not provide any way to use it
- // except with a preallocated buffer of fixed size, so we use something
- // reasonably large.
- const int len = 32768;
- wchar_t filenameC[len] = {};
- wcsncpy(filenameC, Widen(*filename).c_str(), len - 1);
-
- std::wstring selPatternW = Widen(ConvertFilters(filters));
- std::wstring defExtensionW = Widen(defExtension);
-
- OPENFILENAME ofn = {};
- ofn.lStructSize = sizeof(ofn);
- ofn.hInstance = Instance;
- ofn.hwndOwner = GraphicsWnd;
- ofn.lpstrFilter = selPatternW.c_str();
- ofn.lpstrDefExt = defExtensionW.c_str();
- ofn.lpstrFile = filenameC;
- ofn.nMaxFile = len;
- ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
-
- EnableWindow(GraphicsWnd, false);
- EnableWindow(TextWnd, false);
-
- BOOL r;
- if(isOpen) {
- r = GetOpenFileNameW(&ofn);
- } else {
- r = GetSaveFileNameW(&ofn);
- }
-
- EnableWindow(TextWnd, true);
- EnableWindow(GraphicsWnd, true);
- SetForegroundWindow(GraphicsWnd);
-
- if(r) *filename = Narrow(filenameC);
- return r ? true : false;
-}
-
-bool SolveSpace::GetOpenFile(std::string *filename, const std::string &defExtension,
- const FileFilter filters[])
-{
- return OpenSaveFile(true, filename, defExtension, filters);
-}
-
-bool SolveSpace::GetSaveFile(std::string *filename, const std::string &defExtension,
- const FileFilter filters[])
-{
- return OpenSaveFile(false, filename, defExtension, filters);
-}
-
-DialogChoice SolveSpace::SaveFileYesNoCancel(void)
-{
- EnableWindow(GraphicsWnd, false);
- EnableWindow(TextWnd, false);
-
- int r = MessageBoxW(GraphicsWnd,
- L"The file has changed since it was last saved.\n\n"
- L"Do you want to save the changes?", L"SolveSpace",
- MB_YESNOCANCEL | MB_ICONWARNING);
-
- EnableWindow(TextWnd, true);
- EnableWindow(GraphicsWnd, true);
- SetForegroundWindow(GraphicsWnd);
-
- switch(r) {
- case IDYES:
- return DIALOG_YES;
- case IDNO:
- return DIALOG_NO;
- case IDCANCEL:
- default:
- return DIALOG_CANCEL;
- }
-}
-
-DialogChoice SolveSpace::LoadAutosaveYesNo(void)
-{
- EnableWindow(GraphicsWnd, false);
- EnableWindow(TextWnd, false);
-
- int r = MessageBoxW(GraphicsWnd,
- L"An autosave file is availible for this project.\n\n"
- L"Do you want to load the autosave file instead?", L"SolveSpace",
- MB_YESNO | MB_ICONWARNING);
-
- EnableWindow(TextWnd, true);
- EnableWindow(GraphicsWnd, true);
- SetForegroundWindow(GraphicsWnd);
-
- switch (r) {
- case IDYES:
- return DIALOG_YES;
- case IDNO:
- default:
- return DIALOG_NO;
- }
-}
-
-DialogChoice SolveSpace::LocateImportedFileYesNoCancel(const std::string &filename,
- bool canCancel) {
- EnableWindow(GraphicsWnd, false);
- EnableWindow(TextWnd, false);
-
- std::string message =
- "The linked file " + filename + " is not present.\n\n"
- "Do you want to locate it manually?\n\n"
- "If you select \"No\", any geometry that depends on "
- "the missing file will be removed.";
-
- int r = MessageBoxW(GraphicsWnd, Widen(message).c_str(), L"SolveSpace",
- (canCancel ? MB_YESNOCANCEL : MB_YESNO) | MB_ICONWARNING);
-
- EnableWindow(TextWnd, true);
- EnableWindow(GraphicsWnd, true);
- SetForegroundWindow(GraphicsWnd);
-
- switch(r) {
- case IDYES:
- return DIALOG_YES;
- case IDNO:
- return DIALOG_NO;
- case IDCANCEL:
- default:
- return DIALOG_CANCEL;
- }
-}
-
-std::vector<std::string> SolveSpace::GetFontFiles() {
- std::vector<std::string> fonts;
-
- std::wstring fontsDir(MAX_PATH, '\0');
- fontsDir.resize(GetWindowsDirectoryW(&fontsDir[0], fontsDir.length()));
- fontsDir += L"\\fonts\\";
-
- WIN32_FIND_DATA wfd;
- HANDLE h = FindFirstFileW((fontsDir + L"*").c_str(), &wfd);
- while(h != INVALID_HANDLE_VALUE) {
- fonts.push_back(Narrow(fontsDir) + Narrow(wfd.cFileName));
- if(!FindNextFileW(h, &wfd)) break;
- }
-
- return fonts;
-}
-
-static void MenuById(int id, bool yes, bool check)
-{
- int i;
- int subMenu = -1;
-
- for(i = 0; SS.GW.menu[i].level >= 0; i++) {
- if(SS.GW.menu[i].level == 0) subMenu++;
-
- if(SS.GW.menu[i].id == id) {
- if(subMenu < 0) oops();
- if(subMenu >= (int)arraylen(SubMenus)) oops();
-
- if(check) {
- CheckMenuItem(SubMenus[subMenu], id,
- yes ? MF_CHECKED : MF_UNCHECKED);
- } else {
- EnableMenuItem(SubMenus[subMenu], id,
- yes ? MF_ENABLED : MF_GRAYED);
- }
- return;
- }
- }
- oops();
-}
-void SolveSpace::CheckMenuById(int id, bool checked)
-{
- MenuById(id, checked, true);
-}
-void SolveSpace::RadioMenuById(int id, bool selected)
-{
- // Windows does not natively support radio-button menu items
- MenuById(id, selected, true);
-}
-void SolveSpace::EnableMenuById(int id, bool enabled)
-{
- MenuById(id, enabled, false);
-}
-static void DoRecent(HMENU m, int base)
-{
- while(DeleteMenu(m, 0, MF_BYPOSITION))
- ;
- int i, c = 0;
- for(i = 0; i < MAX_RECENT; i++) {
- if(!RecentFile[i].empty()) {
- AppendMenuW(m, MF_STRING, base + i, Widen(RecentFile[i]).c_str());
- c++;
- }
- }
- if(c == 0) AppendMenuW(m, MF_STRING | MF_GRAYED, 0, L"(no recent files)");
-}
-void SolveSpace::RefreshRecentMenus(void)
-{
- DoRecent(RecentOpenMenu, RECENT_OPEN);
- DoRecent(RecentImportMenu, RECENT_LINK);
-}
-
-HMENU CreateGraphicsWindowMenus(void)
-{
- HMENU top = CreateMenu();
- HMENU m = 0;
-
- int i;
- int subMenu = 0;
-
- for(i = 0; SS.GW.menu[i].level >= 0; i++) {
- std::string label;
- if(SS.GW.menu[i].label) {
- std::string accel = MakeAcceleratorLabel(SS.GW.menu[i].accel);
- const char *sep = accel.empty() ? "" : "\t";
- label = ssprintf("%s%s%s", SS.GW.menu[i].label, sep, accel.c_str());
- }
-
- if(SS.GW.menu[i].level == 0) {
- m = CreateMenu();
- AppendMenuW(top, MF_STRING | MF_POPUP, (UINT_PTR)m, Widen(label).c_str());
- if(subMenu >= (int)arraylen(SubMenus)) oops();
- SubMenus[subMenu] = m;
- subMenu++;
- } else if(SS.GW.menu[i].level == 1) {
- if(SS.GW.menu[i].id == GraphicsWindow::MNU_OPEN_RECENT) {
- RecentOpenMenu = CreateMenu();
- AppendMenuW(m, MF_STRING | MF_POPUP,
- (UINT_PTR)RecentOpenMenu, Widen(label).c_str());
- } else if(SS.GW.menu[i].id == GraphicsWindow::MNU_GROUP_RECENT) {
- RecentImportMenu = CreateMenu();
- AppendMenuW(m, MF_STRING | MF_POPUP,
- (UINT_PTR)RecentImportMenu, Widen(label).c_str());
- } else if(SS.GW.menu[i].label) {
- AppendMenuW(m, MF_STRING, SS.GW.menu[i].id, Widen(label).c_str());
- } else {
- AppendMenuW(m, MF_SEPARATOR, SS.GW.menu[i].id, L"");
- }
- } else oops();
- }
- RefreshRecentMenus();
-
- return top;
-}
-
-static void CreateMainWindows(void)
-{
- WNDCLASSEX wc = {};
-
- wc.cbSize = sizeof(wc);
-
- // The graphics window, where the sketch is drawn and shown.
- wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC |
- CS_DBLCLKS;
- wc.lpfnWndProc = (WNDPROC)GraphicsWndProc;
- wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
- wc.lpszClassName = L"GraphicsWnd";
- wc.lpszMenuName = NULL;
- wc.hCursor = LoadCursor(NULL, IDC_ARROW);
- wc.hIcon = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
- IMAGE_ICON, 32, 32, 0);
- wc.hIconSm = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
- IMAGE_ICON, 16, 16, 0);
- if(!RegisterClassEx(&wc)) oops();
-
- HMENU top = CreateGraphicsWindowMenus();
- GraphicsWnd = CreateWindowExW(0, L"GraphicsWnd",
- L"SolveSpace (not yet saved)",
- WS_OVERLAPPED | WS_THICKFRAME | WS_CLIPCHILDREN | WS_MAXIMIZEBOX |
- WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX | WS_CLIPSIBLINGS,
- 50, 50, 900, 600, NULL, top, Instance, NULL);
- if(!GraphicsWnd) oops();
-
- GraphicsEditControl = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDIT, L"",
- WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS,
- 50, 50, 100, 21, GraphicsWnd, NULL, Instance, NULL);
-
- // The text window, with a comand line and some textual information
- // about the sketch.
- wc.style &= ~CS_DBLCLKS;
- wc.lpfnWndProc = (WNDPROC)TextWndProc;
- wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
- wc.lpszClassName = L"TextWnd";
- wc.hCursor = NULL;
- if(!RegisterClassEx(&wc)) oops();
-
- // We get the desired Alt+Tab behaviour by specifying that the text
- // window is a child of the graphics window.
- TextWnd = CreateWindowExW(0,
- L"TextWnd", L"SolveSpace - Property Browser", WS_THICKFRAME | WS_CLIPCHILDREN,
- 650, 500, 420, 300, GraphicsWnd, (HMENU)NULL, Instance, NULL);
- if(!TextWnd) oops();
-
- TextWndScrollBar = CreateWindowExW(0, WC_SCROLLBAR, L"", WS_CHILD |
- SBS_VERT | SBS_LEFTALIGN | WS_VISIBLE | WS_CLIPSIBLINGS,
- 200, 100, 100, 100, TextWnd, NULL, Instance, NULL);
- // Force the scrollbar to get resized to the window,
- TextWndProc(TextWnd, WM_SIZE, 0, 0);
-
- TextEditControl = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDIT, L"",
- WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS,
- 50, 50, 100, 21, TextWnd, NULL, Instance, NULL);
-
- // Now that all our windows exist, set up gl contexts.
- CreateGlContext(TextWnd, &TextGl);
- CreateGlContext(GraphicsWnd, &GraphicsGl);
-
- RECT r, rc;
- GetWindowRect(TextWnd, &r);
- GetClientRect(TextWnd, &rc);
- ClientIsSmallerBy = (r.bottom - r.top) - (rc.bottom - rc.top);
-}
-
-#ifdef HAVE_SPACEWARE
-//-----------------------------------------------------------------------------
-// Test if a message comes from the SpaceNavigator device. If yes, dispatch
-// it appropriately and return true. Otherwise, do nothing and return false.
-//-----------------------------------------------------------------------------
-static bool ProcessSpaceNavigatorMsg(MSG *msg) {
- if(SpaceNavigator == SI_NO_HANDLE) return false;
-
- SiGetEventData sged;
- SiSpwEvent sse;
-
- SiGetEventWinInit(&sged, msg->message, msg->wParam, msg->lParam);
- int ret = SiGetEvent(SpaceNavigator, 0, &sged, &sse);
- if(ret == SI_NOT_EVENT) return false;
- // So the device is a SpaceNavigator event, or a SpaceNavigator error.
-
- if(ret == SI_IS_EVENT) {
- if(sse.type == SI_MOTION_EVENT) {
- // The Z axis translation and rotation are both
- // backwards in the default mapping.
- double tx = sse.u.spwData.mData[SI_TX]*1.0,
- ty = sse.u.spwData.mData[SI_TY]*1.0,
- tz = -sse.u.spwData.mData[SI_TZ]*1.0,
- rx = sse.u.spwData.mData[SI_RX]*0.001,
- ry = sse.u.spwData.mData[SI_RY]*0.001,
- rz = -sse.u.spwData.mData[SI_RZ]*0.001;
- SS.GW.SpaceNavigatorMoved(tx, ty, tz, rx, ry, rz,
- !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
- } else if(sse.type == SI_BUTTON_EVENT) {
- int button;
- button = SiButtonReleased(&sse);
- if(button == SI_APP_FIT_BUTTON) SS.GW.SpaceNavigatorButtonUp();
- }
- }
- return true;
-}
-#endif // HAVE_SPACEWARE
-
-//-----------------------------------------------------------------------------
-// Entry point into the program.
-//-----------------------------------------------------------------------------
-int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
- LPSTR lpCmdLine, INT nCmdShow)
-{
- Instance = hInstance;
-
- InitCommonControls();
-
- // A monospaced font
- FixedFont = CreateFontW(SS.TW.CHAR_HEIGHT, SS.TW.CHAR_WIDTH, 0, 0,
- FW_REGULAR, false,
- false, false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
- DEFAULT_QUALITY, FF_DONTCARE, L"Lucida Console");
- if(!FixedFont)
- FixedFont = (HFONT)GetStockObject(SYSTEM_FONT);
-
- // Create the root windows: one for control, with text, and one for
- // the graphics
- CreateMainWindows();
-
- ThawWindowPos(TextWnd, "TextWnd");
- ThawWindowPos(GraphicsWnd, "GraphicsWnd");
-
- ShowWindow(TextWnd, SW_SHOWNOACTIVATE);
- ShowWindow(GraphicsWnd, SW_SHOW);
-
- glClearColor(0, 0, 0, 1);
- glClear(GL_COLOR_BUFFER_BIT);
- SwapBuffers(GetDC(GraphicsWnd));
- glClearColor(0, 0, 0, 1);
- glClear(GL_COLOR_BUFFER_BIT);
- SwapBuffers(GetDC(GraphicsWnd));
-
- // Create the heaps for all dynamic memory (AllocTemporary, MemAlloc)
- InitHeaps();
-
- // Pull out the Unicode command line.
- int argc;
- LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc);
-
- // A filename may have been specified on the command line; if so, then
- // strip any quotation marks, and make it absolute.
- std::wstring filenameRel, filename;
- if(argc >= 2) {
- filenameRel = argv[1];
- if(filenameRel[0] == L'\"' && filenameRel[filenameRel.length() - 1] == L'\"') {
- filenameRel.erase(0, 1);
- filenameRel.erase(filenameRel.length() - 1, 1);
- }
-
- DWORD len = GetFullPathNameW(&filenameRel[0], 0, NULL, NULL);
- filename.resize(len - 1);
- GetFullPathNameW(&filenameRel[0], len, &filename[0], NULL);
- }
-
-#ifdef HAVE_SPACEWARE
- // Initialize the SpaceBall, if present. Test if the driver is running
- // first, to avoid a long timeout if it's not.
- HWND swdc = FindWindowW(L"SpaceWare Driver Class", NULL);
- if(swdc != NULL) {
- SiOpenData sod;
- SiInitialize();
- SiOpenWinInit(&sod, GraphicsWnd);
- SpaceNavigator =
- SiOpen("GraphicsWnd", SI_ANY_DEVICE, SI_NO_MASK, SI_EVENT, &sod);
- SiSetUiMode(SpaceNavigator, SI_UI_NO_CONTROLS);
- }
-#endif
-
- // Call in to the platform-independent code, and let them do their init
- SS.Init();
- if(!filename.empty())
- SS.OpenFile(Narrow(filename));
-
- // And now it's the message loop. All calls in to the rest of the code
- // will be from the wndprocs.
- MSG msg;
- DWORD ret;
- while((ret = GetMessage(&msg, NULL, 0, 0)) != 0) {
-#ifdef HAVE_SPACEWARE
- // Is it a message from the six degree of freedom input device?
- if(ProcessSpaceNavigatorMsg(&msg)) goto done;
-#endif
-
- // A message from the keyboard, which should be processed as a keyboard
- // accelerator?
- if(msg.message == WM_KEYDOWN) {
- if(ProcessKeyDown(msg.wParam)) goto done;
- }
- if(msg.message == WM_SYSKEYDOWN && msg.hwnd == TextWnd) {
- // If the user presses the Alt key when the text window has focus,
- // then that should probably go to the graphics window instead.
- SetForegroundWindow(GraphicsWnd);
- }
-
- // None of the above; so just a normal message to process.
- TranslateMessage(&msg);
- DispatchMessage(&msg);
-done:
- SS.DoLater();
- }
-
-#ifdef HAVE_SPACEWARE
- if(swdc != NULL) {
- if(SpaceNavigator != SI_NO_HANDLE) SiClose(SpaceNavigator);
- SiTerminate();
- }
-#endif
-
- // Write everything back to the registry
- FreezeWindowPos(TextWnd, "TextWnd");
- FreezeWindowPos(GraphicsWnd, "GraphicsWnd");
-
- // Free the memory we've used; anything that remains is a leak.
- SK.Clear();
- SS.Clear();
-
- return 0;
-}
+++ /dev/null
-//-----------------------------------------------------------------------------
-// Utility functions that depend on Win32. Notably, our memory allocation;
-// we use two separate allocators, one for long-lived stuff and one for
-// stuff that gets freed after every regeneration of the model, to save us
-// the trouble of freeing the latter explicitly.
-//
-// Copyright 2008-2013 Jonathan Westhues.
-//-----------------------------------------------------------------------------
-#include "solvespace.h"
-
-namespace SolveSpace {
-static HANDLE PermHeap, TempHeap;
-
-void dbp(const char *str, ...)
-{
- va_list f;
- static char buf[1024*50];
- va_start(f, str);
- _vsnprintf(buf, sizeof(buf), str, f);
- va_end(f);
-
- // The native version of OutputDebugString, unlike most others,
- // is OutputDebugStringA.
- OutputDebugStringA(buf);
-}
-
-std::string Narrow(const wchar_t *in)
-{
- std::string out;
- DWORD len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL);
- out.resize(len - 1);
- if(!WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], len, NULL, NULL))
- oops();
- return out;
-}
-
-std::string Narrow(const std::wstring &in)
-{
- if(in == L"") return "";
-
- std::string out;
- out.resize(WideCharToMultiByte(CP_UTF8, 0, &in[0], in.length(), NULL, 0, NULL, NULL));
- if(!WideCharToMultiByte(CP_UTF8, 0, &in[0], in.length(),
- &out[0], out.length(), NULL, NULL))
- oops();
- return out;
-}
-
-std::wstring Widen(const char *in)
-{
- std::wstring out;
- DWORD len = MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0);
- out.resize(len - 1);
- if(!MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], len))
- oops();
- return out;
-}
-
-std::wstring Widen(const std::string &in)
-{
- if(in == "") return L"";
-
- std::wstring out;
- out.resize(MultiByteToWideChar(CP_UTF8, 0, &in[0], in.length(), NULL, 0));
- if(!MultiByteToWideChar(CP_UTF8, 0, &in[0], in.length(), &out[0], out.length()))
- oops();
- return out;
-}
-
-static std::string MakeUNCFilename(const std::string &filename)
-{
- // Prepend \\?\ UNC prefix unless already an UNC path.
- // We never try to fopen paths that are not absolute or
- // contain separators inappropriate for the platform;
- // thus, it is always safe to prepend this prefix.
- std::string uncFilename = filename;
- if(uncFilename.substr(0, 2) != "\\\\")
- uncFilename = "\\\\?\\" + uncFilename;
- return uncFilename;
-}
-
-FILE *ssfopen(const std::string &filename, const char *mode)
-{
- if(filename.length() != strlen(filename.c_str())) oops();
- return _wfopen(Widen(MakeUNCFilename(filename)).c_str(), Widen(mode).c_str());
-}
-
-void ssremove(const std::string &filename)
-{
- if(filename.length() != strlen(filename.c_str())) oops();
- _wremove(Widen(filename).c_str());
-}
-
-//-----------------------------------------------------------------------------
-// A separate heap, on which we allocate expressions. Maybe a bit faster,
-// since no fragmentation issues whatsoever, and it also makes it possible
-// to be sloppy with our memory management, and just free everything at once
-// at the end.
-//-----------------------------------------------------------------------------
-void *AllocTemporary(size_t n)
-{
- void *v = HeapAlloc(TempHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n);
- if(!v) oops();
- return v;
-}
-void FreeTemporary(void *p) {
- HeapFree(TempHeap, HEAP_NO_SERIALIZE, p);
-}
-void FreeAllTemporary(void)
-{
- if(TempHeap) HeapDestroy(TempHeap);
- TempHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0);
- // This is a good place to validate, because it gets called fairly
- // often.
- vl();
-}
-
-void *MemAlloc(size_t n) {
- void *p = HeapAlloc(PermHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n);
- if(!p) oops();
- return p;
-}
-void MemFree(void *p) {
- HeapFree(PermHeap, HEAP_NO_SERIALIZE, p);
-}
-
-void vl(void) {
- if(!HeapValidate(TempHeap, HEAP_NO_SERIALIZE, NULL)) oops();
- if(!HeapValidate(PermHeap, HEAP_NO_SERIALIZE, NULL)) oops();
-}
-
-void InitHeaps(void) {
- // Create the heap used for long-lived stuff (that gets freed piecewise).
- PermHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0);
- // Create the heap that we use to store Exprs and other temp stuff.
- FreeAllTemporary();
-}
-}
--- /dev/null
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR})
+
+foreach(pkg_config_lib CAIRO)
+ include_directories(${${pkg_config_lib}_INCLUDE_DIRS})
+ link_directories(${${pkg_config_lib}_LIBRARY_DIRS})
+endforeach()
+
+if(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows")
+ add_definitions(-DTEST_BUILD_ON_WINDOWS)
+endif()
+
+# test suite
+
+set(testsuite_SOURCES
+ harness.cpp
+ analysis/contour_area/test.cpp
+ core/expr/test.cpp
+ core/locale/test.cpp
+ core/path/test.cpp
+ constraint/points_coincident/test.cpp
+ constraint/pt_pt_distance/test.cpp
+ constraint/pt_plane_distance/test.cpp
+ constraint/pt_line_distance/test.cpp
+ constraint/pt_face_distance/test.cpp
+ constraint/proj_pt_distance/test.cpp
+ constraint/pt_in_plane/test.cpp
+ constraint/pt_on_line/test.cpp
+ constraint/pt_on_face/test.cpp
+ constraint/equal_length_lines/test.cpp
+ constraint/length_ratio/test.cpp
+ constraint/eq_len_pt_line_d/test.cpp
+ constraint/eq_pt_ln_distances/test.cpp
+ constraint/equal_angle/test.cpp
+ constraint/equal_line_arc_len/test.cpp
+ constraint/length_difference/test.cpp
+ constraint/symmetric/test.cpp
+ constraint/symmetric_horiz/test.cpp
+ constraint/symmetric_vert/test.cpp
+ constraint/symmetric_line/test.cpp
+ constraint/at_midpoint/test.cpp
+ constraint/horizontal/test.cpp
+ constraint/vertical/test.cpp
+ constraint/diameter/test.cpp
+ constraint/pt_on_circle/test.cpp
+ constraint/same_orientation/test.cpp
+ constraint/angle/test.cpp
+ constraint/parallel/test.cpp
+ constraint/perpendicular/test.cpp
+ constraint/arc_line_tangent/test.cpp
+ constraint/cubic_line_tangent/test.cpp
+ constraint/curve_curve_tangent/test.cpp
+ constraint/equal_radius/test.cpp
+ constraint/where_dragged/test.cpp
+ constraint/comment/test.cpp
+ request/arc_of_circle/test.cpp
+ request/circle/test.cpp
+ request/cubic/test.cpp
+ request/cubic_periodic/test.cpp
+ request/datum_point/test.cpp
+ request/image/test.cpp
+ request/line_segment/test.cpp
+ request/ttf_text/test.cpp
+ request/workplane/test.cpp
+ group/link/test.cpp
+ group/translate_asy/test.cpp
+ group/translate_nd/test.cpp
+)
+
+add_executable(solvespace-testsuite
+ ${testsuite_SOURCES}
+ $<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
+
+target_link_libraries(solvespace-testsuite
+ solvespace-headless
+ ${COVERAGE_LIBRARY})
+
+add_dependencies(solvespace-testsuite
+ resources)
+
+add_custom_target(test_solvespace
+ COMMAND $<TARGET_FILE:solvespace-testsuite>
+ COMMENT "Testing SolveSpace"
+ VERBATIM)
+
+# coverage reports
+
+if(ENABLE_COVERAGE)
+ set(LCOV_FLAGS -q --gcov-tool ${GCOV})
+ set(LCOV_FLAGS ${LCOV_FLAGS} --rc lcov_branch_coverage=1)
+ set(LCOV_FLAGS ${LCOV_FLAGS} --rc "lcov_excl_line=(ssassert|switch)")
+ set(LCOV_FLAGS ${LCOV_FLAGS} --rc "lcov_excl_br_line=BRANCH_ALWAYS_TAKEN")
+ set(LCOV_COLLECT -c -b ${CMAKE_SOURCE_DIR}/src -d ${CMAKE_BINARY_DIR}/src --no-external)
+
+ add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/coverage_base.info
+ COMMAND ${LCOV} ${LCOV_FLAGS} ${LCOV_COLLECT}
+ -o ${CMAKE_BINARY_DIR}/coverage_base.info -i
+ DEPENDS solvespace-testsuite
+ COMMENT "Importing baseline coverage data"
+ VERBATIM)
+
+ add_custom_target(coverage_solvespace ALL
+ COMMAND ${LCOV} ${LCOV_FLAGS} ${LCOV_COLLECT}
+ -o ${CMAKE_BINARY_DIR}/coverage_test.info
+ COMMAND ${LCOV} ${LCOV_FLAGS}
+ -o ${CMAKE_BINARY_DIR}/coverage_full.info
+ -a ${CMAKE_BINARY_DIR}/coverage_base.info
+ -a ${CMAKE_BINARY_DIR}/coverage_test.info
+ COMMAND ${LCOV} ${LCOV_FLAGS} --summary
+ ${CMAKE_BINARY_DIR}/coverage_full.info
+ COMMAND ${GENHTML} -q --branch-coverage --demangle-cpp --legend
+ ${CMAKE_BINARY_DIR}/coverage_full.info
+ -o ${CMAKE_BINARY_DIR}/coverage/
+ -t "SolveSpace testbench"
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/coverage_base.info
+ DEPENDS test_solvespace
+ COMMENT "Generating coverage report"
+ VERBATIM)
+endif()
+
+# debug runner
+
+set(debugtool_SOURCES
+ debugtool.cpp
+)
+
+add_executable(solvespace-debugtool
+ ${debugtool_SOURCES}
+ $<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
+
+target_link_libraries(solvespace-debugtool
+ solvespace-core
+ solvespace-headless)
+
+add_dependencies(solvespace-debugtool
+ resources)
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00070013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00070014
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00070001
+Entity.point[1].v=00070002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040001
+Constraint.ptB.v=00050002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050001
+Constraint.ptB.v=00060002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00060001
+Constraint.ptB.v=00070002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00070001
+Constraint.ptB.v=00040002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000005
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000006
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000007
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000008
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00070000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ SS.showContourAreas = true;
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+}
--- /dev/null
+#!/bin/sh -ex
+
+make -C build solvespace-testsuite
+./build/test/solvespace-testsuite $* || true
+for e in slvs png; do
+ for i in `find . -name *.out.$e`; do
+ mv $i `dirname $i`/`basename $i .out.$e`.$e;
+ done;
+done
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000088818
+AddParam
+
+Param.h.v.=00050015
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000088818
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.valA=45.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=0.51894056308654845644
+Constraint.disp.offset.y=3.99584233576642278152
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000090000
+AddParam
+
+Param.h.v.=00050015
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000090000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.valA=45.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=0.51894056308654846000
+Constraint.disp.offset.y=3.99584233576642280000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000088818
+AddParam
+
+Param.h.v.=00050015
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000088818
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.valA=45.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=0.51894056308654845644
+Constraint.disp.offset.y=3.99584233576642278152
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000088818
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000088818
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=45.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.y=4.50000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000090000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000090000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=45.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.y=4.50000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000090000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000090000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=45.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.y=4.50000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000088818
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000088818
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=45.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.y=4.50000000000000000000
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=135.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.x=-1.99273176225234482928
+Constraint.disp.offset.y=1.12091161626694391096
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000088818
+AddParam
+
+Param.h.v.=00050015
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000088818
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.valA=45.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.x=0.51894056308654845644
+Constraint.disp.offset.y=3.99584233576642278152
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000090000
+AddParam
+
+Param.h.v.=00050015
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000090000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.valA=45.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.x=0.51894056308654846000
+Constraint.disp.offset.y=3.99584233576642280000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000088818
+AddParam
+
+Param.h.v.=00050015
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000088818
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.valA=45.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.x=0.51894056308654845644
+Constraint.disp.offset.y=3.99584233576642278152
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000090000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000090000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=45.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.y=4.50000000000000000000
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=135.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.x=-1.99273176225234480000
+Constraint.disp.offset.y=1.12091161626694390000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000088818
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000088818
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=45.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.y=4.50000000000000000000
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=135.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.x=-1.99273176225234482928
+Constraint.disp.offset.y=1.12091161626694391096
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=00020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.valA=90.00000000000000000000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=2.50000000000000044409
+Constraint.disp.offset.y=-2.50000000000000000000
+Constraint.disp.offset.z=-2.50000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=00020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=120
+Constraint.group.v=00000002
+Constraint.valA=90.00000000000000000000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=2.50000000000000044409
+Constraint.disp.offset.y=-2.50000000000000000000
+Constraint.disp.offset.z=-2.50000000000000000000
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(free_in_3d_roundtrip) {
+ CHECK_LOAD("free_in_3d.slvs");
+ CHECK_RENDER("free_in_3d.png");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v20) {
+ CHECK_LOAD("free_in_3d_v20.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v22) {
+ CHECK_LOAD("free_in_3d_v22.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(reference_roundtrip) {
+ CHECK_LOAD("reference.slvs");
+ CHECK_RENDER("reference.png");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v20) {
+ CHECK_LOAD("reference_v20.slvs");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v22) {
+ CHECK_LOAD("reference_v22.slvs");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_free_in_3d_roundtrip) {
+ CHECK_LOAD("reference_free_in_3d.slvs");
+ CHECK_RENDER("reference_free_in_3d.png");
+ CHECK_SAVE("reference_free_in_3d.slvs");
+}
+
+TEST_CASE(reference_free_in_3d_migrate_from_v20) {
+ CHECK_LOAD("reference_free_in_3d_v20.slvs");
+ CHECK_SAVE("reference_free_in_3d.slvs");
+}
+
+TEST_CASE(reference_free_in_3d_migrate_from_v22) {
+ CHECK_LOAD("reference_free_in_3d_v22.slvs");
+ CHECK_SAVE("reference_free_in_3d.slvs");
+}
+
+TEST_CASE(skew_render) {
+ CHECK_LOAD("skew.slvs");
+ CHECK_RENDER("skew.png");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=25.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=25.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=123
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040003
+Constraint.ptB.v=00060001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=123
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00060000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=25.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=25.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=123
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040003
+Constraint.ptB.v=00060001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=123
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00060000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=25.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=25.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=123
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040003
+Constraint.ptB.v=00060001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=123
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00060000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=70
+Constraint.group.v=00000002
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=70
+Constraint.group.v=00000002
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=70
+Constraint.group.v=00000002
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=70
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=70
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=70
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=70
+Constraint.group.v=00000002
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=70
+Constraint.group.v=00000002
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=70
+Constraint.group.v=00000002
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=70
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=70
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=70
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(line_pt_normal_roundtrip) {
+ CHECK_LOAD("line_pt_normal.slvs");
+ CHECK_RENDER("line_pt_normal.png");
+ CHECK_SAVE("line_pt_normal.slvs");
+}
+
+TEST_CASE(line_pt_normal_migrate_from_v20) {
+ CHECK_LOAD("line_pt_normal_v20.slvs");
+ CHECK_SAVE("line_pt_normal.slvs");
+}
+
+TEST_CASE(line_pt_normal_migrate_from_v22) {
+ CHECK_LOAD("line_pt_normal_v22.slvs");
+ CHECK_SAVE("line_pt_normal.slvs");
+}
+
+TEST_CASE(line_pt_free_in_3d_roundtrip) {
+ CHECK_LOAD("line_pt_free_in_3d.slvs");
+ CHECK_RENDER("line_pt_free_in_3d.png");
+ CHECK_SAVE("line_pt_free_in_3d.slvs");
+}
+
+TEST_CASE(line_pt_free_in_3d_migrate_from_v20) {
+ CHECK_LOAD("line_pt_free_in_3d_v20.slvs");
+ CHECK_SAVE("line_pt_free_in_3d.slvs");
+}
+
+TEST_CASE(line_pt_free_in_3d_migrate_from_v22) {
+ CHECK_LOAD("line_pt_free_in_3d_v22.slvs");
+ CHECK_SAVE("line_pt_free_in_3d.slvs");
+}
+
+TEST_CASE(line_plane_normal_roundtrip) {
+ CHECK_LOAD("line_plane_normal.slvs");
+ CHECK_RENDER("line_plane_normal.png");
+ CHECK_SAVE("line_plane_normal.slvs");
+}
+
+TEST_CASE(line_plane_normal_migrate_from_v20) {
+ CHECK_LOAD("line_plane_normal_v20.slvs");
+ CHECK_SAVE("line_plane_normal.slvs");
+}
+
+TEST_CASE(line_plane_normal_migrate_from_v22) {
+ CHECK_LOAD("line_plane_normal_v22.slvs");
+ CHECK_SAVE("line_plane_normal.slvs");
+}
+
+TEST_CASE(line_plane_free_in_3d_roundtrip) {
+ CHECK_LOAD("line_plane_free_in_3d.slvs");
+ CHECK_RENDER("line_plane_free_in_3d.png");
+ CHECK_SAVE("line_plane_free_in_3d.slvs");
+}
+
+TEST_CASE(line_plane_free_in_3d_migrate_from_v20) {
+ CHECK_LOAD("line_plane_free_in_3d_v20.slvs");
+ CHECK_SAVE("line_plane_free_in_3d.slvs");
+}
+
+TEST_CASE(line_plane_free_in_3d_migrate_from_v22) {
+ CHECK_LOAD("line_plane_free_in_3d_v22.slvs");
+ CHECK_SAVE("line_plane_free_in_3d.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=1000
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.comment=Text
+Constraint.disp.offset.x=-5.50000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=1000
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.comment=Text
+Constraint.disp.offset.x=-5.50000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=1000
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.comment=Text
+Constraint.disp.offset.x=-5.50000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00040016
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040018
+AddParam
+
+Param.h.v.=00040019
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=0004001a
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=0004001b
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050015
+AddParam
+
+Param.h.v.=40000002
+Param.val=-1.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=300
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=12000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00050001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=124
+Constraint.group.v=00000002
+Constraint.valP.v=40000002
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00040016
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040018
+AddParam
+
+Param.h.v.=00040019
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=0004001a
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=0004001b
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050015
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=300
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=12000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00050001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=124
+Constraint.group.v=00000002
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00040016
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040018
+AddParam
+
+Param.h.v.=00040019
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=0004001a
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=0004001b
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050015
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=300
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=12000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00050001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=124
+Constraint.group.v=00000002
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040013
+AddParam
+
+Param.h.v.=00040014
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+AddParam
+
+Param.h.v.=00040019
+AddParam
+
+Param.h.v.=0004001a
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+AddParam
+
+Param.h.v.=00060013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=300
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=12000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040004
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=124
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040001
+Constraint.ptB.v=00060001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=124
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040013
+AddParam
+
+Param.h.v.=00040014
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+AddParam
+
+Param.h.v.=00040019
+AddParam
+
+Param.h.v.=0004001a
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+AddParam
+
+Param.h.v.=00060013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=300
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=12000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040004
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=124
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040001
+Constraint.ptB.v=00060001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=124
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040013
+AddParam
+
+Param.h.v.=00040014
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+AddParam
+
+Param.h.v.=00040019
+AddParam
+
+Param.h.v.=0004001a
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+AddParam
+
+Param.h.v.=00060013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=300
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=12000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040004
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=124
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040001
+Constraint.ptB.v=00060001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=124
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(free_in_3d_roundtrip) {
+ CHECK_LOAD("free_in_3d.slvs");
+ CHECK_RENDER("free_in_3d.png");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v20) {
+ CHECK_LOAD("free_in_3d_v20.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v22) {
+ CHECK_LOAD("free_in_3d_v22.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-25.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050016
+Param.val=-25.00000000000000000000
+AddParam
+
+Param.h.v.=00050017
+Param.val=25.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.point[2].v=00050003
+Entity.normal.v=00050020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-25.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-25.00000000000000000000
+Entity.actPoint.y=25.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050002
+Constraint.ptB.v=00040002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=125
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-25.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050016
+Param.val=-25.00000000000000000000
+AddParam
+
+Param.h.v.=00050017
+Param.val=25.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.point[2].v=00050003
+Entity.normal.v=00050020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-25.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-25.00000000000000000000
+Entity.actPoint.y=25.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050002
+Constraint.ptB.v=00040002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=125
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-25.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050016
+Param.val=-25.00000000000000000000
+AddParam
+
+Param.h.v.=00050017
+Param.val=25.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.point[2].v=00050003
+Entity.normal.v=00050020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-25.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-25.00000000000000000000
+Entity.actPoint.y=25.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050002
+Constraint.ptB.v=00040002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=125
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050016
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050019
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=0005001a
+AddParam
+
+Param.h.v.=00060010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+AddParam
+
+Param.h.v.=00060016
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060017
+Param.val=-10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=300
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=12000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.point[2].v=00050003
+Entity.point[3].v=00050004
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.point[2].v=00060003
+Entity.normal.v=00060020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040003
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=125
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=1
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050004
+Constraint.ptB.v=00060002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=125
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00060000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050016
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050019
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=0005001a
+AddParam
+
+Param.h.v.=00060010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+AddParam
+
+Param.h.v.=00060016
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060017
+Param.val=-10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=300
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=12000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.point[2].v=00050003
+Entity.point[3].v=00050004
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.point[2].v=00060003
+Entity.normal.v=00060020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040003
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=125
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=1
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050004
+Constraint.ptB.v=00060002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=125
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00060000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050016
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050019
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=0005001a
+AddParam
+
+Param.h.v.=00060010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+AddParam
+
+Param.h.v.=00060016
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060017
+Param.val=-10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=300
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=12000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.point[2].v=00050003
+Entity.point[3].v=00050004
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.point[2].v=00060003
+Entity.normal.v=00060020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040003
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=125
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=1
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050004
+Constraint.ptB.v=00060002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=125
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00060000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(arc_arc_roundtrip) {
+ CHECK_LOAD("arc_arc.slvs");
+ CHECK_RENDER("arc_arc.png");
+ CHECK_SAVE("arc_arc.slvs");
+}
+
+TEST_CASE(arc_arc_migrate_from_v20) {
+ CHECK_LOAD("arc_arc_v20.slvs");
+ CHECK_SAVE("arc_arc.slvs");
+}
+
+TEST_CASE(arc_arc_migrate_from_v22) {
+ CHECK_LOAD("arc_arc_v22.slvs");
+ CHECK_SAVE("arc_arc.slvs");
+}
+
+TEST_CASE(arc_cubic_roundtrip) {
+ CHECK_LOAD("arc_cubic.slvs");
+ CHECK_RENDER("arc_cubic.png");
+ CHECK_SAVE("arc_cubic.slvs");
+}
+
+TEST_CASE(arc_cubic_migrate_from_v20) {
+ CHECK_LOAD("arc_cubic_v20.slvs");
+ CHECK_SAVE("arc_cubic.slvs");
+}
+
+TEST_CASE(arc_cubic_migrate_from_v22) {
+ CHECK_LOAD("arc_cubic_v22.slvs");
+ CHECK_SAVE("arc_cubic.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=90
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=10.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=5.00000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=90
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=10.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=5.00000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=90
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=10.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=5.00000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=90
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=10.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.x=5.00000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=90
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=10.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.x=5.00000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=90
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=10.00000000000000000000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.x=5.00000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(reference_roundtrip) {
+ CHECK_LOAD("reference.slvs");
+ CHECK_RENDER("reference.png");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v20) {
+ CHECK_LOAD("reference_v20.slvs");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v22) {
+ CHECK_LOAD("reference_v22.slvs");
+ CHECK_SAVE("reference.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=52
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00060000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=52
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00060000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=52
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00060000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=53
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00070000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=53
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00070000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=53
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00070000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00070014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00070001
+Entity.point[1].v=00070002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00060002
+Constraint.ptB.v=00070001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=54
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.entityC.v=00070000
+Constraint.entityD.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00070014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00070001
+Entity.point[1].v=00070002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00060002
+Constraint.ptB.v=00070001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=54
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.entityC.v=00070000
+Constraint.entityD.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00070014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00070001
+Entity.point[1].v=00070002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00060002
+Constraint.ptB.v=00070001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=54
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.entityC.v=00070000
+Constraint.entityD.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070013
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00070014
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00070001
+Entity.point[1].v=00070002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00060002
+Constraint.ptB.v=00070001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=54
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.entityC.v=00070000
+Constraint.entityD.v=00060000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070013
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00070014
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00070001
+Entity.point[1].v=00070002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00060002
+Constraint.ptB.v=00070001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=54
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.entityC.v=00070000
+Constraint.entityD.v=00060000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070013
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00070014
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00070001
+Entity.point[1].v=00070002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040002
+Constraint.ptB.v=00050001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00060002
+Constraint.ptB.v=00070001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=54
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.entityC.v=00070000
+Constraint.entityD.v=00060000
+Constraint.other=1
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(other_roundtrip) {
+ CHECK_LOAD("other.slvs");
+ CHECK_RENDER("other.png");
+ CHECK_SAVE("other.slvs");
+}
+
+TEST_CASE(other_migrate_from_v20) {
+ CHECK_LOAD("other_v20.slvs");
+ CHECK_SAVE("other.slvs");
+}
+
+TEST_CASE(other_migrate_from_v22) {
+ CHECK_LOAD("other_v22.slvs");
+ CHECK_SAVE("other.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=50
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=50
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=50
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-4.99993226121819223096
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00006773878180865722
+AddParam
+
+Param.h.v.=00040013
+Param.val=-9.99975209012342425297
+AddParam
+
+Param.h.v.=00040014
+Param.val=9.99968435134161737210
+AddParam
+
+Param.h.v.=00040016
+Param.val=-5.00031564865838440426
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00024790987657663521
+AddParam
+
+Param.h.v.=00050010
+Param.val=4.99990506249655908277
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=12.85283693258537773829
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-4.99993226121819223096
+Entity.actPoint.y=10.00006773878180865722
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-9.99975209012342425297
+Entity.actPoint.y=9.99968435134161737210
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00031564865838440426
+Entity.actPoint.y=5.00024790987657663521
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=4.99990506249655908277
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=12.85283693258537773829
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=55
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-4.99993226121819220000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00006773878180900000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-9.99975209012342430000
+AddParam
+
+Param.h.v.=00040014
+Param.val=9.99968435134161740000
+AddParam
+
+Param.h.v.=00040016
+Param.val=-5.00031564865838440000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00024790987657660000
+AddParam
+
+Param.h.v.=00050010
+Param.val=4.99990506249655910000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=12.85283693258537800000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-4.99993226121819220000
+Entity.actPoint.y=10.00006773878180900000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-9.99975209012342430000
+Entity.actPoint.y=9.99968435134161740000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00031564865838440000
+Entity.actPoint.y=5.00024790987657660000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=4.99990506249655910000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=12.85283693258537800000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=55
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-4.99993226121819220000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00006773878180900000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-9.99975209012342430000
+AddParam
+
+Param.h.v.=00040014
+Param.val=9.99968435134161740000
+AddParam
+
+Param.h.v.=00040016
+Param.val=-5.00031564865838440000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00024790987657660000
+AddParam
+
+Param.h.v.=00050010
+Param.val=4.99990506249655910000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=12.85283693258537800000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-4.99993226121819220000
+Entity.actPoint.y=10.00006773878180900000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-9.99975209012342430000
+Entity.actPoint.y=9.99968435134161740000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00031564865838440000
+Entity.actPoint.y=5.00024790987657660000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=4.99990506249655910000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=12.85283693258537800000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=55
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-4.99999651845403381145
+AddParam
+
+Param.h.v.=00040011
+Param.val=9.99999682365714548382
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000349810587607635
+AddParam
+
+Param.h.v.=00040014
+Param.val=14.99999363650495176614
+AddParam
+
+Param.h.v.=00040016
+Param.val=-5.00000000434181757214
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000001080568345913
+AddParam
+
+Param.h.v.=00050010
+Param.val=4.99998746596540222242
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=20.70793025565613021399
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-4.99999651845403381145
+Entity.actPoint.y=9.99999682365714548382
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000349810587607635
+Entity.actPoint.y=14.99999363650495176614
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000434181757214
+Entity.actPoint.y=5.00000001080568345913
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=4.99998746596540222242
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=20.70793025565613021399
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=55
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00017546830115566792
+AddParam
+
+Param.h.v.=00040014
+Param.val=14.99944877586899494304
+AddParam
+
+Param.h.v.=00040016
+Param.val=-3.60558569714161292197
+AddParam
+
+Param.h.v.=00040017
+Param.val=14.80105163951722957449
+AddParam
+
+Param.h.v.=00050010
+Param.val=-14.99961021487080792269
+AddParam
+
+Param.h.v.=00050011
+Param.val=-5.00000098153893191011
+AddParam
+
+Param.h.v.=00050013
+Param.val=14.99952004548601536271
+AddParam
+
+Param.h.v.=00050014
+Param.val=-5.00000000001570388264
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00017546830115566792
+Entity.actPoint.y=14.99944877586899494304
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-3.60558569714161292197
+Entity.actPoint.y=14.80105163951722957449
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-14.99961021487080792269
+Entity.actPoint.y=-5.00000098153893191011
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=14.99952004548601536271
+Entity.actPoint.y=-5.00000000001570388264
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=55
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(pi) {
+ CHECK_LOAD("pi.slvs");
+ CHECK_RENDER("pi.png");
+}
+
+TEST_CASE(tau) {
+ CHECK_LOAD("tau.slvs");
+ CHECK_RENDER("tau.png");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.normal.v=00050020
+Entity.distance.v=00050040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=130
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.normal.v=00050020
+Entity.distance.v=00050040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=130
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.normal.v=00050020
+Entity.distance.v=00050040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=130
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(line_roundtrip) {
+ CHECK_LOAD("line.slvs");
+ CHECK_RENDER("line.png");
+ CHECK_SAVE("line.slvs");
+}
+
+TEST_CASE(line_migrate_from_v20) {
+ CHECK_LOAD("line_v20.slvs");
+ CHECK_SAVE("line.slvs");
+}
+
+TEST_CASE(line_migrate_from_v22) {
+ CHECK_LOAD("line_v22.slvs");
+ CHECK_SAVE("line.slvs");
+}
+
+TEST_CASE(pt_pt_roundtrip) {
+ CHECK_LOAD("pt_pt.slvs");
+ CHECK_RENDER("pt_pt.png");
+ CHECK_SAVE("pt_pt.slvs");
+}
+
+TEST_CASE(pt_pt_migrate_from_v20) {
+ CHECK_LOAD("pt_pt_v20.slvs");
+ CHECK_SAVE("pt_pt.slvs");
+}
+
+TEST_CASE(pt_pt_migrate_from_v22) {
+ CHECK_LOAD("pt_pt_v22.slvs");
+ CHECK_SAVE("pt_pt.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=56
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=-5.00000000000000000000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.y=10.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=56
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=-5.00000000000000000000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.y=10.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=56
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=-5.00000000000000000000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.y=10.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=56
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=-5.00000000000000000000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.y=10.00000000000000000000
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(reference_roundtrip) {
+ CHECK_LOAD("reference.slvs");
+ CHECK_RENDER("reference.png");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v22) {
+ CHECK_LOAD("reference_v22.slvs");
+ CHECK_SAVE("reference.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=51
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=0.50000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=-10.00000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=51
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=0.50000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=-10.00000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=51
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=0.50000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=-10.00000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=51
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=0.50000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.x=-10.00000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=51
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=0.50000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.x=-10.00000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=51
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=0.50000000000000000000
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.x=-10.00000000000000000000
+Constraint.disp.offset.y=5.00000000000000000000
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(reference_roundtrip) {
+ CHECK_LOAD("reference.slvs");
+ CHECK_RENDER("reference.png");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v20) {
+ CHECK_LOAD("reference_v20.slvs");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v22) {
+ CHECK_LOAD("reference_v22.slvs");
+ CHECK_SAVE("reference.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=40000001
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=121
+Constraint.group.v=00000002
+Constraint.valP.v=40000001
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00030020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=121
+Constraint.group.v=00000002
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00030020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=121
+Constraint.group.v=00000002
+Constraint.entityA.v=00040000
+Constraint.entityB.v=00030020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=121
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=121
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=15.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=121
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(free_in_3d_roundtrip) {
+ CHECK_LOAD("free_in_3d.slvs");
+ CHECK_RENDER("free_in_3d.png");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v20) {
+ CHECK_LOAD("free_in_3d_v20.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v22) {
+ CHECK_LOAD("free_in_3d_v22.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=122
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=122
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=122
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.entityB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(free_in_3d_roundtrip) {
+ CHECK_LOAD("free_in_3d.slvs");
+ CHECK_RENDER("free_in_3d.png");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v20) {
+ CHECK_LOAD("free_in_3d_v20.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v22) {
+ CHECK_LOAD("free_in_3d_v22.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=34
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.entityA.v=00020020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.y=-4.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=34
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.entityA.v=00020020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=0.00000000000000000000
+Constraint.disp.offset.y=-4.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=34
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.entityA.v=00020020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.y=-4.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=34
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.entityA.v=00020020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.y=-4.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=34
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.entityA.v=00020020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.y=-4.00000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=34
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.entityA.v=00020020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.y=-4.00000000000000000000
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(reference_roundtrip) {
+ CHECK_LOAD("reference.slvs");
+ CHECK_RENDER("reference.png");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v20) {
+ CHECK_LOAD("reference_v20.slvs");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v22) {
+ CHECK_LOAD("reference_v22.slvs");
+ CHECK_SAVE("reference.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=00020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000003
+Group.type=5100
+Group.order=2
+Group.name=extrude
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000002
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.predef.entityB.v=00020000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00040000 1002
+ 2 00040001 1002
+ 3 00040020 1002
+ 4 00040040 1002
+ 5 00040000 1001
+ 6 00040001 1001
+ 7 00040020 1001
+ 8 00040040 1001
+ 9 00040001 1003
+ 10 80020000 1002
+ 11 80020000 1001
+ 12 80020001 1002
+ 13 80020002 1002
+ 14 80020001 1001
+ 15 80020002 1001
+ 16 80020002 1003
+ 17 00000000 1001
+ 18 00000000 1002
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030000
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030001
+AddParam
+
+Param.h.v.=80030002
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=00020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=00010000
+Request.group.v=00000003
+Request.construction=1
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=00020000
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=1
+Entity.workplane.v=00010000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030001
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.normal.v=80030003
+Entity.distance.v=80030004
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030004
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.normal.v=80030007
+Entity.distance.v=80030008
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030007
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.point[1].v=80030002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=2010
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8003000f
+Entity.point[1].v=8003000d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actPoint.x=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=00020000
+Constraint.ptA.v=00010001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=33
+Constraint.group.v=00000003
+Constraint.workplane.v=00010000
+Constraint.valA=-10.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=80030012
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Surface 00000001 00646464 80030012 1 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000a 0 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -0.00000000000000088818
+TrimBy 00000001 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000004 0 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000044409
+TrimBy 00000007 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000044409 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000000000
+AddSurface
+Surface 00000002 00646464 80030011 1 1
+SCtrl 0 0 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000005 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000044409 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000002 1 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000b 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000088818 10.00000000000000000000 -0.00000000000000044409 -5.00000000000000000000
+TrimBy 00000008 1 10.00000000000000000000 -0.00000000000000044409 -5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000044409
+AddSurface
+Surface 00000003 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000002 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000001 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000c 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000003 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddSurface
+Surface 00000004 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+TrimBy 00000005 0 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000004 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000003 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000006 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddSurface
+Surface 00000005 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000008 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000007 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000006 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000009 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddSurface
+Surface 00000006 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+TrimBy 0000000b 0 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+TrimBy 0000000a 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000009 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 0000000c 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddSurface
+Curve 00000001 1 2 00000001 00000003
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 0.00000000000000000000 4.91652684208287205081 0.90981526206072360630
+CurvePt 0 0.00000000000000000000 4.64894150531215188948 1.84047354780936389673
+CurvePt 0 0.00000000000000000000 4.18497755609325672310 2.73604876692571252761
+CurvePt 0 0.00000000000000000000 3.53553390593273775266 3.53553390593273775266
+CurvePt 0 0.00000000000000000000 2.73604876692571252761 4.18497755609325672310
+CurvePt 0 0.00000000000000000000 1.84047354780936411878 4.64894150531215188948
+CurvePt 0 0.00000000000000000000 0.90981526206072382834 4.91652684208287205081
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000002 1 2 00000002 00000003
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 10.00000000000000000000 4.91652684208287205081 0.90981526206072360630
+CurvePt 0 10.00000000000000000000 4.64894150531215188948 1.84047354780936389673
+CurvePt 0 10.00000000000000000000 4.18497755609325672310 2.73604876692571252761
+CurvePt 0 10.00000000000000000000 3.53553390593273775266 3.53553390593273775266
+CurvePt 0 10.00000000000000000000 2.73604876692571252761 4.18497755609325672310
+CurvePt 0 10.00000000000000000000 1.84047354780936411878 4.64894150531215188948
+CurvePt 0 10.00000000000000000000 0.90981526206072382834 4.91652684208287205081
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000003 1 1 00000003 00000004
+CCtrl 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000004 1 2 00000001 00000004
+CCtrl 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 0 0.00000000000000000000 -0.90981526206072327323 4.91652684208287205081
+CurvePt 0 0.00000000000000000000 -1.84047354780936323060 4.64894150531215188948
+CurvePt 0 0.00000000000000000000 -2.73604876692571208352 4.18497755609325672310
+CurvePt 0 0.00000000000000000000 -3.53553390593273775266 3.53553390593273775266
+CurvePt 0 0.00000000000000000000 -4.18497755609325672310 2.73604876692571297170
+CurvePt 0 0.00000000000000000000 -4.64894150531215188948 1.84047354780936434082
+CurvePt 0 0.00000000000000000000 -4.91652684208287205081 0.90981526206072405039
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000005 1 2 00000002 00000004
+CCtrl 0 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 0 10.00000000000000000000 -0.90981526206072327323 4.91652684208287205081
+CurvePt 0 10.00000000000000000000 -1.84047354780936323060 4.64894150531215188948
+CurvePt 0 10.00000000000000000000 -2.73604876692571208352 4.18497755609325672310
+CurvePt 0 10.00000000000000000000 -3.53553390593273775266 3.53553390593273775266
+CurvePt 0 10.00000000000000000000 -4.18497755609325672310 2.73604876692571297170
+CurvePt 0 10.00000000000000000000 -4.64894150531215188948 1.84047354780936434082
+CurvePt 0 10.00000000000000000000 -4.91652684208287205081 0.90981526206072405039
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000006 1 1 00000004 00000005
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000007 1 2 00000001 00000005
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 0 0.00000000000000000000 -4.91652684208287205081 -0.90981526206072305119
+CurvePt 0 0.00000000000000000000 -4.64894150531215188948 -1.84047354780936300855
+CurvePt 0 0.00000000000000000000 -4.18497755609325672310 -2.73604876692571208352
+CurvePt 0 0.00000000000000000000 -3.53553390593273864084 -3.53553390593273730858
+CurvePt 0 0.00000000000000000000 -2.73604876692571297170 -4.18497755609325672310
+CurvePt 0 0.00000000000000000000 -1.84047354780936478491 -4.64894150531215188948
+CurvePt 0 0.00000000000000000000 -0.90981526206072438345 -4.91652684208287205081
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 00000008 1 2 00000002 00000005
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 0 10.00000000000000000000 -4.91652684208287205081 -0.90981526206072305119
+CurvePt 0 10.00000000000000000000 -4.64894150531215188948 -1.84047354780936300855
+CurvePt 0 10.00000000000000000000 -4.18497755609325672310 -2.73604876692571208352
+CurvePt 0 10.00000000000000000000 -3.53553390593273864084 -3.53553390593273730858
+CurvePt 0 10.00000000000000000000 -2.73604876692571297170 -4.18497755609325672310
+CurvePt 0 10.00000000000000000000 -1.84047354780936478491 -4.64894150531215188948
+CurvePt 0 10.00000000000000000000 -0.90981526206072438345 -4.91652684208287205081
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 00000009 1 1 00000005 00000006
+CCtrl 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 0000000a 1 2 00000001 00000006
+CCtrl 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 0 0.00000000000000000000 0.90981526206072282914 -4.91652684208287205081
+CurvePt 0 0.00000000000000000000 1.84047354780936300855 -4.64894150531215188948
+CurvePt 0 0.00000000000000000000 2.73604876692571163943 -4.18497755609325672310
+CurvePt 0 0.00000000000000000000 3.53553390593273730858 -3.53553390593273864084
+CurvePt 0 0.00000000000000000000 4.18497755609325583492 -2.73604876692571297170
+CurvePt 0 0.00000000000000000000 4.64894150531215188948 -1.84047354780936500696
+CurvePt 0 0.00000000000000000000 4.91652684208287205081 -0.90981526206072471652
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
+Curve 0000000b 1 2 00000002 00000006
+CCtrl 0 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 0 10.00000000000000000000 0.90981526206072282914 -4.91652684208287205081
+CurvePt 0 10.00000000000000000000 1.84047354780936300855 -4.64894150531215188948
+CurvePt 0 10.00000000000000000000 2.73604876692571163943 -4.18497755609325672310
+CurvePt 0 10.00000000000000000000 3.53553390593273730858 -3.53553390593273864084
+CurvePt 0 10.00000000000000000000 4.18497755609325583492 -2.73604876692571297170
+CurvePt 0 10.00000000000000000000 4.64894150531215188948 -1.84047354780936500696
+CurvePt 0 10.00000000000000000000 4.91652684208287205081 -0.90981526206072471652
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
+Curve 0000000c 1 1 00000006 00000003
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=00020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000003
+Group.type=5100
+Group.order=2
+Group.name=extrude
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000002
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.predef.entityB.v=00020000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00040000 1002
+ 2 00040001 1002
+ 3 00040020 1002
+ 4 00040040 1002
+ 5 00040000 1001
+ 6 00040001 1001
+ 7 00040020 1001
+ 8 00040040 1001
+ 9 00040001 1003
+ 10 80020000 1002
+ 11 80020000 1001
+ 12 80020001 1002
+ 13 80020002 1002
+ 14 80020001 1001
+ 15 80020002 1001
+ 16 80020002 1003
+ 17 00000000 1001
+ 18 00000000 1002
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030000
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030001
+AddParam
+
+Param.h.v.=80030002
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=00020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=00010000
+Request.group.v=00000003
+Request.construction=1
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=00020000
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00010000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030001
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.normal.v=80030003
+Entity.distance.v=80030004
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030004
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.normal.v=80030007
+Entity.distance.v=80030008
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030007
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.point[1].v=80030002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.point[1].v=8003000d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actPoint.x=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=00020000
+Constraint.ptA.v=00010001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=33
+Constraint.group.v=00000003
+Constraint.workplane.v=00010000
+Constraint.valA=-10.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=80030012
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Surface 00000001 00646464 80030012 1 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000a 0 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -0.00000000000000088818
+TrimBy 00000001 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000004 0 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000044409
+TrimBy 00000007 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000044409 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000000000
+AddSurface
+Surface 00000002 00646464 80030011 1 1
+SCtrl 0 0 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000005 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000044409 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000002 1 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000b 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000088818 10.00000000000000000000 -0.00000000000000044409 -5.00000000000000000000
+TrimBy 00000008 1 10.00000000000000000000 -0.00000000000000044409 -5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000044409
+AddSurface
+Surface 00000003 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 2 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000002 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000003 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000001 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000c 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000004 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -4.99999999999999910000 5.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 1 1 10.00000000000000000000 -4.99999999999999910000 5.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 2 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+TrimBy 00000005 0 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000006 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000004 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000003 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddSurface
+Surface 00000005 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -5.00000000000000090000 -4.99999999999999910000 Weight 0.70710678118654757000
+SCtrl 1 1 10.00000000000000000000 -5.00000000000000090000 -4.99999999999999910000 Weight 0.70710678118654757000
+SCtrl 2 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000008 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000009 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000007 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000006 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddSurface
+Surface 00000006 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 4.99999999999999910000 -5.00000000000000090000 Weight 0.70710678118654757000
+SCtrl 1 1 10.00000000000000000000 4.99999999999999910000 -5.00000000000000090000 Weight 0.70710678118654757000
+SCtrl 2 0 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+TrimBy 0000000b 0 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+TrimBy 0000000c 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+TrimBy 0000000a 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000009 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddSurface
+Curve 00000001 1 2 00000001 00000003
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 0.00000000000000000000 4.91652684208287210000 0.90981526206072361000
+CurvePt 0 0.00000000000000000000 4.64894150531215190000 1.84047354780936390000
+CurvePt 0 0.00000000000000000000 4.18497755609325670000 2.73604876692571250000
+CurvePt 0 0.00000000000000000000 3.53553390593273780000 3.53553390593273780000
+CurvePt 0 0.00000000000000000000 2.73604876692571250000 4.18497755609325670000
+CurvePt 0 0.00000000000000000000 1.84047354780936410000 4.64894150531215190000
+CurvePt 0 0.00000000000000000000 0.90981526206072383000 4.91652684208287210000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000002 1 2 00000002 00000003
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 10.00000000000000000000 4.91652684208287210000 0.90981526206072361000
+CurvePt 0 10.00000000000000000000 4.64894150531215190000 1.84047354780936390000
+CurvePt 0 10.00000000000000000000 4.18497755609325670000 2.73604876692571250000
+CurvePt 0 10.00000000000000000000 3.53553390593273780000 3.53553390593273780000
+CurvePt 0 10.00000000000000000000 2.73604876692571250000 4.18497755609325670000
+CurvePt 0 10.00000000000000000000 1.84047354780936410000 4.64894150531215190000
+CurvePt 0 10.00000000000000000000 0.90981526206072383000 4.91652684208287210000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000003 1 1 00000003 00000004
+CCtrl 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000004 1 2 00000001 00000004
+CCtrl 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -4.99999999999999910000 5.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 0 0.00000000000000000000 -0.90981526206072327000 4.91652684208287210000
+CurvePt 0 0.00000000000000000000 -1.84047354780936320000 4.64894150531215190000
+CurvePt 0 0.00000000000000000000 -2.73604876692571210000 4.18497755609325670000
+CurvePt 0 0.00000000000000000000 -3.53553390593273780000 3.53553390593273780000
+CurvePt 0 0.00000000000000000000 -4.18497755609325670000 2.73604876692571300000
+CurvePt 0 0.00000000000000000000 -4.64894150531215190000 1.84047354780936430000
+CurvePt 0 0.00000000000000000000 -4.91652684208287210000 0.90981526206072405000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000005 1 2 00000002 00000004
+CCtrl 0 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -4.99999999999999910000 5.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 0 10.00000000000000000000 -0.90981526206072327000 4.91652684208287210000
+CurvePt 0 10.00000000000000000000 -1.84047354780936320000 4.64894150531215190000
+CurvePt 0 10.00000000000000000000 -2.73604876692571210000 4.18497755609325670000
+CurvePt 0 10.00000000000000000000 -3.53553390593273780000 3.53553390593273780000
+CurvePt 0 10.00000000000000000000 -4.18497755609325670000 2.73604876692571300000
+CurvePt 0 10.00000000000000000000 -4.64894150531215190000 1.84047354780936430000
+CurvePt 0 10.00000000000000000000 -4.91652684208287210000 0.90981526206072405000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000006 1 1 00000004 00000005
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000007 1 2 00000001 00000005
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000090000 -4.99999999999999910000 Weight 0.70710678118654757000
+CCtrl 2 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 0 0.00000000000000000000 -4.91652684208287210000 -0.90981526206072305000
+CurvePt 0 0.00000000000000000000 -4.64894150531215190000 -1.84047354780936300000
+CurvePt 0 0.00000000000000000000 -4.18497755609325670000 -2.73604876692571210000
+CurvePt 0 0.00000000000000000000 -3.53553390593273860000 -3.53553390593273730000
+CurvePt 0 0.00000000000000000000 -2.73604876692571300000 -4.18497755609325670000
+CurvePt 0 0.00000000000000000000 -1.84047354780936480000 -4.64894150531215190000
+CurvePt 0 0.00000000000000000000 -0.90981526206072438000 -4.91652684208287210000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 00000008 1 2 00000002 00000005
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000090000 -4.99999999999999910000 Weight 0.70710678118654757000
+CCtrl 2 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 0 10.00000000000000000000 -4.91652684208287210000 -0.90981526206072305000
+CurvePt 0 10.00000000000000000000 -4.64894150531215190000 -1.84047354780936300000
+CurvePt 0 10.00000000000000000000 -4.18497755609325670000 -2.73604876692571210000
+CurvePt 0 10.00000000000000000000 -3.53553390593273860000 -3.53553390593273730000
+CurvePt 0 10.00000000000000000000 -2.73604876692571300000 -4.18497755609325670000
+CurvePt 0 10.00000000000000000000 -1.84047354780936480000 -4.64894150531215190000
+CurvePt 0 10.00000000000000000000 -0.90981526206072438000 -4.91652684208287210000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 00000009 1 1 00000005 00000006
+CCtrl 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 0000000a 1 2 00000001 00000006
+CCtrl 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 4.99999999999999910000 -5.00000000000000090000 Weight 0.70710678118654757000
+CCtrl 2 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 0 0.00000000000000000000 0.90981526206072283000 -4.91652684208287210000
+CurvePt 0 0.00000000000000000000 1.84047354780936300000 -4.64894150531215190000
+CurvePt 0 0.00000000000000000000 2.73604876692571160000 -4.18497755609325670000
+CurvePt 0 0.00000000000000000000 3.53553390593273730000 -3.53553390593273860000
+CurvePt 0 0.00000000000000000000 4.18497755609325580000 -2.73604876692571300000
+CurvePt 0 0.00000000000000000000 4.64894150531215190000 -1.84047354780936500000
+CurvePt 0 0.00000000000000000000 4.91652684208287210000 -0.90981526206072472000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
+Curve 0000000b 1 2 00000002 00000006
+CCtrl 0 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 4.99999999999999910000 -5.00000000000000090000 Weight 0.70710678118654757000
+CCtrl 2 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 0 10.00000000000000000000 0.90981526206072283000 -4.91652684208287210000
+CurvePt 0 10.00000000000000000000 1.84047354780936300000 -4.64894150531215190000
+CurvePt 0 10.00000000000000000000 2.73604876692571160000 -4.18497755609325670000
+CurvePt 0 10.00000000000000000000 3.53553390593273730000 -3.53553390593273860000
+CurvePt 0 10.00000000000000000000 4.18497755609325580000 -2.73604876692571300000
+CurvePt 0 10.00000000000000000000 4.64894150531215190000 -1.84047354780936500000
+CurvePt 0 10.00000000000000000000 4.91652684208287210000 -0.90981526206072472000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
+Curve 0000000c 1 1 00000006 00000003
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=00020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000003
+Group.type=5100
+Group.order=2
+Group.name=extrude
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000002
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.predef.entityB.v=00020000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00040000 1002
+ 2 00040001 1002
+ 3 00040020 1002
+ 4 00040040 1002
+ 5 00040000 1001
+ 6 00040001 1001
+ 7 00040020 1001
+ 8 00040040 1001
+ 9 00040001 1003
+ 10 80020000 1002
+ 11 80020000 1001
+ 12 80020001 1002
+ 13 80020002 1002
+ 14 80020001 1001
+ 15 80020002 1001
+ 16 80020002 1003
+ 17 00000000 1001
+ 18 00000000 1002
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030000
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030001
+AddParam
+
+Param.h.v.=80030002
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=00020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=00010000
+Request.group.v=00000003
+Request.construction=1
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=00020000
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00010000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030001
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.normal.v=80030003
+Entity.distance.v=80030004
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030004
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.normal.v=80030007
+Entity.distance.v=80030008
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030007
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.point[1].v=80030002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.point[1].v=8003000d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actPoint.x=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=00020000
+Constraint.ptA.v=00010001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=33
+Constraint.group.v=00000003
+Constraint.workplane.v=00010000
+Constraint.valA=-10.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=80030012
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Surface 00000001 00646464 80030012 1 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000a 0 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -0.00000000000000088818
+TrimBy 00000001 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000004 0 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000044409
+TrimBy 00000007 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000044409 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000000000
+AddSurface
+Surface 00000002 00646464 80030011 1 1
+SCtrl 0 0 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000005 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000044409 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000002 1 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000b 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000088818 10.00000000000000000000 -0.00000000000000044409 -5.00000000000000000000
+TrimBy 00000008 1 10.00000000000000000000 -0.00000000000000044409 -5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000044409
+AddSurface
+Surface 00000003 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000002 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000001 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000c 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000003 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddSurface
+Surface 00000004 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+TrimBy 00000005 0 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000004 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000003 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000006 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddSurface
+Surface 00000005 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000008 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000007 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000006 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000009 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddSurface
+Surface 00000006 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+TrimBy 0000000b 0 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+TrimBy 0000000a 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000009 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 0000000c 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddSurface
+Curve 00000001 1 2 00000001 00000003
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 0.00000000000000000000 4.64894150531215188948 1.84047354780936389673
+CurvePt 0 0.00000000000000000000 3.53553390593273775266 3.53553390593273775266
+CurvePt 0 0.00000000000000000000 1.84047354780936411878 4.64894150531215188948
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000002 1 2 00000002 00000003
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 10.00000000000000000000 4.64894150531215188948 1.84047354780936389673
+CurvePt 0 10.00000000000000000000 3.53553390593273775266 3.53553390593273775266
+CurvePt 0 10.00000000000000000000 1.84047354780936411878 4.64894150531215188948
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000003 1 1 00000003 00000004
+CCtrl 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000004 1 2 00000001 00000004
+CCtrl 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 0 0.00000000000000000000 -1.84047354780936323060 4.64894150531215188948
+CurvePt 0 0.00000000000000000000 -3.53553390593273775266 3.53553390593273775266
+CurvePt 0 0.00000000000000000000 -4.64894150531215188948 1.84047354780936434082
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000005 1 2 00000002 00000004
+CCtrl 0 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 0 10.00000000000000000000 -1.84047354780936323060 4.64894150531215188948
+CurvePt 0 10.00000000000000000000 -3.53553390593273775266 3.53553390593273775266
+CurvePt 0 10.00000000000000000000 -4.64894150531215188948 1.84047354780936434082
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000006 1 1 00000004 00000005
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000007 1 2 00000001 00000005
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 0 0.00000000000000000000 -4.64894150531215188948 -1.84047354780936300855
+CurvePt 0 0.00000000000000000000 -3.53553390593273864084 -3.53553390593273730858
+CurvePt 0 0.00000000000000000000 -1.84047354780936478491 -4.64894150531215188948
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 00000008 1 2 00000002 00000005
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 0 10.00000000000000000000 -4.64894150531215188948 -1.84047354780936300855
+CurvePt 0 10.00000000000000000000 -3.53553390593273864084 -3.53553390593273730858
+CurvePt 0 10.00000000000000000000 -1.84047354780936478491 -4.64894150531215188948
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 00000009 1 1 00000005 00000006
+CCtrl 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 0000000a 1 2 00000001 00000006
+CCtrl 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 0 0.00000000000000000000 1.84047354780936300855 -4.64894150531215188948
+CurvePt 0 0.00000000000000000000 3.53553390593273730858 -3.53553390593273864084
+CurvePt 0 0.00000000000000000000 4.64894150531215188948 -1.84047354780936500696
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
+Curve 0000000b 1 2 00000002 00000006
+CCtrl 0 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 0 10.00000000000000000000 1.84047354780936300855 -4.64894150531215188948
+CurvePt 0 10.00000000000000000000 3.53553390593273730858 -3.53553390593273864084
+CurvePt 0 10.00000000000000000000 4.64894150531215188948 -1.84047354780936500696
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
+Curve 0000000c 1 1 00000006 00000003
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=00020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000003
+Group.type=5100
+Group.order=2
+Group.name=extrude
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000002
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.predef.entityB.v=00020000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00040000 1002
+ 2 00040001 1002
+ 3 00040020 1002
+ 4 00040040 1002
+ 5 00040000 1001
+ 6 00040001 1001
+ 7 00040020 1001
+ 8 00040040 1001
+ 9 00040001 1003
+ 10 80020000 1002
+ 11 80020000 1001
+ 12 80020001 1002
+ 13 80020002 1002
+ 14 80020001 1001
+ 15 80020002 1001
+ 16 80020002 1003
+ 17 00000000 1001
+ 18 00000000 1002
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030000
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030001
+AddParam
+
+Param.h.v.=80030002
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=00020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=00010000
+Request.group.v=00000003
+Request.construction=1
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=00020000
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=1
+Entity.workplane.v=00010000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030001
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.normal.v=80030003
+Entity.distance.v=80030004
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030004
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.normal.v=80030007
+Entity.distance.v=80030008
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030007
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.point[1].v=80030002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=2010
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8003000f
+Entity.point[1].v=8003000d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actPoint.x=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=00020000
+Constraint.ptA.v=00010001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=33
+Constraint.group.v=00000003
+Constraint.workplane.v=00010000
+Constraint.valA=-10.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=80030012
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+AddConstraint
+
+Surface 00000001 00646464 80030012 1 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000a 0 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -0.00000000000000088818
+TrimBy 00000001 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000004 0 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000044409
+TrimBy 00000007 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000044409 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000000000
+AddSurface
+Surface 00000002 00646464 80030011 1 1
+SCtrl 0 0 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000005 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000044409 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000002 1 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000b 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000088818 10.00000000000000000000 -0.00000000000000044409 -5.00000000000000000000
+TrimBy 00000008 1 10.00000000000000000000 -0.00000000000000044409 -5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000044409
+AddSurface
+Surface 00000003 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000002 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000001 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000c 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000003 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddSurface
+Surface 00000004 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+TrimBy 00000005 0 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000004 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000003 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000006 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddSurface
+Surface 00000005 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000008 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000007 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000006 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000009 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddSurface
+Surface 00000006 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+TrimBy 0000000b 0 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+TrimBy 0000000a 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000009 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 0000000c 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddSurface
+Curve 00000001 1 2 00000001 00000003
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 0.00000000000000000000 4.91652684208287205081 0.90981526206072360630
+CurvePt 0 0.00000000000000000000 4.64894150531215188948 1.84047354780936389673
+CurvePt 0 0.00000000000000000000 4.18497755609325672310 2.73604876692571252761
+CurvePt 0 0.00000000000000000000 3.53553390593273775266 3.53553390593273775266
+CurvePt 0 0.00000000000000000000 2.73604876692571252761 4.18497755609325672310
+CurvePt 0 0.00000000000000000000 1.84047354780936411878 4.64894150531215188948
+CurvePt 0 0.00000000000000000000 0.90981526206072382834 4.91652684208287205081
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000002 1 2 00000002 00000003
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 10.00000000000000000000 4.91652684208287205081 0.90981526206072360630
+CurvePt 0 10.00000000000000000000 4.64894150531215188948 1.84047354780936389673
+CurvePt 0 10.00000000000000000000 4.18497755609325672310 2.73604876692571252761
+CurvePt 0 10.00000000000000000000 3.53553390593273775266 3.53553390593273775266
+CurvePt 0 10.00000000000000000000 2.73604876692571252761 4.18497755609325672310
+CurvePt 0 10.00000000000000000000 1.84047354780936411878 4.64894150531215188948
+CurvePt 0 10.00000000000000000000 0.90981526206072382834 4.91652684208287205081
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000003 1 1 00000003 00000004
+CCtrl 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000004 1 2 00000001 00000004
+CCtrl 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 0 0.00000000000000000000 -0.90981526206072327323 4.91652684208287205081
+CurvePt 0 0.00000000000000000000 -1.84047354780936323060 4.64894150531215188948
+CurvePt 0 0.00000000000000000000 -2.73604876692571208352 4.18497755609325672310
+CurvePt 0 0.00000000000000000000 -3.53553390593273775266 3.53553390593273775266
+CurvePt 0 0.00000000000000000000 -4.18497755609325672310 2.73604876692571297170
+CurvePt 0 0.00000000000000000000 -4.64894150531215188948 1.84047354780936434082
+CurvePt 0 0.00000000000000000000 -4.91652684208287205081 0.90981526206072405039
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000005 1 2 00000002 00000004
+CCtrl 0 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 0 10.00000000000000000000 -0.90981526206072327323 4.91652684208287205081
+CurvePt 0 10.00000000000000000000 -1.84047354780936323060 4.64894150531215188948
+CurvePt 0 10.00000000000000000000 -2.73604876692571208352 4.18497755609325672310
+CurvePt 0 10.00000000000000000000 -3.53553390593273775266 3.53553390593273775266
+CurvePt 0 10.00000000000000000000 -4.18497755609325672310 2.73604876692571297170
+CurvePt 0 10.00000000000000000000 -4.64894150531215188948 1.84047354780936434082
+CurvePt 0 10.00000000000000000000 -4.91652684208287205081 0.90981526206072405039
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000006 1 1 00000004 00000005
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000007 1 2 00000001 00000005
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 0 0.00000000000000000000 -4.91652684208287205081 -0.90981526206072305119
+CurvePt 0 0.00000000000000000000 -4.64894150531215188948 -1.84047354780936300855
+CurvePt 0 0.00000000000000000000 -4.18497755609325672310 -2.73604876692571208352
+CurvePt 0 0.00000000000000000000 -3.53553390593273864084 -3.53553390593273730858
+CurvePt 0 0.00000000000000000000 -2.73604876692571297170 -4.18497755609325672310
+CurvePt 0 0.00000000000000000000 -1.84047354780936478491 -4.64894150531215188948
+CurvePt 0 0.00000000000000000000 -0.90981526206072438345 -4.91652684208287205081
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 00000008 1 2 00000002 00000005
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 0 10.00000000000000000000 -4.91652684208287205081 -0.90981526206072305119
+CurvePt 0 10.00000000000000000000 -4.64894150531215188948 -1.84047354780936300855
+CurvePt 0 10.00000000000000000000 -4.18497755609325672310 -2.73604876692571208352
+CurvePt 0 10.00000000000000000000 -3.53553390593273864084 -3.53553390593273730858
+CurvePt 0 10.00000000000000000000 -2.73604876692571297170 -4.18497755609325672310
+CurvePt 0 10.00000000000000000000 -1.84047354780936478491 -4.64894150531215188948
+CurvePt 0 10.00000000000000000000 -0.90981526206072438345 -4.91652684208287205081
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 00000009 1 1 00000005 00000006
+CCtrl 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 0000000a 1 2 00000001 00000006
+CCtrl 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 0 0.00000000000000000000 0.90981526206072282914 -4.91652684208287205081
+CurvePt 0 0.00000000000000000000 1.84047354780936300855 -4.64894150531215188948
+CurvePt 0 0.00000000000000000000 2.73604876692571163943 -4.18497755609325672310
+CurvePt 0 0.00000000000000000000 3.53553390593273730858 -3.53553390593273864084
+CurvePt 0 0.00000000000000000000 4.18497755609325583492 -2.73604876692571297170
+CurvePt 0 0.00000000000000000000 4.64894150531215188948 -1.84047354780936500696
+CurvePt 0 0.00000000000000000000 4.91652684208287205081 -0.90981526206072471652
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
+Curve 0000000b 1 2 00000002 00000006
+CCtrl 0 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 0 10.00000000000000000000 0.90981526206072282914 -4.91652684208287205081
+CurvePt 0 10.00000000000000000000 1.84047354780936300855 -4.64894150531215188948
+CurvePt 0 10.00000000000000000000 2.73604876692571163943 -4.18497755609325672310
+CurvePt 0 10.00000000000000000000 3.53553390593273730858 -3.53553390593273864084
+CurvePt 0 10.00000000000000000000 4.18497755609325583492 -2.73604876692571297170
+CurvePt 0 10.00000000000000000000 4.64894150531215188948 -1.84047354780936500696
+CurvePt 0 10.00000000000000000000 4.91652684208287205081 -0.90981526206072471652
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
+Curve 0000000c 1 1 00000006 00000003
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=00020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000003
+Group.type=5100
+Group.order=2
+Group.name=extrude
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000002
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.predef.entityB.v=00020000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00040000 1002
+ 2 00040001 1002
+ 3 00040020 1002
+ 4 00040040 1002
+ 5 00040000 1001
+ 6 00040001 1001
+ 7 00040020 1001
+ 8 00040040 1001
+ 9 00040001 1003
+ 10 80020000 1002
+ 11 80020000 1001
+ 12 80020001 1002
+ 13 80020002 1002
+ 14 80020001 1001
+ 15 80020002 1001
+ 16 80020002 1003
+ 17 00000000 1001
+ 18 00000000 1002
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030000
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030001
+AddParam
+
+Param.h.v.=80030002
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=00020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=00010000
+Request.group.v=00000003
+Request.construction=1
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=00020000
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00010000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030001
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.normal.v=80030003
+Entity.distance.v=80030004
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030004
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.normal.v=80030007
+Entity.distance.v=80030008
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030007
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.point[1].v=80030002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.point[1].v=8003000d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actPoint.x=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=00020000
+Constraint.ptA.v=00010001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=33
+Constraint.group.v=00000003
+Constraint.workplane.v=00010000
+Constraint.valA=-10.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=80030012
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+AddConstraint
+
+Surface 00000001 00646464 80030012 1 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000a 0 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -0.00000000000000088818
+TrimBy 00000001 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000004 0 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000044409
+TrimBy 00000007 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000044409 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000000000
+AddSurface
+Surface 00000002 00646464 80030011 1 1
+SCtrl 0 0 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000005 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000044409 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000002 1 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000b 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000088818 10.00000000000000000000 -0.00000000000000044409 -5.00000000000000000000
+TrimBy 00000008 1 10.00000000000000000000 -0.00000000000000044409 -5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000044409
+AddSurface
+Surface 00000003 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 2 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000002 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000003 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000001 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000c 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000004 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -4.99999999999999910000 5.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 1 1 10.00000000000000000000 -4.99999999999999910000 5.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 2 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+TrimBy 00000005 0 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000006 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000004 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000003 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddSurface
+Surface 00000005 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -5.00000000000000090000 -4.99999999999999910000 Weight 0.70710678118654757000
+SCtrl 1 1 10.00000000000000000000 -5.00000000000000090000 -4.99999999999999910000 Weight 0.70710678118654757000
+SCtrl 2 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000008 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000009 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000007 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000006 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddSurface
+Surface 00000006 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 4.99999999999999910000 -5.00000000000000090000 Weight 0.70710678118654757000
+SCtrl 1 1 10.00000000000000000000 4.99999999999999910000 -5.00000000000000090000 Weight 0.70710678118654757000
+SCtrl 2 0 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+TrimBy 0000000b 0 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+TrimBy 0000000c 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+TrimBy 0000000a 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000009 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddSurface
+Curve 00000001 1 2 00000001 00000003
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 0.00000000000000000000 4.91652684208287210000 0.90981526206072361000
+CurvePt 0 0.00000000000000000000 4.64894150531215190000 1.84047354780936390000
+CurvePt 0 0.00000000000000000000 4.18497755609325670000 2.73604876692571250000
+CurvePt 0 0.00000000000000000000 3.53553390593273780000 3.53553390593273780000
+CurvePt 0 0.00000000000000000000 2.73604876692571250000 4.18497755609325670000
+CurvePt 0 0.00000000000000000000 1.84047354780936410000 4.64894150531215190000
+CurvePt 0 0.00000000000000000000 0.90981526206072383000 4.91652684208287210000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000002 1 2 00000002 00000003
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 10.00000000000000000000 4.91652684208287210000 0.90981526206072361000
+CurvePt 0 10.00000000000000000000 4.64894150531215190000 1.84047354780936390000
+CurvePt 0 10.00000000000000000000 4.18497755609325670000 2.73604876692571250000
+CurvePt 0 10.00000000000000000000 3.53553390593273780000 3.53553390593273780000
+CurvePt 0 10.00000000000000000000 2.73604876692571250000 4.18497755609325670000
+CurvePt 0 10.00000000000000000000 1.84047354780936410000 4.64894150531215190000
+CurvePt 0 10.00000000000000000000 0.90981526206072383000 4.91652684208287210000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000003 1 1 00000003 00000004
+CCtrl 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000004 1 2 00000001 00000004
+CCtrl 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -4.99999999999999910000 5.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 0 0.00000000000000000000 -0.90981526206072327000 4.91652684208287210000
+CurvePt 0 0.00000000000000000000 -1.84047354780936320000 4.64894150531215190000
+CurvePt 0 0.00000000000000000000 -2.73604876692571210000 4.18497755609325670000
+CurvePt 0 0.00000000000000000000 -3.53553390593273780000 3.53553390593273780000
+CurvePt 0 0.00000000000000000000 -4.18497755609325670000 2.73604876692571300000
+CurvePt 0 0.00000000000000000000 -4.64894150531215190000 1.84047354780936430000
+CurvePt 0 0.00000000000000000000 -4.91652684208287210000 0.90981526206072405000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000005 1 2 00000002 00000004
+CCtrl 0 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -4.99999999999999910000 5.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 0 10.00000000000000000000 -0.90981526206072327000 4.91652684208287210000
+CurvePt 0 10.00000000000000000000 -1.84047354780936320000 4.64894150531215190000
+CurvePt 0 10.00000000000000000000 -2.73604876692571210000 4.18497755609325670000
+CurvePt 0 10.00000000000000000000 -3.53553390593273780000 3.53553390593273780000
+CurvePt 0 10.00000000000000000000 -4.18497755609325670000 2.73604876692571300000
+CurvePt 0 10.00000000000000000000 -4.64894150531215190000 1.84047354780936430000
+CurvePt 0 10.00000000000000000000 -4.91652684208287210000 0.90981526206072405000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000006 1 1 00000004 00000005
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000007 1 2 00000001 00000005
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000090000 -4.99999999999999910000 Weight 0.70710678118654757000
+CCtrl 2 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 0 0.00000000000000000000 -4.91652684208287210000 -0.90981526206072305000
+CurvePt 0 0.00000000000000000000 -4.64894150531215190000 -1.84047354780936300000
+CurvePt 0 0.00000000000000000000 -4.18497755609325670000 -2.73604876692571210000
+CurvePt 0 0.00000000000000000000 -3.53553390593273860000 -3.53553390593273730000
+CurvePt 0 0.00000000000000000000 -2.73604876692571300000 -4.18497755609325670000
+CurvePt 0 0.00000000000000000000 -1.84047354780936480000 -4.64894150531215190000
+CurvePt 0 0.00000000000000000000 -0.90981526206072438000 -4.91652684208287210000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 00000008 1 2 00000002 00000005
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000090000 -4.99999999999999910000 Weight 0.70710678118654757000
+CCtrl 2 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 0 10.00000000000000000000 -4.91652684208287210000 -0.90981526206072305000
+CurvePt 0 10.00000000000000000000 -4.64894150531215190000 -1.84047354780936300000
+CurvePt 0 10.00000000000000000000 -4.18497755609325670000 -2.73604876692571210000
+CurvePt 0 10.00000000000000000000 -3.53553390593273860000 -3.53553390593273730000
+CurvePt 0 10.00000000000000000000 -2.73604876692571300000 -4.18497755609325670000
+CurvePt 0 10.00000000000000000000 -1.84047354780936480000 -4.64894150531215190000
+CurvePt 0 10.00000000000000000000 -0.90981526206072438000 -4.91652684208287210000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 00000009 1 1 00000005 00000006
+CCtrl 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 0000000a 1 2 00000001 00000006
+CCtrl 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 4.99999999999999910000 -5.00000000000000090000 Weight 0.70710678118654757000
+CCtrl 2 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 0 0.00000000000000000000 0.90981526206072283000 -4.91652684208287210000
+CurvePt 0 0.00000000000000000000 1.84047354780936300000 -4.64894150531215190000
+CurvePt 0 0.00000000000000000000 2.73604876692571160000 -4.18497755609325670000
+CurvePt 0 0.00000000000000000000 3.53553390593273730000 -3.53553390593273860000
+CurvePt 0 0.00000000000000000000 4.18497755609325580000 -2.73604876692571300000
+CurvePt 0 0.00000000000000000000 4.64894150531215190000 -1.84047354780936500000
+CurvePt 0 0.00000000000000000000 4.91652684208287210000 -0.90981526206072472000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
+Curve 0000000b 1 2 00000002 00000006
+CCtrl 0 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 4.99999999999999910000 -5.00000000000000090000 Weight 0.70710678118654757000
+CCtrl 2 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 0 10.00000000000000000000 0.90981526206072283000 -4.91652684208287210000
+CurvePt 0 10.00000000000000000000 1.84047354780936300000 -4.64894150531215190000
+CurvePt 0 10.00000000000000000000 2.73604876692571160000 -4.18497755609325670000
+CurvePt 0 10.00000000000000000000 3.53553390593273730000 -3.53553390593273860000
+CurvePt 0 10.00000000000000000000 4.18497755609325580000 -2.73604876692571300000
+CurvePt 0 10.00000000000000000000 4.64894150531215190000 -1.84047354780936500000
+CurvePt 0 10.00000000000000000000 4.91652684208287210000 -0.90981526206072472000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
+Curve 0000000c 1 1 00000006 00000003
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=00020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000003
+Group.type=5100
+Group.order=2
+Group.name=extrude
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000002
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.predef.entityB.v=00020000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00040000 1002
+ 2 00040001 1002
+ 3 00040020 1002
+ 4 00040040 1002
+ 5 00040000 1001
+ 6 00040001 1001
+ 7 00040020 1001
+ 8 00040040 1001
+ 9 00040001 1003
+ 10 80020000 1002
+ 11 80020000 1001
+ 12 80020001 1002
+ 13 80020002 1002
+ 14 80020001 1001
+ 15 80020002 1001
+ 16 80020002 1003
+ 17 00000000 1001
+ 18 00000000 1002
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030000
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030001
+AddParam
+
+Param.h.v.=80030002
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=00020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=00010000
+Request.group.v=00000003
+Request.construction=1
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=00020000
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=00020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=00010000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030001
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.normal.v=80030003
+Entity.distance.v=80030004
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030004
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.normal.v=80030007
+Entity.distance.v=80030008
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030007
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.point[1].v=80030002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.point[1].v=8003000d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actPoint.x=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=00020000
+Constraint.ptA.v=00010001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=33
+Constraint.group.v=00000003
+Constraint.workplane.v=00010000
+Constraint.valA=-10.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=80030012
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+AddConstraint
+
+Surface 00000001 00646464 80030012 1 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000a 0 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -0.00000000000000088818
+TrimBy 00000001 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000004 0 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000044409
+TrimBy 00000007 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000044409 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000000000
+AddSurface
+Surface 00000002 00646464 80030011 1 1
+SCtrl 0 0 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000005 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000044409 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000002 1 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000b 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000088818 10.00000000000000000000 -0.00000000000000044409 -5.00000000000000000000
+TrimBy 00000008 1 10.00000000000000000000 -0.00000000000000044409 -5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000044409
+AddSurface
+Surface 00000003 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000002 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000001 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000c 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000003 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddSurface
+Surface 00000004 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+TrimBy 00000005 0 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000004 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000003 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+TrimBy 00000006 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddSurface
+Surface 00000005 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000008 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000007 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000006 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+TrimBy 00000009 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddSurface
+Surface 00000006 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+SCtrl 1 1 10.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+SCtrl 2 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+TrimBy 0000000b 0 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+TrimBy 0000000a 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 00000009 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+TrimBy 0000000c 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddSurface
+Curve 00000001 1 2 00000001 00000003
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 0.00000000000000000000 4.64894150531215188948 1.84047354780936389673
+CurvePt 0 0.00000000000000000000 3.53553390593273775266 3.53553390593273775266
+CurvePt 0 0.00000000000000000000 1.84047354780936411878 4.64894150531215188948
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000002 1 2 00000002 00000003
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 10.00000000000000000000 4.64894150531215188948 1.84047354780936389673
+CurvePt 0 10.00000000000000000000 3.53553390593273775266 3.53553390593273775266
+CurvePt 0 10.00000000000000000000 1.84047354780936411878 4.64894150531215188948
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000003 1 1 00000003 00000004
+CCtrl 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+AddCurve
+Curve 00000004 1 2 00000001 00000004
+CCtrl 0 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 0 0.00000000000000000000 -1.84047354780936323060 4.64894150531215188948
+CurvePt 0 0.00000000000000000000 -3.53553390593273775266 3.53553390593273775266
+CurvePt 0 0.00000000000000000000 -4.64894150531215188948 1.84047354780936434082
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000005 1 2 00000002 00000004
+CCtrl 0 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -4.99999999999999911182 5.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000030616 5.00000000000000000000
+CurvePt 0 10.00000000000000000000 -1.84047354780936323060 4.64894150531215188948
+CurvePt 0 10.00000000000000000000 -3.53553390593273775266 3.53553390593273775266
+CurvePt 0 10.00000000000000000000 -4.64894150531215188948 1.84047354780936434082
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000006 1 1 00000004 00000005
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+AddCurve
+Curve 00000007 1 2 00000001 00000005
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 0 0.00000000000000000000 -4.64894150531215188948 -1.84047354780936300855
+CurvePt 0 0.00000000000000000000 -3.53553390593273864084 -3.53553390593273730858
+CurvePt 0 0.00000000000000000000 -1.84047354780936478491 -4.64894150531215188948
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 00000008 1 2 00000002 00000005
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000088818 -4.99999999999999911182 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232
+CurvePt 0 10.00000000000000000000 -4.64894150531215188948 -1.84047354780936300855
+CurvePt 0 10.00000000000000000000 -3.53553390593273864084 -3.53553390593273730858
+CurvePt 0 10.00000000000000000000 -1.84047354780936478491 -4.64894150531215188948
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 00000009 1 1 00000005 00000006
+CCtrl 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+AddCurve
+Curve 0000000a 1 2 00000001 00000006
+CCtrl 0 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 0 0.00000000000000000000 1.84047354780936300855 -4.64894150531215188948
+CurvePt 0 0.00000000000000000000 3.53553390593273730858 -3.53553390593273864084
+CurvePt 0 0.00000000000000000000 4.64894150531215188948 -1.84047354780936500696
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
+Curve 0000000b 1 2 00000002 00000006
+CCtrl 0 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 4.99999999999999911182 -5.00000000000000088818 Weight 0.70710678118654757274
+CCtrl 2 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000
+CurvePt 0 10.00000000000000000000 1.84047354780936300855 -4.64894150531215188948
+CurvePt 0 10.00000000000000000000 3.53553390593273730858 -3.53553390593273864084
+CurvePt 0 10.00000000000000000000 4.64894150531215188948 -1.84047354780936500696
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
+Curve 0000000c 1 1 00000006 00000003
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 -0.00000000000000122465
+AddCurve
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(reference_roundtrip) {
+ CHECK_LOAD("reference.slvs");
+ CHECK_RENDER("reference.png");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v20) {
+ CHECK_LOAD("reference_v20.slvs");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v22) {
+ CHECK_LOAD("reference_v22.slvs");
+ CHECK_SAVE("reference.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=41
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.entityA.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=41
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.entityA.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=41
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.entityA.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=32
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=-5.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=-10.00000000000000000000
+Constraint.disp.offset.y=0.0000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=32
+Constraint.group.v=00000002
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=32
+Constraint.group.v=00000002
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=32
+Constraint.group.v=00000002
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=32
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=-5.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=32
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=-5.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=32
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=-5.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=32
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=-5.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=32
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=-5.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=32
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=-5.00000000000000000000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(free_in_3d_roundtrip) {
+ CHECK_LOAD("free_in_3d.slvs");
+ CHECK_RENDER("free_in_3d.png");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v20) {
+ CHECK_LOAD("free_in_3d_v20.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v22) {
+ CHECK_LOAD("free_in_3d_v22.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(reference_roundtrip) {
+ CHECK_LOAD("reference.slvs");
+ CHECK_RENDER("reference.png");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v20) {
+ CHECK_LOAD("reference_v20.slvs");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v22) {
+ CHECK_LOAD("reference_v22.slvs");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(extended_render) {
+ CHECK_LOAD("extended.slvs");
+ CHECK_RENDER("extended.png");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=9.99958442064562014195
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=-4.99958442064562014195
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=14.99916884129124028391
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=9.99958442064562014195
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=-4.99958442064562014195
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=14.99916884129124028391
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=90
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=-9.99916884129124028391
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.x=5.09750582376745420277
+Constraint.disp.offset.y=5.06841431955496624084
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=100
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=100
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00060000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-9.99999912024158099655
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=4.99999824048316199310
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000087975841900345
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-9.99999912024158099655
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=4.99999824048316199310
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000087975841900345
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=100
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-9.99999912024158100000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=4.99999824048316200000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000087975841900000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-9.99999912024158100000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=4.99999824048316200000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000087975841900000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=100
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-9.99999912024158100000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=4.99999824048316200000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000087975841900000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-9.99999912024158100000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=4.99999824048316200000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000087975841900000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=100
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(negative_dia) {
+ CHECK_LOAD("negative_dia.slvs");
+ Entity *e = SK.GetEntity(hRequest { 4 }.entity(0));
+ CHECK_TRUE(e->CircleGetRadiusNum() > 0);
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000003
+Group.type=5100
+Group.order=2
+Group.name=extrude
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000002
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.predef.entityB.v=80020000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00040000 1002
+ 2 00040001 1002
+ 3 00040020 1002
+ 4 00040040 1002
+ 5 00040000 1001
+ 6 00040001 1001
+ 7 00040020 1001
+ 8 00040040 1001
+ 9 00040001 1003
+ 10 80020000 1002
+ 11 80020000 1001
+ 12 80020001 1002
+ 13 80020002 1002
+ 14 80020001 1001
+ 15 80020002 1001
+ 16 80020002 1003
+ 17 00000000 1001
+ 18 00000000 1002
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=80030000
+AddParam
+
+Param.h.v.=80030001
+AddParam
+
+Param.h.v.=80030002
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000003
+Request.construction=1
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=1
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030001
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.normal.v=80030003
+Entity.distance.v=80030004
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030004
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.normal.v=80030007
+Entity.distance.v=80030008
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030007
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.point[1].v=80030002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=2010
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.z=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8003000f
+Entity.point[1].v=8003000d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actPoint.z=-10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00010001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=43
+Constraint.group.v=00000003
+Constraint.ptA.v=00050000
+Constraint.entityA.v=80030012
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Surface 00000001 00646464 80030011 1 1
+SCtrl 0 0 -5.00000000000000000000 -5.00000000000000088818 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000010000000827 -5.00000000000000088818 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 4.99999999999999911182 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000010000000827 4.99999999999999911182 -10.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000a 0 -0.00000000000000177636 -5.00000000000000088818 -10.00000000000000000000 5.00000000000000000000 -0.00000000000000088818 -10.00000000000000000000
+TrimBy 00000001 0 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 4.99999999999999911182 -10.00000000000000000000
+TrimBy 00000004 0 0.00000000000000000000 4.99999999999999911182 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000
+TrimBy 00000007 0 -5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -0.00000000000000177636 -5.00000000000000088818 -10.00000000000000000000
+AddSurface
+Surface 00000002 00646464 80030012 1 1
+SCtrl 0 0 5.00000000010000000827 -5.00000000000000088818 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 -5.00000000000000088818 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000010000000827 4.99999999999999911182 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 4.99999999999999911182 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000005 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 4.99999999999999911182 0.00000000000000000000
+TrimBy 00000002 1 0.00000000000000000000 4.99999999999999911182 0.00000000000000000000 5.00000000000000088818 -0.00000000000000000000 0.00000000000000000000
+TrimBy 0000000b 1 5.00000000000000088818 -0.00000000000000088818 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000088818 0.00000000000000000000
+TrimBy 00000008 1 -0.00000000000000088818 -5.00000000000000088818 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000003 00646464 00000000 2 1
+SCtrl 0 0 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000002 0 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000001 1 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000
+TrimBy 0000000c 0 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000003 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+AddSurface
+Surface 00000004 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -4.99999999999999911182 5.00000000000000000000 -10.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 -4.99999999999999911182 5.00000000000000000000 0.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000005 0 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+TrimBy 00000004 1 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+TrimBy 00000003 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000006 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+AddSurface
+Surface 00000005 00646464 00000000 2 1
+SCtrl 0 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000088818 -4.99999999999999911182 -10.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 -5.00000000000000088818 -4.99999999999999911182 0.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000008 0 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000007 1 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+TrimBy 00000006 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+TrimBy 00000009 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+AddSurface
+Surface 00000006 00646464 00000000 2 1
+SCtrl 0 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 4.99999999999999911182 -5.00000000000000088818 -10.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 4.99999999999999911182 -5.00000000000000088818 0.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000b 0 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000
+TrimBy 0000000a 1 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+TrimBy 00000009 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000c 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000
+AddSurface
+Curve 00000001 1 2 00000001 00000003
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000
+CurvePt 0 4.91652684208287205081 0.90981526206072360630 -10.00000000000000000000
+CurvePt 0 4.64894150531215188948 1.84047354780936389673 -10.00000000000000000000
+CurvePt 0 4.18497755609325672310 2.73604876692571252761 -10.00000000000000000000
+CurvePt 0 3.53553390593273775266 3.53553390593273775266 -10.00000000000000000000
+CurvePt 0 2.73604876692571252761 4.18497755609325672310 -10.00000000000000000000
+CurvePt 0 1.84047354780936411878 4.64894150531215188948 -10.00000000000000000000
+CurvePt 0 0.90981526206072382834 4.91652684208287205081 -10.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+AddCurve
+Curve 00000002 1 2 00000002 00000003
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 0 4.91652684208287205081 0.90981526206072360630 0.00000000000000000000
+CurvePt 0 4.64894150531215188948 1.84047354780936389673 0.00000000000000000000
+CurvePt 0 4.18497755609325672310 2.73604876692571252761 0.00000000000000000000
+CurvePt 0 3.53553390593273775266 3.53553390593273775266 0.00000000000000000000
+CurvePt 0 2.73604876692571252761 4.18497755609325672310 0.00000000000000000000
+CurvePt 0 1.84047354780936411878 4.64894150531215188948 0.00000000000000000000
+CurvePt 0 0.90981526206072382834 4.91652684208287205081 0.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000003 1 1 00000003 00000004
+CCtrl 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000004 1 2 00000001 00000004
+CCtrl 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -4.99999999999999911182 5.00000000000000000000 -10.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+CurvePt 0 -0.90981526206072327323 4.91652684208287205081 -10.00000000000000000000
+CurvePt 0 -1.84047354780936323060 4.64894150531215188948 -10.00000000000000000000
+CurvePt 0 -2.73604876692571208352 4.18497755609325672310 -10.00000000000000000000
+CurvePt 0 -3.53553390593273775266 3.53553390593273775266 -10.00000000000000000000
+CurvePt 0 -4.18497755609325672310 2.73604876692571297170 -10.00000000000000000000
+CurvePt 0 -4.64894150531215188948 1.84047354780936434082 -10.00000000000000000000
+CurvePt 0 -4.91652684208287205081 0.90981526206072405039 -10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+AddCurve
+Curve 00000005 1 2 00000002 00000004
+CCtrl 0 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -4.99999999999999911182 5.00000000000000000000 0.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 -0.90981526206072327323 4.91652684208287205081 0.00000000000000000000
+CurvePt 0 -1.84047354780936323060 4.64894150531215188948 0.00000000000000000000
+CurvePt 0 -2.73604876692571208352 4.18497755609325672310 0.00000000000000000000
+CurvePt 0 -3.53553390593273775266 3.53553390593273775266 0.00000000000000000000
+CurvePt 0 -4.18497755609325672310 2.73604876692571297170 0.00000000000000000000
+CurvePt 0 -4.64894150531215188948 1.84047354780936434082 0.00000000000000000000
+CurvePt 0 -4.91652684208287205081 0.90981526206072405039 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+AddCurve
+Curve 00000006 1 1 00000004 00000005
+CCtrl 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+AddCurve
+Curve 00000007 1 2 00000001 00000005
+CCtrl 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000088818 -4.99999999999999911182 -10.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+CurvePt 0 -4.91652684208287205081 -0.90981526206072305119 -10.00000000000000000000
+CurvePt 0 -4.64894150531215188948 -1.84047354780936300855 -10.00000000000000000000
+CurvePt 0 -4.18497755609325672310 -2.73604876692571208352 -10.00000000000000000000
+CurvePt 0 -3.53553390593273864084 -3.53553390593273730858 -10.00000000000000000000
+CurvePt 0 -2.73604876692571297170 -4.18497755609325672310 -10.00000000000000000000
+CurvePt 0 -1.84047354780936478491 -4.64894150531215188948 -10.00000000000000000000
+CurvePt 0 -0.90981526206072438345 -4.91652684208287205081 -10.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+AddCurve
+Curve 00000008 1 2 00000002 00000005
+CCtrl 0 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000088818 -4.99999999999999911182 0.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+CurvePt 0 -4.91652684208287205081 -0.90981526206072305119 0.00000000000000000000
+CurvePt 0 -4.64894150531215188948 -1.84047354780936300855 0.00000000000000000000
+CurvePt 0 -4.18497755609325672310 -2.73604876692571208352 0.00000000000000000000
+CurvePt 0 -3.53553390593273864084 -3.53553390593273730858 0.00000000000000000000
+CurvePt 0 -2.73604876692571297170 -4.18497755609325672310 0.00000000000000000000
+CurvePt 0 -1.84047354780936478491 -4.64894150531215188948 0.00000000000000000000
+CurvePt 0 -0.90981526206072438345 -4.91652684208287205081 0.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000009 1 1 00000005 00000006
+CCtrl 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000a 1 2 00000001 00000006
+CCtrl 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 4.99999999999999911182 -5.00000000000000088818 -10.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+CurvePt 0 0.90981526206072282914 -4.91652684208287205081 -10.00000000000000000000
+CurvePt 0 1.84047354780936300855 -4.64894150531215188948 -10.00000000000000000000
+CurvePt 0 2.73604876692571163943 -4.18497755609325672310 -10.00000000000000000000
+CurvePt 0 3.53553390593273730858 -3.53553390593273864084 -10.00000000000000000000
+CurvePt 0 4.18497755609325583492 -2.73604876692571297170 -10.00000000000000000000
+CurvePt 0 4.64894150531215188948 -1.84047354780936500696 -10.00000000000000000000
+CurvePt 0 4.91652684208287205081 -0.90981526206072471652 -10.00000000000000000000
+CurvePt 1 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000
+AddCurve
+Curve 0000000b 1 2 00000002 00000006
+CCtrl 0 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 4.99999999999999911182 -5.00000000000000088818 0.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+CurvePt 0 0.90981526206072282914 -4.91652684208287205081 0.00000000000000000000
+CurvePt 0 1.84047354780936300855 -4.64894150531215188948 0.00000000000000000000
+CurvePt 0 2.73604876692571163943 -4.18497755609325672310 0.00000000000000000000
+CurvePt 0 3.53553390593273730858 -3.53553390593273864084 0.00000000000000000000
+CurvePt 0 4.18497755609325583492 -2.73604876692571297170 0.00000000000000000000
+CurvePt 0 4.64894150531215188948 -1.84047354780936500696 0.00000000000000000000
+CurvePt 0 4.91652684208287205081 -0.90981526206072471652 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000
+AddCurve
+Curve 0000000c 1 1 00000006 00000003
+CCtrl 0 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000
+CurvePt 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000
+AddCurve
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000003
+Group.type=5100
+Group.order=2
+Group.name=extrude
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000002
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.predef.entityB.v=80020000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00040000 1002
+ 2 00040001 1002
+ 3 00040020 1002
+ 4 00040040 1002
+ 5 00040000 1001
+ 6 00040001 1001
+ 7 00040020 1001
+ 8 00040040 1001
+ 9 00040001 1003
+ 10 80020000 1002
+ 11 80020000 1001
+ 12 80020001 1002
+ 13 80020002 1002
+ 14 80020001 1001
+ 15 80020002 1001
+ 16 80020002 1003
+ 17 00000000 1001
+ 18 00000000 1002
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=80030000
+AddParam
+
+Param.h.v.=80030001
+AddParam
+
+Param.h.v.=80030002
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000003
+Request.construction=1
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030001
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.normal.v=80030003
+Entity.distance.v=80030004
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030004
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.normal.v=80030007
+Entity.distance.v=80030008
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030007
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.point[1].v=80030002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.point[1].v=8003000d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actPoint.z=-10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00010001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=43
+Constraint.group.v=00000003
+Constraint.ptA.v=00050000
+Constraint.entityA.v=80030012
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Surface 00000001 00646464 80030011 1 1
+SCtrl 0 0 -5.00000000000000000000 -5.00000000000000090000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000010000000000 -5.00000000000000090000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 4.99999999999999910000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000010000000000 4.99999999999999910000 -10.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000a 0 -0.00000000000000177636 -5.00000000000000090000 -10.00000000000000000000 5.00000000000000000000 -0.00000000000000088818 -10.00000000000000000000
+TrimBy 00000001 0 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 4.99999999999999910000 -10.00000000000000000000
+TrimBy 00000004 0 0.00000000000000000000 4.99999999999999910000 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000
+TrimBy 00000007 0 -5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -0.00000000000000177636 -5.00000000000000090000 -10.00000000000000000000
+AddSurface
+Surface 00000002 00646464 80030012 1 1
+SCtrl 0 0 5.00000000010000000000 -5.00000000000000090000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 -5.00000000000000090000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000010000000000 4.99999999999999910000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 4.99999999999999910000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000005 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 4.99999999999999910000 0.00000000000000000000
+TrimBy 00000002 1 0.00000000000000000000 4.99999999999999910000 0.00000000000000000000 5.00000000000000090000 -0.00000000000000000000 0.00000000000000000000
+TrimBy 0000000b 1 5.00000000000000090000 -0.00000000000000088818 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000090000 0.00000000000000000000
+TrimBy 00000008 1 -0.00000000000000088818 -5.00000000000000090000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000003 00646464 00000000 2 1
+SCtrl 0 0 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 1 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 2 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000002 0 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000003 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+TrimBy 00000001 1 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000
+TrimBy 0000000c 0 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000004 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -4.99999999999999910000 5.00000000000000000000 -10.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 1 1 -4.99999999999999910000 5.00000000000000000000 0.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 2 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000005 0 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+TrimBy 00000006 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+TrimBy 00000004 1 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+TrimBy 00000003 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000005 00646464 00000000 2 1
+SCtrl 0 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000090000 -4.99999999999999910000 -10.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 1 1 -5.00000000000000090000 -4.99999999999999910000 0.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 2 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000008 0 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000009 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+TrimBy 00000007 1 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+TrimBy 00000006 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+AddSurface
+Surface 00000006 00646464 00000000 2 1
+SCtrl 0 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 4.99999999999999910000 -5.00000000000000090000 -10.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 1 1 4.99999999999999910000 -5.00000000000000090000 0.00000000000000000000 Weight 0.70710678118654757000
+SCtrl 2 0 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000b 0 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000
+TrimBy 0000000c 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000
+TrimBy 0000000a 1 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+TrimBy 00000009 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+AddSurface
+Curve 00000001 1 2 00000001 00000003
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000
+CurvePt 0 4.64894150531215190000 1.84047354780936390000 -10.00000000000000000000
+CurvePt 0 3.53553390593273780000 3.53553390593273780000 -10.00000000000000000000
+CurvePt 0 1.84047354780936410000 4.64894150531215190000 -10.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+AddCurve
+Curve 00000002 1 2 00000002 00000003
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 0 4.64894150531215190000 1.84047354780936390000 0.00000000000000000000
+CurvePt 0 3.53553390593273780000 3.53553390593273780000 0.00000000000000000000
+CurvePt 0 1.84047354780936410000 4.64894150531215190000 0.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000003 1 1 00000003 00000004
+CCtrl 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000004 1 2 00000001 00000004
+CCtrl 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -4.99999999999999910000 5.00000000000000000000 -10.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+CurvePt 0 -1.84047354780936320000 4.64894150531215190000 -10.00000000000000000000
+CurvePt 0 -3.53553390593273780000 3.53553390593273780000 -10.00000000000000000000
+CurvePt 0 -4.64894150531215190000 1.84047354780936430000 -10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+AddCurve
+Curve 00000005 1 2 00000002 00000004
+CCtrl 0 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -4.99999999999999910000 5.00000000000000000000 0.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 -1.84047354780936320000 4.64894150531215190000 0.00000000000000000000
+CurvePt 0 -3.53553390593273780000 3.53553390593273780000 0.00000000000000000000
+CurvePt 0 -4.64894150531215190000 1.84047354780936430000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+AddCurve
+Curve 00000006 1 1 00000004 00000005
+CCtrl 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+AddCurve
+Curve 00000007 1 2 00000001 00000005
+CCtrl 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000090000 -4.99999999999999910000 -10.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+CurvePt 0 -4.64894150531215190000 -1.84047354780936300000 -10.00000000000000000000
+CurvePt 0 -3.53553390593273860000 -3.53553390593273730000 -10.00000000000000000000
+CurvePt 0 -1.84047354780936480000 -4.64894150531215190000 -10.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+AddCurve
+Curve 00000008 1 2 00000002 00000005
+CCtrl 0 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000090000 -4.99999999999999910000 0.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+CurvePt 0 -4.64894150531215190000 -1.84047354780936300000 0.00000000000000000000
+CurvePt 0 -3.53553390593273860000 -3.53553390593273730000 0.00000000000000000000
+CurvePt 0 -1.84047354780936480000 -4.64894150531215190000 0.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000009 1 1 00000005 00000006
+CCtrl 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000a 1 2 00000001 00000006
+CCtrl 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 4.99999999999999910000 -5.00000000000000090000 -10.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+CurvePt 0 1.84047354780936300000 -4.64894150531215190000 -10.00000000000000000000
+CurvePt 0 3.53553390593273730000 -3.53553390593273860000 -10.00000000000000000000
+CurvePt 0 4.64894150531215190000 -1.84047354780936500000 -10.00000000000000000000
+CurvePt 1 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000
+AddCurve
+Curve 0000000b 1 2 00000002 00000006
+CCtrl 0 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 4.99999999999999910000 -5.00000000000000090000 0.00000000000000000000 Weight 0.70710678118654757000
+CCtrl 2 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+CurvePt 0 1.84047354780936300000 -4.64894150531215190000 0.00000000000000000000
+CurvePt 0 3.53553390593273730000 -3.53553390593273860000 0.00000000000000000000
+CurvePt 0 4.64894150531215190000 -1.84047354780936500000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000
+AddCurve
+Curve 0000000c 1 1 00000006 00000003
+CCtrl 0 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000
+CurvePt 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000
+AddCurve
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000003
+Group.type=5100
+Group.order=2
+Group.name=extrude
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000002
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.predef.entityB.v=80020000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00040000 1002
+ 2 00040001 1002
+ 3 00040020 1002
+ 4 00040040 1002
+ 5 00040000 1001
+ 6 00040001 1001
+ 7 00040020 1001
+ 8 00040040 1001
+ 9 00040001 1003
+ 10 80020000 1002
+ 11 80020000 1001
+ 12 80020001 1002
+ 13 80020002 1002
+ 14 80020001 1001
+ 15 80020002 1001
+ 16 80020002 1003
+ 17 00000000 1001
+ 18 00000000 1002
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=80030000
+AddParam
+
+Param.h.v.=80030001
+AddParam
+
+Param.h.v.=80030002
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000003
+Request.construction=1
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030001
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.normal.v=80030003
+Entity.distance.v=80030004
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030004
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.normal.v=80030007
+Entity.distance.v=80030008
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030007
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=4001
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.point[1].v=80030002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.point[1].v=8003000d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.actPoint.z=-10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003000d
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00010001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=43
+Constraint.group.v=00000003
+Constraint.ptA.v=00050000
+Constraint.entityA.v=80030012
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Surface 00000001 00646464 80030011 1 1
+SCtrl 0 0 -5.00000000000000000000 -5.00000000000000088818 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000010000000827 -5.00000000000000088818 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 4.99999999999999911182 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000010000000827 4.99999999999999911182 -10.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000a 0 -0.00000000000000177636 -5.00000000000000088818 -10.00000000000000000000 5.00000000000000000000 -0.00000000000000088818 -10.00000000000000000000
+TrimBy 00000001 0 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 4.99999999999999911182 -10.00000000000000000000
+TrimBy 00000004 0 0.00000000000000000000 4.99999999999999911182 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000
+TrimBy 00000007 0 -5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -0.00000000000000177636 -5.00000000000000088818 -10.00000000000000000000
+AddSurface
+Surface 00000002 00646464 80030012 1 1
+SCtrl 0 0 5.00000000010000000827 -5.00000000000000088818 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 -5.00000000000000088818 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000010000000827 4.99999999999999911182 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 4.99999999999999911182 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000005 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 4.99999999999999911182 0.00000000000000000000
+TrimBy 00000002 1 0.00000000000000000000 4.99999999999999911182 0.00000000000000000000 5.00000000000000088818 -0.00000000000000000000 0.00000000000000000000
+TrimBy 0000000b 1 5.00000000000000088818 -0.00000000000000088818 0.00000000000000000000 -0.00000000000000088818 -5.00000000000000088818 0.00000000000000000000
+TrimBy 00000008 1 -0.00000000000000088818 -5.00000000000000088818 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000003 00646464 00000000 2 1
+SCtrl 0 0 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000002 0 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000001 1 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000
+TrimBy 0000000c 0 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000003 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+AddSurface
+Surface 00000004 00646464 00000000 2 1
+SCtrl 0 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -4.99999999999999911182 5.00000000000000000000 -10.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 -4.99999999999999911182 5.00000000000000000000 0.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000005 0 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+TrimBy 00000004 1 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+TrimBy 00000003 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000006 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+AddSurface
+Surface 00000005 00646464 00000000 2 1
+SCtrl 0 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000088818 -4.99999999999999911182 -10.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 -5.00000000000000088818 -4.99999999999999911182 0.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000008 0 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000007 1 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+TrimBy 00000006 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+TrimBy 00000009 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+AddSurface
+Surface 00000006 00646464 00000000 2 1
+SCtrl 0 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 4.99999999999999911182 -5.00000000000000088818 -10.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 1 1 4.99999999999999911182 -5.00000000000000088818 0.00000000000000000000 Weight 0.70710678118654757274
+SCtrl 2 0 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 2 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000b 0 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000
+TrimBy 0000000a 1 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+TrimBy 00000009 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000c 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000
+AddSurface
+Curve 00000001 1 2 00000001 00000003
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000
+CurvePt 0 4.64894150531215188948 1.84047354780936389673 -10.00000000000000000000
+CurvePt 0 4.18497755609325672310 2.73604876692571252761 -10.00000000000000000000
+CurvePt 0 3.53553390593273775266 3.53553390593273775266 -10.00000000000000000000
+CurvePt 0 2.73604876692571252761 4.18497755609325672310 -10.00000000000000000000
+CurvePt 0 1.84047354780936411878 4.64894150531215188948 -10.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+AddCurve
+Curve 00000002 1 2 00000002 00000003
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 0 4.64894150531215188948 1.84047354780936389673 0.00000000000000000000
+CurvePt 0 4.18497755609325672310 2.73604876692571252761 0.00000000000000000000
+CurvePt 0 3.53553390593273775266 3.53553390593273775266 0.00000000000000000000
+CurvePt 0 2.73604876692571252761 4.18497755609325672310 0.00000000000000000000
+CurvePt 0 1.84047354780936411878 4.64894150531215188948 0.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000003 1 1 00000003 00000004
+CCtrl 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000004 1 2 00000001 00000004
+CCtrl 0 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -4.99999999999999911182 5.00000000000000000000 -10.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 -10.00000000000000000000
+CurvePt 0 -1.84047354780936323060 4.64894150531215188948 -10.00000000000000000000
+CurvePt 0 -2.73604876692571208352 4.18497755609325672310 -10.00000000000000000000
+CurvePt 0 -3.53553390593273775266 3.53553390593273775266 -10.00000000000000000000
+CurvePt 0 -4.18497755609325672310 2.73604876692571297170 -10.00000000000000000000
+CurvePt 0 -4.64894150531215188948 1.84047354780936434082 -10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+AddCurve
+Curve 00000005 1 2 00000002 00000004
+CCtrl 0 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -4.99999999999999911182 5.00000000000000000000 0.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000030616 5.00000000000000000000 0.00000000000000000000
+CurvePt 0 -1.84047354780936323060 4.64894150531215188948 0.00000000000000000000
+CurvePt 0 -2.73604876692571208352 4.18497755609325672310 0.00000000000000000000
+CurvePt 0 -3.53553390593273775266 3.53553390593273775266 0.00000000000000000000
+CurvePt 0 -4.18497755609325672310 2.73604876692571297170 0.00000000000000000000
+CurvePt 0 -4.64894150531215188948 1.84047354780936434082 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+AddCurve
+Curve 00000006 1 1 00000004 00000005
+CCtrl 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+AddCurve
+Curve 00000007 1 2 00000001 00000005
+CCtrl 0 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000088818 -4.99999999999999911182 -10.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 -10.00000000000000000000
+CurvePt 0 -4.64894150531215188948 -1.84047354780936300855 -10.00000000000000000000
+CurvePt 0 -4.18497755609325672310 -2.73604876692571208352 -10.00000000000000000000
+CurvePt 0 -3.53553390593273864084 -3.53553390593273730858 -10.00000000000000000000
+CurvePt 0 -2.73604876692571297170 -4.18497755609325672310 -10.00000000000000000000
+CurvePt 0 -1.84047354780936478491 -4.64894150531215188948 -10.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+AddCurve
+Curve 00000008 1 2 00000002 00000005
+CCtrl 0 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000088818 -4.99999999999999911182 0.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000061232 0.00000000000000000000
+CurvePt 0 -4.64894150531215188948 -1.84047354780936300855 0.00000000000000000000
+CurvePt 0 -4.18497755609325672310 -2.73604876692571208352 0.00000000000000000000
+CurvePt 0 -3.53553390593273864084 -3.53553390593273730858 0.00000000000000000000
+CurvePt 0 -2.73604876692571297170 -4.18497755609325672310 0.00000000000000000000
+CurvePt 0 -1.84047354780936478491 -4.64894150531215188948 0.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000009 1 1 00000005 00000006
+CCtrl 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000a 1 2 00000001 00000006
+CCtrl 0 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 4.99999999999999911182 -5.00000000000000088818 -10.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 -10.00000000000000000000
+CurvePt 0 1.84047354780936300855 -4.64894150531215188948 -10.00000000000000000000
+CurvePt 0 2.73604876692571163943 -4.18497755609325672310 -10.00000000000000000000
+CurvePt 0 3.53553390593273730858 -3.53553390593273864084 -10.00000000000000000000
+CurvePt 0 4.18497755609325583492 -2.73604876692571297170 -10.00000000000000000000
+CurvePt 0 4.64894150531215188948 -1.84047354780936500696 -10.00000000000000000000
+CurvePt 1 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000
+AddCurve
+Curve 0000000b 1 2 00000002 00000006
+CCtrl 0 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 4.99999999999999911182 -5.00000000000000088818 0.00000000000000000000 Weight 0.70710678118654757274
+CCtrl 2 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -0.00000000000000091849 -5.00000000000000000000 0.00000000000000000000
+CurvePt 0 1.84047354780936300855 -4.64894150531215188948 0.00000000000000000000
+CurvePt 0 2.73604876692571163943 -4.18497755609325672310 0.00000000000000000000
+CurvePt 0 3.53553390593273730858 -3.53553390593273864084 0.00000000000000000000
+CurvePt 0 4.18497755609325583492 -2.73604876692571297170 0.00000000000000000000
+CurvePt 0 4.64894150531215188948 -1.84047354780936500696 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000
+AddCurve
+Curve 0000000c 1 1 00000006 00000003
+CCtrl 0 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -0.00000000000000122465 -10.00000000000000000000
+CurvePt 1 5.00000000000000000000 -0.00000000000000122465 0.00000000000000000000
+AddCurve
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-20.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-20.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=42
+Constraint.group.v=00000002
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-20.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-20.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=42
+Constraint.group.v=00000002
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-20.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=40000001
+Param.val=0.33333333333333331483
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-20.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=42
+Constraint.group.v=00000002
+Constraint.valP.v=40000001
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=40000001
+Param.val=0.33333333333333331483
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=42
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valP.v=40000001
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=42
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=42
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-20.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040015
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=40000001
+Param.val=0.66666666666666662966
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-20.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=42
+Constraint.group.v=00000002
+Constraint.valP.v=40000001
+Constraint.ptA.v=00050000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(left_free_in_3d_roundtrip) {
+ CHECK_LOAD("left_free_in_3d.slvs");
+ CHECK_RENDER("left_free_in_3d.png");
+ CHECK_SAVE("left_free_in_3d.slvs");
+}
+
+TEST_CASE(right_free_in_3d_roundtrip) {
+ CHECK_LOAD("right_free_in_3d.slvs");
+ CHECK_RENDER("right_free_in_3d.png");
+ CHECK_SAVE("right_free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v20) {
+ CHECK_LOAD("free_in_3d_v20.slvs");
+ CHECK_SAVE("left_free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v22) {
+ CHECK_LOAD("free_in_3d_v22.slvs");
+ CHECK_SAVE("left_free_in_3d.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=31
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.entityA.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=31
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.entityA.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=31
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.entityA.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=31
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.entityA.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=31
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.entityA.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=31
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=5.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.entityA.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(reference_roundtrip) {
+ CHECK_LOAD("reference.slvs");
+ CHECK_RENDER("reference.png");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v20) {
+ CHECK_LOAD("reference_v20.slvs");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v22) {
+ CHECK_LOAD("reference_v22.slvs");
+ CHECK_SAVE("reference.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=30
+Constraint.group.v=00000002
+Constraint.valA=10.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.y=-1.49454882168925862196
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=30
+Constraint.group.v=00000002
+Constraint.valA=10.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.y=-1.49454882168925860000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=30
+Constraint.group.v=00000002
+Constraint.valA=10.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.y=-1.49454882168925862196
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=30
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=10.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.y=2.50000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=30
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=10.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.y=2.50000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=30
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=10.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.y=2.50000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=30
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=10.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.y=2.50000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=30
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=10.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.y=2.50000000000000000000
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=30
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.valA=10.00000000000000000000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=1
+Constraint.disp.offset.y=2.50000000000000000000
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(free_in_3d_roundtrip) {
+ CHECK_LOAD("free_in_3d.slvs");
+ CHECK_RENDER("free_in_3d.png");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v20) {
+ CHECK_LOAD("free_in_3d_v20.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v22) {
+ CHECK_LOAD("free_in_3d_v22.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(reference_roundtrip) {
+ CHECK_LOAD("reference.slvs");
+ CHECK_RENDER("reference.png");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v20) {
+ CHECK_LOAD("reference_v20.slvs");
+ CHECK_SAVE("reference.slvs");
+}
+
+TEST_CASE(reference_migrate_from_v22) {
+ CHECK_LOAD("reference_v22.slvs");
+ CHECK_SAVE("reference.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040020
+Param.val=0.70710678118654757274
+AddParam
+
+Param.h.v.=00040021
+Param.val=-0.70710678118654746172
+AddParam
+
+Param.h.v.=00040022
+AddParam
+
+Param.h.v.=00040023
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=40000002
+Param.val=1.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.actNormal.w=0.70710678118654757274
+Entity.actNormal.vx=-0.70710678118654746172
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000002
+Constraint.type=110
+Constraint.group.v=00000002
+Constraint.valP.v=40000002
+Constraint.entityA.v=00040020
+Constraint.entityB.v=00030020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040020
+Param.val=0.70710678118654757000
+AddParam
+
+Param.h.v.=00040021
+Param.val=-0.70710678118654746000
+AddParam
+
+Param.h.v.=00040022
+AddParam
+
+Param.h.v.=00040023
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.actNormal.w=0.70710678118654757000
+Entity.actNormal.vx=-0.70710678118654746000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000002
+Constraint.type=110
+Constraint.group.v=00000002
+Constraint.entityA.v=00040020
+Constraint.entityB.v=00030020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040020
+Param.val=0.70710678118654757000
+AddParam
+
+Param.h.v.=00040021
+Param.val=-0.70710678118654746000
+AddParam
+
+Param.h.v.=00040022
+AddParam
+
+Param.h.v.=00040023
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.actNormal.w=0.70710678118654757000
+Entity.actNormal.vx=-0.70710678118654746000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000002
+Constraint.type=110
+Constraint.group.v=00000002
+Constraint.entityA.v=00040020
+Constraint.entityB.v=00030020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-25.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040020
+Param.val=0.70710678118654757274
+AddParam
+
+Param.h.v.=00040021
+AddParam
+
+Param.h.v.=00040022
+Param.val=0.70710678118654746172
+AddParam
+
+Param.h.v.=00040023
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=00050020
+Param.val=0.70710678118654757274
+AddParam
+
+Param.h.v.=00050021
+AddParam
+
+Param.h.v.=00050022
+Param.val=-0.70710678118654757274
+AddParam
+
+Param.h.v.=00050023
+AddParam
+
+Param.h.v.=00050040
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=40000002
+Param.val=-1.00000000000000022204
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=400
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-25.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.actNormal.w=0.70710678118654757274
+Entity.actNormal.vy=0.70710678118654746172
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.normal.v=00050020
+Entity.distance.v=00050040
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.actNormal.w=0.70710678118654757274
+Entity.actNormal.vy=-0.70710678118654757274
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050040
+Entity.type=4000
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000002
+Constraint.type=110
+Constraint.group.v=00000002
+Constraint.valP.v=40000002
+Constraint.entityA.v=00050020
+Constraint.entityB.v=00040020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(same_group_roundtrip) {
+ CHECK_LOAD("same_group.slvs");
+ CHECK_RENDER("same_group.png");
+ CHECK_SAVE("same_group.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=00060010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=60
+Constraint.group.v=00000002
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00060000
+Constraint.entityA.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=00060010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=60
+Constraint.group.v=00000002
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00060000
+Constraint.entityA.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050012
+AddParam
+
+Param.h.v.=00060010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=60
+Constraint.group.v=00000002
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00060000
+Constraint.entityA.v=00030000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=60
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00040000
+Constraint.entityA.v=00020000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=60
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00040000
+Constraint.entityA.v=00020000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=60
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00040000
+Constraint.entityA.v=00020000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(free_in_3d_roundtrip) {
+ CHECK_LOAD("free_in_3d.slvs");
+ CHECK_RENDER("free_in_3d.png");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v20) {
+ CHECK_LOAD("free_in_3d_v20.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v22) {
+ CHECK_LOAD("free_in_3d_v22.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=61
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=61
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=61
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=63
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00060000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=63
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00060000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=63
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050000
+Constraint.ptB.v=00060000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=62
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=62
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=62
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.ptB.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(line_roundtrip) {
+ CHECK_LOAD("line.slvs");
+ CHECK_RENDER("line.png");
+ CHECK_SAVE("line.slvs");
+}
+
+TEST_CASE(line_migrate_from_v20) {
+ CHECK_LOAD("line_v20.slvs");
+ CHECK_SAVE("line.slvs");
+}
+
+TEST_CASE(line_migrate_from_v22) {
+ CHECK_LOAD("line_v22.slvs");
+ CHECK_SAVE("line.slvs");
+}
+
+TEST_CASE(pt_pt_roundtrip) {
+ CHECK_LOAD("pt_pt.slvs");
+ CHECK_RENDER("pt_pt.png");
+ CHECK_SAVE("pt_pt.slvs");
+}
+
+TEST_CASE(pt_pt_migrate_from_v20) {
+ CHECK_LOAD("pt_pt_v20.slvs");
+ CHECK_SAVE("pt_pt.slvs");
+}
+
+TEST_CASE(pt_pt_migrate_from_v22) {
+ CHECK_LOAD("pt_pt_v22.slvs");
+ CHECK_SAVE("pt_pt.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=200
+Constraint.group.v=00000002
+Constraint.ptA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=200
+Constraint.group.v=00000002
+Constraint.ptA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=200
+Constraint.group.v=00000002
+Constraint.ptA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=200
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=200
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=200
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(free_in_3d_roundtrip) {
+ CHECK_LOAD("free_in_3d.slvs");
+ CHECK_RENDER("free_in_3d.png");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v20) {
+ CHECK_LOAD("free_in_3d_v20.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v22) {
+ CHECK_LOAD("free_in_3d_v22.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
--- /dev/null
+#include "harness.h"
+
+#define CHECK_PARSE(var, expr) \
+ do { \
+ var = Expr::From(expr, false); \
+ CHECK_TRUE(var != NULL); \
+ } while(0)
+
+#define CHECK_PARSE_ERR(expr, msg) \
+ do { \
+ std::string err; \
+ Expr *e = Expr::Parse(expr, &err); \
+ CHECK_TRUE(e == NULL); \
+ CHECK_TRUE(err.find(msg) != std::string::npos); \
+ } while(0)
+
+TEST_CASE(constant) {
+ Expr *e;
+ CHECK_PARSE(e, "pi");
+ CHECK_EQ_EPS(e->Eval(), 3.1415926);
+}
+
+TEST_CASE(literal) {
+ Expr *e;
+ CHECK_PARSE(e, "42");
+ CHECK_TRUE(e->Eval() == 42);
+ CHECK_PARSE(e, "42.5");
+ CHECK_TRUE(e->Eval() == 42.5);
+ CHECK_PARSE(e, "1_000_000");
+ CHECK_TRUE(e->Eval() == 1000000);
+}
+
+TEST_CASE(unary_ops) {
+ Expr *e;
+ CHECK_PARSE(e, "-10");
+ CHECK_TRUE(e->Eval() == -10);
+}
+
+TEST_CASE(binary_ops) {
+ Expr *e;
+ CHECK_PARSE(e, "1 + 2");
+ CHECK_TRUE(e->Eval() == 3);
+ CHECK_PARSE(e, "1 - 2");
+ CHECK_TRUE(e->Eval() == -1);
+ CHECK_PARSE(e, "3 * 4");
+ CHECK_TRUE(e->Eval() == 12);
+ CHECK_PARSE(e, "3 / 4");
+ CHECK_TRUE(e->Eval() == 0.75);
+}
+
+TEST_CASE(parentheses) {
+ Expr *e;
+ CHECK_PARSE(e, "(1 + 2) * 3");
+ CHECK_TRUE(e->Eval() == 9);
+ CHECK_PARSE(e, "1 + (2 * 3)");
+ CHECK_TRUE(e->Eval() == 7);
+}
+
+TEST_CASE(functions) {
+ Expr *e;
+ CHECK_PARSE(e, "sqrt(2)");
+ CHECK_EQ_EPS(e->Eval(), 1.414213);
+ CHECK_PARSE(e, "square(3)");
+ CHECK_EQ_EPS(e->Eval(), 9);
+ CHECK_PARSE(e, "sin(180)");
+ CHECK_EQ_EPS(e->Eval(), 0);
+ CHECK_PARSE(e, "sin(90)");
+ CHECK_EQ_EPS(e->Eval(), 1);
+ CHECK_PARSE(e, "cos(180)");
+ CHECK_EQ_EPS(e->Eval(), -1);
+ CHECK_PARSE(e, "asin(1)");
+ CHECK_EQ_EPS(e->Eval(), 90);
+ CHECK_PARSE(e, "acos(0)");
+ CHECK_EQ_EPS(e->Eval(), 90);
+}
+
+TEST_CASE(variable) {
+ Expr *e;
+ CHECK_PARSE(e, "Var");
+ CHECK_TRUE(e->op == Expr::Op::VARIABLE);
+}
+
+TEST_CASE(precedence) {
+ Expr *e;
+ CHECK_PARSE(e, "2 + 3 * 4");
+ CHECK_TRUE(e->Eval() == 14);
+ CHECK_PARSE(e, "2 - 3 / 4");
+ CHECK_TRUE(e->Eval() == 1.25);
+ CHECK_PARSE(e, "-3 + 2");
+ CHECK_TRUE(e->Eval() == -1);
+ CHECK_PARSE(e, "2 + 3 - 4");
+ CHECK_TRUE(e->Eval() == 1);
+}
+
+TEST_CASE(errors) {
+ CHECK_PARSE_ERR("\x01",
+ "Unexpected character");
+ CHECK_PARSE_ERR("notavar",
+ "'notavar' is not a valid variable, function or constant");
+ CHECK_PARSE_ERR("1e2e3",
+ "'1e2e3' is not a valid number");
+ CHECK_PARSE_ERR("_",
+ "'_' is not a valid operator");
+ CHECK_PARSE_ERR("2 2",
+ "Expected an operator");
+ CHECK_PARSE_ERR("2 + +",
+ "Expected an operand");
+ CHECK_PARSE_ERR("( 2 + 2",
+ "Expected ')'");
+ CHECK_PARSE_ERR("(",
+ "Expected ')'");
+}
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(parseable) {
+ for(auto locale : Locales()) {
+ SetLocale(locale.Culture());
+ }
+ CHECK_TRUE(true); // didn't crash
+}
--- /dev/null
+#include "harness.h"
+
+using Platform::Path;
+
+#if defined(WIN32)
+#define S "\\"
+#define R "C:"
+#define U "\\\\?\\C:"
+#else
+#define S "/"
+#define R ""
+#define U ""
+#endif
+
+TEST_CASE(from_raw) {
+ Path path = Path::From("/foo");
+ CHECK_EQ_STR(path.raw, "/foo");
+}
+
+#if defined(WIN32) || defined(__APPLE__)
+TEST_CASE(equals_win32_apple) {
+ CHECK_TRUE(Path::From(R S "foo").Equals(Path::From(R S "foo")));
+ CHECK_TRUE(Path::From(R S "foo").Equals(Path::From(R S "FOO")));
+ CHECK_FALSE(Path::From(R S "foo").Equals(Path::From(R S "bar")));
+}
+#else
+TEST_CASE(equals_unix) {
+ CHECK_TRUE(Path::From(R S "foo").Equals(Path::From(R S "foo")));
+ CHECK_FALSE(Path::From(R S "foo").Equals(Path::From(R S "FOO")));
+ CHECK_FALSE(Path::From(R S "foo").Equals(Path::From(R S "bar")));
+}
+#endif
+
+#if defined(WIN32)
+TEST_CASE(is_absolute_win32) {
+ CHECK_TRUE(Path::From("c:\\foo").IsAbsolute());
+ CHECK_TRUE(Path::From("\\\\?\\c:\\").IsAbsolute());
+ CHECK_TRUE(Path::From("\\\\server\\share\\").IsAbsolute());
+ CHECK_FALSE(Path::From("c:/foo").IsAbsolute());
+ CHECK_FALSE(Path::From("c:foo").IsAbsolute());
+ CHECK_FALSE(Path::From("\\\\?").IsAbsolute());
+ CHECK_FALSE(Path::From("\\\\server\\").IsAbsolute());
+ CHECK_FALSE(Path::From("\\\\?\\c:").IsAbsolute());
+ CHECK_FALSE(Path::From("\\\\server\\share").IsAbsolute());
+ CHECK_FALSE(Path::From("foo").IsAbsolute());
+ CHECK_FALSE(Path::From("/foo").IsAbsolute());
+}
+#else
+TEST_CASE(is_absolute_unix) {
+ CHECK_TRUE(Path::From("/foo").IsAbsolute());
+ CHECK_FALSE(Path::From("c:/foo").IsAbsolute());
+ CHECK_FALSE(Path::From("c:\\foo").IsAbsolute());
+ CHECK_FALSE(Path::From("\\\\?\\foo").IsAbsolute());
+ CHECK_FALSE(Path::From("c:foo").IsAbsolute());
+ CHECK_FALSE(Path::From("foo").IsAbsolute());
+}
+#endif
+
+TEST_CASE(has_extension) {
+ CHECK_TRUE(Path::From("foo.bar").HasExtension("bar"));
+ CHECK_TRUE(Path::From("foo.bar").HasExtension("BAR"));
+ CHECK_TRUE(Path::From("foo.bAr").HasExtension("BaR"));
+ CHECK_TRUE(Path::From("foo.bar").HasExtension("bar"));
+ CHECK_FALSE(Path::From("foo.bar").HasExtension("baz"));
+}
+
+TEST_CASE(file_name) {
+ CHECK_EQ_STR(Path::From("foo").FileName(), "foo");
+ CHECK_EQ_STR(Path::From("foo" S "bar").FileName(), "bar");
+}
+
+TEST_CASE(file_stem) {
+ CHECK_EQ_STR(Path::From("foo").FileStem(), "foo");
+ CHECK_EQ_STR(Path::From("foo" S "bar").FileStem(), "bar");
+ CHECK_EQ_STR(Path::From("foo.ext").FileStem(), "foo");
+ CHECK_EQ_STR(Path::From("foo" S "bar.ext").FileStem(), "bar");
+}
+
+TEST_CASE(extension) {
+ CHECK_EQ_STR(Path::From("foo").Extension(), "");
+ CHECK_EQ_STR(Path::From("foo.bar").Extension(), "bar");
+ CHECK_EQ_STR(Path::From("foo.bar.baz").Extension(), "baz");
+}
+
+TEST_CASE(with_extension) {
+ CHECK_EQ_STR(Path::From("foo.bar").WithExtension("baz").raw, "foo.baz");
+ CHECK_EQ_STR(Path::From("foo").WithExtension("baz").raw, "foo.baz");
+}
+
+TEST_CASE(parent) {
+ Path path;
+ path = Path::From("foo" S "bar");
+ CHECK_EQ_STR(path.Parent().raw, "foo" S);
+ path = Path::From("foo" S "bar" S);
+ CHECK_EQ_STR(path.Parent().raw, "foo" S);
+ path = Path::From(R S "foo" S "bar");
+ CHECK_EQ_STR(path.Parent().raw, R S "foo" S);
+ path = Path::From(R S "foo");
+ CHECK_EQ_STR(path.Parent().raw, R S);
+
+ path = Path::From("");
+ CHECK_TRUE(path.Parent().IsEmpty());
+ path = Path::From("foo");
+ CHECK_TRUE(path.Parent().IsEmpty());
+ path = Path::From("foo" S);
+ CHECK_TRUE(path.Parent().IsEmpty());
+ path = Path::From(R S);
+ CHECK_TRUE(path.Parent().IsEmpty());
+}
+
+#if defined(WIN32)
+TEST_CASE(parent_win32) {
+ Path path;
+ path = Path::From(U S);
+ CHECK_TRUE(path.Parent().IsEmpty());
+}
+#endif
+
+TEST_CASE(join) {
+ Path path;
+ path = Path::From("foo");
+ CHECK_EQ_STR(path.Join(Path::From("bar")).raw, "foo" S "bar");
+ path = Path::From("foo" S);
+ CHECK_EQ_STR(path.Join(Path::From("bar")).raw, "foo" S "bar");
+
+ path = Path::From("");
+ CHECK_TRUE(path.Join(Path::From("bar")).IsEmpty());
+ path = Path::From("foo");
+ CHECK_TRUE(path.Join(Path::From("")).IsEmpty());
+ path = Path::From("foo");
+ CHECK_TRUE(path.Join(Path::From(R S "bar")).IsEmpty());
+}
+
+TEST_CASE(expand) {
+ Path path;
+ path = Path::From("foo");
+ CHECK_EQ_STR(path.Expand().raw, "foo");
+ path = Path::From("foo" S "bar");
+ CHECK_EQ_STR(path.Expand().raw, "foo" S "bar");
+ path = Path::From("foo" S);
+ CHECK_EQ_STR(path.Expand().raw, "foo");
+ path = Path::From("foo" S ".");
+ CHECK_EQ_STR(path.Expand().raw, "foo");
+ path = Path::From("foo" S "." S "bar");
+ CHECK_EQ_STR(path.Expand().raw, "foo" S "bar");
+ path = Path::From("foo" S ".." S "bar");
+ CHECK_EQ_STR(path.Expand().raw, "bar");
+ path = Path::From("foo" S "..");
+ CHECK_EQ_STR(path.Expand().raw, ".");
+ path = Path::From(R S "foo" S "..");
+ CHECK_EQ_STR(path.Expand().raw, U S);
+ path = Path::From(R S);
+ CHECK_EQ_STR(path.Expand().raw, U S);
+
+ path = Path::From(R S "..");
+ CHECK_TRUE(path.Expand().IsEmpty());
+ path = Path::From(R S ".." S "foo");
+ CHECK_TRUE(path.Expand().IsEmpty());
+ path = Path::From("..");
+ CHECK_TRUE(path.Expand().IsEmpty());
+}
+
+#if defined(WIN32)
+TEST_CASE(expand_win32) {
+ Path path;
+ path = Path::From(R S "foo");
+ CHECK_EQ_STR(path.Expand().raw, U S "foo");
+ path = Path::From(U S "foo");
+ CHECK_EQ_STR(path.Expand().raw, U S "foo");
+}
+#endif
+
+TEST_CASE(expand_from_cwd) {
+ Path cwd = Path::CurrentDirectory().Expand();
+
+ Path path;
+ path = Path::From(R S "foo");
+ CHECK_EQ_STR(path.Expand(/*fromCurrentDirectory=*/true).raw,
+ U S "foo");
+ path = Path::From("foo" S "bar");
+ CHECK_EQ_STR(path.Expand(/*fromCurrentDirectory=*/true).raw,
+ cwd.raw + S "foo" S "bar");
+ path = Path::From(".." S "bar");
+ CHECK_EQ_STR(path.Expand(/*fromCurrentDirectory=*/true).raw,
+ cwd.Parent().raw + "bar");
+}
+
+TEST_CASE(relative_to) {
+ Path base;
+ base = Path::From(R S "foo" S "bar");
+ CHECK_EQ_STR(Path::From(R S "foo").RelativeTo(base).raw,
+ "..");
+ base = Path::From(R S "foo" S "bar");
+ CHECK_EQ_STR(Path::From(R S "foo" S "baz").RelativeTo(base).raw,
+ ".." S "baz");
+ base = Path::From(R S "foo" S "bar");
+ CHECK_EQ_STR(Path::From(R S "foo" S "bar" S "quux").RelativeTo(base).raw,
+ "quux");
+ base = Path::From(R S "foo" S "bar");
+ CHECK_EQ_STR(Path::From(R S "foo" S "bar").RelativeTo(base).raw,
+ ".");
+
+ base = Path::From("foo");
+ CHECK_TRUE(Path::From(R S "foo" S "bar").RelativeTo(base).IsEmpty());
+ base = Path::From(R S "foo" S "bar");
+ CHECK_TRUE(Path::From("foo").RelativeTo(base).IsEmpty());
+}
+
+#if defined(WIN32)
+TEST_CASE(relative_to_win32) {
+ Path base;
+ base = Path::From("C:\\foo");
+ CHECK_EQ_STR(Path::From("\\\\?\\C:\\bar").RelativeTo(base).raw,
+ "..\\bar");
+ base = Path::From("C:\\foo");
+ CHECK_EQ_STR(Path::From("c:\\FOO").RelativeTo(base).raw,
+ ".");
+
+ base = Path::From("C:\\foo");
+ CHECK_TRUE(Path::From("D:\\bar").RelativeTo(base).IsEmpty());
+ CHECK_TRUE(Path::From("\\\\server\\share\\bar").RelativeTo(base).IsEmpty());
+}
+#elif defined(__APPLE__)
+TEST_CASE(relative_to_apple) {
+ Path base;
+ base = Path::From("/users/foo");
+ CHECK_EQ_STR(Path::From("/Users/FOO").RelativeTo(base).raw,
+ ".");
+}
+#else
+TEST_CASE(relative_to_unix) {
+ Path base;
+ base = Path::From("/users/foo");
+ CHECK_EQ_STR(Path::From("/Users/FOO").RelativeTo(base).raw,
+ "../../Users/FOO");
+}
+#endif
+
+TEST_CASE(from_portable) {
+ CHECK_EQ_STR(Path::FromPortable("foo/bar").raw, "foo" S "bar");
+}
+
+TEST_CASE(to_portable) {
+ CHECK_EQ_STR(Path::From("foo" S "bar").ToPortable(), "foo/bar");
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Our entry point for exposing various internal mechanisms.
+//
+// Copyright 2017 whitequark
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+
+int main(int argc, char **argv) {
+ std::vector<std::string> args = Platform::InitCli(argc, argv);
+
+ if(args.size() == 3 && args[1] == "expr") {
+ std::string expr = args[2], err;
+ Expr *e = Expr::Parse(expr.c_str(), &err);
+ if(e == NULL) {
+ fprintf(stderr, "cannot parse: %s\n", err.c_str());
+ } else {
+ fprintf(stderr, "%g\n", e->Eval());
+ }
+ FreeAllTemporary();
+ } else {
+ fprintf(stderr, "Usage: %s <command> <options>\n", args[0].c_str());
+//-----------------------------------------------------------------------------> 80 col */
+ fprintf(stderr, R"(
+Commands:
+ expr [expr]
+ Evaluate an expression.
+)");
+ }
+
+ return 0;
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000003
+Group.type=5300
+Group.order=2
+Group.name=rect-v20
+Group.activeWorkplane.v=00010000
+Group.color=00646464
+Group.skipFirst=0
+Group.meshCombine=2
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00010000 0
+ 2 00010001 0
+ 3 00010020 0
+ 4 00020000 0
+ 5 00020001 0
+ 6 00020020 0
+ 7 00030000 0
+ 8 00030001 0
+ 9 00030020 0
+ 10 00040000 0
+ 11 00040001 0
+ 12 00040002 0
+ 13 00050000 0
+ 14 00050001 0
+ 15 00050002 0
+ 16 00060000 0
+ 17 00060001 0
+ 18 00060002 0
+ 19 00070000 0
+ 20 00070001 0
+ 21 00070002 0
+ 22 80020000 0
+ 23 80020001 0
+ 24 80020002 0
+}
+Group.impFile=/TESTROOT/rect_v20.slvs
+Group.impFileRel=rect_v20.slvs
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=80030000
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=80030001
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=80030002
+AddParam
+
+Param.h.v.=80030003
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=80030004
+AddParam
+
+Param.h.v.=80030005
+AddParam
+
+Param.h.v.=80030006
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030005
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030008
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000b
+Entity.point[1].v=8003000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000b
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000e
+Entity.point[1].v=8003000f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030011
+Entity.point[1].v=80030012
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030013
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030014
+Entity.point[1].v=80030015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030014
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030015
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030017
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030018
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030018
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000003
+Group.type=5300
+Group.order=2
+Group.name=rect-v20
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000002
+Group.color=00646464
+Group.skipFirst=0
+Group.meshCombine=2
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00010000 0
+ 2 00010001 0
+ 3 00010020 0
+ 4 00020000 0
+ 5 00020001 0
+ 6 00020020 0
+ 7 00030000 0
+ 8 00030001 0
+ 9 00030020 0
+ 10 00040000 0
+ 11 00040001 0
+ 12 00040002 0
+ 13 00050000 0
+ 14 00050001 0
+ 15 00050002 0
+ 16 00060000 0
+ 17 00060001 0
+ 18 00060002 0
+ 19 00070000 0
+ 20 00070001 0
+ 21 00070002 0
+ 22 80020000 0
+ 23 80020001 0
+ 24 80020002 0
+}
+Group.impFile=Z:\TESTROOT\rect_v20.slvs
+Group.impFileRel=rect_v20.slvs
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=80030000
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=80030001
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=80030002
+AddParam
+
+Param.h.v.=80030003
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=80030004
+AddParam
+
+Param.h.v.=80030005
+AddParam
+
+Param.h.v.=80030006
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030005
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030008
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000b
+Entity.point[1].v=8003000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000b
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000e
+Entity.point[1].v=8003000f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030011
+Entity.point[1].v=80030012
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030013
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030014
+Entity.point[1].v=80030015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030014
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030015
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030017
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030018
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030018
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000003
+Group.type=5300
+Group.order=2
+Group.name=rect-v20
+Group.activeWorkplane.v=00010000
+Group.color=00646464
+Group.skipFirst=0
+Group.meshCombine=2
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00010000 0
+ 2 00010001 0
+ 3 00010020 0
+ 4 00020000 0
+ 5 00020001 0
+ 6 00020020 0
+ 7 00030000 0
+ 8 00030001 0
+ 9 00030020 0
+ 10 00040000 0
+ 11 00040001 0
+ 12 00040002 0
+ 13 00050000 0
+ 14 00050001 0
+ 15 00050002 0
+ 16 00060000 0
+ 17 00060001 0
+ 18 00060002 0
+ 19 00070000 0
+ 20 00070001 0
+ 21 00070002 0
+ 22 80020000 0
+ 23 80020001 0
+ 24 80020002 0
+}
+Group.impFile=/TESTROOT/rect_v20.slvs
+Group.impFileRel=rect_v20.slvs
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=80030000
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=80030001
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=80030002
+AddParam
+
+Param.h.v.=80030003
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=80030004
+AddParam
+
+Param.h.v.=80030005
+AddParam
+
+Param.h.v.=80030006
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030005
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030008
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000b
+Entity.point[1].v=8003000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000b
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000e
+Entity.point[1].v=8003000f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030011
+Entity.point[1].v=80030012
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030013
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030014
+Entity.point[1].v=80030015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030014
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030015
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030017
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030018
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030018
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00070014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00070001
+Entity.point[1].v=00070002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040001
+Constraint.ptB.v=00050002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050001
+Constraint.ptB.v=00060002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00060001
+Constraint.ptB.v=00070002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00070001
+Constraint.ptB.v=00040002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000005
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000006
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000007
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000008
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00070000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000003
+Group.type=5100
+Group.order=2
+Group.name=extrude
+Group.opA.v=00000002
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.meshCombine=2
+Group.predef.entityB.v=80020000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00040000 1002
+ 2 00040001 1002
+ 3 00040002 1002
+ 4 00040000 1001
+ 5 00040001 1001
+ 6 00040002 1001
+ 7 00040000 1004
+ 8 00040001 1003
+ 9 00040002 1003
+ 10 00050000 1002
+ 11 00050001 1002
+ 12 00050002 1002
+ 13 00050000 1001
+ 14 00050001 1001
+ 15 00050002 1001
+ 16 00050000 1004
+ 17 00050001 1003
+ 18 00050002 1003
+ 19 00060000 1002
+ 20 00060001 1002
+ 21 00060002 1002
+ 22 00060000 1001
+ 23 00060001 1001
+ 24 00060002 1001
+ 25 00060000 1004
+ 26 00060001 1003
+ 27 00060002 1003
+ 28 00070000 1002
+ 29 00070001 1002
+ 30 00070002 1002
+ 31 00070000 1001
+ 32 00070001 1001
+ 33 00070002 1001
+ 34 00070000 1004
+ 35 00070001 1003
+ 36 00070002 1003
+ 37 80020000 1002
+ 38 80020000 1001
+ 39 80020001 1002
+ 40 80020002 1002
+ 41 80020001 1001
+ 42 80020002 1001
+ 43 80020002 1003
+ 44 00000000 1001
+ 45 00000000 1002
+}
+AddGroup
+
+Group.h.v=00000004
+Group.type=5201
+Group.order=3
+Group.name=translate
+Group.opA.v=00000003
+Group.valA=2.00000000000000000000
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.meshCombine=2
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 80030001 0
+ 2 80030002 0
+ 3 80030003 0
+ 4 80030004 0
+ 5 80030005 0
+ 6 80030006 0
+ 7 80030007 0
+ 8 80030008 0
+ 9 80030009 0
+ 10 8003000a 0
+ 11 8003000b 0
+ 12 8003000c 0
+ 13 8003000d 0
+ 14 8003000e 0
+ 15 8003000f 0
+ 16 80030010 0
+ 17 80030011 0
+ 18 80030012 0
+ 19 80030013 0
+ 20 80030014 0
+ 21 80030015 0
+ 22 80030016 0
+ 23 80030017 0
+ 24 80030018 0
+ 25 80030019 0
+ 26 8003001a 0
+ 27 8003001b 0
+ 28 8003001c 0
+ 29 8003001d 0
+ 30 8003001e 0
+ 31 8003001f 0
+ 32 80030020 0
+ 33 80030021 0
+ 34 80030022 0
+ 35 80030023 0
+ 36 80030024 0
+ 37 80030027 0
+ 38 80030028 0
+ 39 80030029 0
+ 40 8003002a 0
+ 41 8003002b 0
+ 42 8003002c 0
+ 43 8003002d 0
+ 44 80030001 1
+ 45 80030002 1
+ 46 80030003 1
+ 47 80030004 1
+ 48 80030005 1
+ 49 80030006 1
+ 50 80030007 1
+ 51 80030008 1
+ 52 80030009 1
+ 53 8003000a 1
+ 54 8003000b 1
+ 55 8003000c 1
+ 56 8003000d 1
+ 57 8003000e 1
+ 58 8003000f 1
+ 59 80030010 1
+ 60 80030011 1
+ 61 80030012 1
+ 62 80030013 1
+ 63 80030014 1
+ 64 80030015 1
+ 65 80030016 1
+ 66 80030017 1
+ 67 80030018 1
+ 68 80030019 1
+ 69 8003001a 1
+ 70 8003001b 1
+ 71 8003001c 1
+ 72 8003001d 1
+ 73 8003001e 1
+ 74 8003001f 1
+ 75 80030020 1
+ 76 80030021 1
+ 77 80030022 1
+ 78 80030023 1
+ 79 80030024 1
+ 80 80030027 1
+ 81 80030028 1
+ 82 80030029 1
+ 83 8003002a 1
+ 84 8003002b 1
+ 85 8003002c 1
+ 86 8003002d 1
+ 87 80030001 1000
+ 88 80030002 1000
+ 89 80030003 1000
+ 90 80030004 1000
+ 91 80030005 1000
+ 92 80030006 1000
+ 93 80030007 1000
+ 94 80030008 1000
+ 95 80030009 1000
+ 96 8003000a 1000
+ 97 8003000b 1000
+ 98 8003000c 1000
+ 99 8003000d 1000
+ 100 8003000e 1000
+ 101 8003000f 1000
+ 102 80030010 1000
+ 103 80030011 1000
+ 104 80030012 1000
+ 105 80030013 1000
+ 106 80030014 1000
+ 107 80030015 1000
+ 108 80030016 1000
+ 109 80030017 1000
+ 110 80030018 1000
+ 111 80030019 1000
+ 112 8003001a 1000
+ 113 8003001b 1000
+ 114 8003001c 1000
+ 115 8003001d 1000
+ 116 8003001e 1000
+ 117 8003001f 1000
+ 118 80030020 1000
+ 119 80030021 1000
+ 120 80030022 1000
+ 121 80030023 1000
+ 122 80030024 1000
+ 123 80030027 1000
+ 124 80030028 1000
+ 125 80030029 1000
+ 126 8003002a 1000
+ 127 8003002b 1000
+ 128 8003002c 1000
+ 129 8003002d 1000
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00070014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030000
+AddParam
+
+Param.h.v.=80030001
+AddParam
+
+Param.h.v.=80030002
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80040000
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80040001
+Param.val=2.50000000000000000000
+AddParam
+
+Param.h.v.=80040002
+Param.val=-2.50000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00070001
+Entity.point[1].v=00070002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030001
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.point[1].v=80030003
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030004
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030005
+Entity.point[1].v=80030006
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030007
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030005
+Entity.point[1].v=80030002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.point[1].v=80030003
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000b
+Entity.point[1].v=8003000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000e
+Entity.point[1].v=8003000f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000e
+Entity.point[1].v=8003000b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.point[1].v=8003000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030013
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030014
+Entity.point[1].v=80030015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030014
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030015
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030016
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030017
+Entity.point[1].v=80030018
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030017
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030018
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030019
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030017
+Entity.point[1].v=80030014
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030018
+Entity.point[1].v=80030015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003001d
+Entity.point[1].v=8003001e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030020
+Entity.point[1].v=80030021
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030020
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030021
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030022
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030023
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030020
+Entity.point[1].v=8003001d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030024
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030021
+Entity.point[1].v=8003001e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030027
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030028
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030028
+Entity.type=2010
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030029
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003002a
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002a
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002b
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8003002a
+Entity.point[1].v=80030028
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002c
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003002a
+Entity.actPoint.z=10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002d
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=80030028
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040001
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040002
+Entity.point[1].v=80040003
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040002
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040003
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040004
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040005
+Entity.point[1].v=80040006
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040005
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040007
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040008
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040005
+Entity.point[1].v=80040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040006
+Entity.point[1].v=80040003
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004000a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004000b
+Entity.point[1].v=8004000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004000b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004000c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004000d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004000e
+Entity.point[1].v=8004000f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004000e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004000f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040010
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040011
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004000e
+Entity.point[1].v=8004000b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040012
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004000f
+Entity.point[1].v=8004000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040013
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040014
+Entity.point[1].v=80040015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040014
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040015
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040016
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040017
+Entity.point[1].v=80040018
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040017
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040018
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040019
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004001a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040017
+Entity.point[1].v=80040014
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004001b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040018
+Entity.point[1].v=80040015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004001c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004001d
+Entity.point[1].v=8004001e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004001d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004001e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004001f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040020
+Entity.point[1].v=80040021
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040020
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040021
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040022
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040023
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040020
+Entity.point[1].v=8004001d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040024
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040021
+Entity.point[1].v=8004001e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040025
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80040026
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040026
+Entity.type=2010
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040027
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80040028
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040028
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040029
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=80040028
+Entity.point[1].v=80040026
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004002a
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.z=10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004002b
+Entity.type=5003
+Entity.construction=0
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040057
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040058
+Entity.point[1].v=80040059
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040058
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040059
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004005a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004005b
+Entity.point[1].v=8004005c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004005b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004005c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004005d
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004005e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004005b
+Entity.point[1].v=80040058
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004005f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004005c
+Entity.point[1].v=80040059
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040060
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040061
+Entity.point[1].v=80040062
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040061
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040062
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040063
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040064
+Entity.point[1].v=80040065
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040064
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040065
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040066
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040067
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040064
+Entity.point[1].v=80040061
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040068
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040065
+Entity.point[1].v=80040062
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040069
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004006a
+Entity.point[1].v=8004006b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004006a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004006b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004006c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004006d
+Entity.point[1].v=8004006e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004006d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004006e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004006f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040070
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004006d
+Entity.point[1].v=8004006a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040071
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004006e
+Entity.point[1].v=8004006b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040072
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040073
+Entity.point[1].v=80040074
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040073
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040074
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040075
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040076
+Entity.point[1].v=80040077
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040076
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040077
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040078
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040079
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040076
+Entity.point[1].v=80040073
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004007a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040077
+Entity.point[1].v=80040074
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004007b
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8004007c
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004007c
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004007d
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8004007e
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004007e
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004007f
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8004007e
+Entity.point[1].v=8004007c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040080
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040081
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040001
+Constraint.ptB.v=00050002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050001
+Constraint.ptB.v=00060002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00060001
+Constraint.ptB.v=00070002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00070001
+Constraint.ptB.v=00040002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000005
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000006
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000007
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000008
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00070000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Surface 00000001 00646464 8004002a 1 1
+SCtrl 0 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000010000000827 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 -5.00000000010000000827 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000010000000827 -5.00000000010000000827 10.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000001 0 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+TrimBy 00000004 0 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+TrimBy 00000007 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+TrimBy 0000000a 0 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+AddSurface
+Surface 00000002 00646464 8004002b 1 1
+SCtrl 0 0 10.00000000010000000827 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000010000000827 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000002 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000005 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000008 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000b 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000003 00646464 80040010 1 1
+SCtrl 0 0 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000001 1 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+TrimBy 00000002 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000003 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+TrimBy 0000000c 0 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000004 00646464 80040019 1 1
+SCtrl 0 0 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000004 1 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+TrimBy 00000005 0 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000006 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+TrimBy 00000003 0 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000005 00646464 80040022 1 1
+SCtrl 0 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000007 1 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+TrimBy 00000008 0 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000009 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+TrimBy 00000006 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000006 00646464 80040007 1 1
+SCtrl 0 0 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000a 1 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+TrimBy 0000000b 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000c 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+TrimBy 00000009 0 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000007 00646464 80040080 1 1
+SCtrl 0 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 20.00000000010000178463 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 -0.00000000010000000827 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 20.00000000010000178463 -0.00000000010000000827 5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000d 0 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000010 0 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+TrimBy 00000013 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+TrimBy 00000016 0 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+AddSurface
+Surface 00000008 00646464 80040081 1 1
+SCtrl 0 0 20.00000000010000178463 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 20.00000000010000178463 -0.00000000010000000827 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 -0.00000000010000000827 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000e 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+TrimBy 00000011 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+TrimBy 00000014 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+TrimBy 00000017 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddSurface
+Surface 00000009 00646464 80040066 1 1
+SCtrl 0 0 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000d 1 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 0000000e 0 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+TrimBy 0000000f 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000018 0 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+AddSurface
+Surface 0000000a 00646464 8004006f 1 1
+SCtrl 0 0 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000010 1 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000011 0 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+TrimBy 00000012 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+TrimBy 0000000f 0 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+AddSurface
+Surface 0000000b 00646464 80040078 1 1
+SCtrl 0 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000013 1 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+TrimBy 00000014 0 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+TrimBy 00000015 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+TrimBy 00000012 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddSurface
+Surface 0000000c 00646464 8004005d 1 1
+SCtrl 0 0 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000016 1 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+TrimBy 00000017 0 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+TrimBy 00000018 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000015 0 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddSurface
+Curve 00000001 1 1 00000001 00000003
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+AddCurve
+Curve 00000002 1 1 00000002 00000003
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000003 1 1 00000003 00000004
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000004 1 1 00000001 00000004
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+AddCurve
+Curve 00000005 1 1 00000002 00000004
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000006 1 1 00000004 00000005
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000007 1 1 00000001 00000005
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+AddCurve
+Curve 00000008 1 1 00000002 00000005
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000009 1 1 00000005 00000006
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000a 1 1 00000001 00000006
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+AddCurve
+Curve 0000000b 1 1 00000002 00000006
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000c 1 1 00000006 00000003
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000d 1 1 00000007 00000009
+CCtrl 0 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+AddCurve
+Curve 0000000e 1 1 00000008 00000009
+CCtrl 0 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 0000000f 1 1 00000009 0000000a
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 00000010 1 1 00000007 0000000a
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+AddCurve
+Curve 00000011 1 1 00000008 0000000a
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 00000012 1 1 0000000a 0000000b
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 00000013 1 1 00000007 0000000b
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+CurvePt 1 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+AddCurve
+Curve 00000014 1 1 00000008 0000000b
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+CurvePt 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 00000015 1 1 0000000b 0000000c
+CCtrl 0 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+CurvePt 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 00000016 1 1 00000007 0000000c
+CCtrl 0 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+CurvePt 1 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+AddCurve
+Curve 00000017 1 1 00000008 0000000c
+CCtrl 0 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+CurvePt 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 00000018 1 1 0000000c 00000009
+CCtrl 0 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+CurvePt 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+AddCurve
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000003
+Group.type=5100
+Group.order=2
+Group.name=extrude
+Group.opA.v=00000002
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.meshCombine=2
+Group.predef.entityB.v=80020000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00040000 1002
+ 2 00040001 1002
+ 3 00040002 1002
+ 4 00040000 1001
+ 5 00040001 1001
+ 6 00040002 1001
+ 7 00040000 1004
+ 8 00040001 1003
+ 9 00040002 1003
+ 10 00050000 1002
+ 11 00050001 1002
+ 12 00050002 1002
+ 13 00050000 1001
+ 14 00050001 1001
+ 15 00050002 1001
+ 16 00050000 1004
+ 17 00050001 1003
+ 18 00050002 1003
+ 19 00060000 1002
+ 20 00060001 1002
+ 21 00060002 1002
+ 22 00060000 1001
+ 23 00060001 1001
+ 24 00060002 1001
+ 25 00060000 1004
+ 26 00060001 1003
+ 27 00060002 1003
+ 28 00070000 1002
+ 29 00070001 1002
+ 30 00070002 1002
+ 31 00070000 1001
+ 32 00070001 1001
+ 33 00070002 1001
+ 34 00070000 1004
+ 35 00070001 1003
+ 36 00070002 1003
+ 37 80020000 1002
+ 38 80020000 1001
+ 39 80020001 1002
+ 40 80020002 1002
+ 41 80020001 1001
+ 42 80020002 1001
+ 43 80020002 1003
+ 44 00000000 1001
+ 45 00000000 1002
+}
+AddGroup
+
+Group.h.v=00000004
+Group.type=5201
+Group.order=3
+Group.name=translate
+Group.opA.v=00000003
+Group.valA=2.00000000000000000000
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.meshCombine=2
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 80030001 0
+ 2 80030002 0
+ 3 80030003 0
+ 4 80030004 0
+ 5 80030005 0
+ 6 80030006 0
+ 7 80030007 0
+ 8 80030008 0
+ 9 80030009 0
+ 10 8003000a 0
+ 11 8003000b 0
+ 12 8003000c 0
+ 13 8003000d 0
+ 14 8003000e 0
+ 15 8003000f 0
+ 16 80030010 0
+ 17 80030011 0
+ 18 80030012 0
+ 19 80030013 0
+ 20 80030014 0
+ 21 80030015 0
+ 22 80030016 0
+ 23 80030017 0
+ 24 80030018 0
+ 25 80030019 0
+ 26 8003001a 0
+ 27 8003001b 0
+ 28 8003001c 0
+ 29 8003001d 0
+ 30 8003001e 0
+ 31 8003001f 0
+ 32 80030020 0
+ 33 80030021 0
+ 34 80030022 0
+ 35 80030023 0
+ 36 80030024 0
+ 37 80030027 0
+ 38 80030028 0
+ 39 80030029 0
+ 40 8003002a 0
+ 41 8003002b 0
+ 42 8003002c 0
+ 43 8003002d 0
+ 44 80030001 1
+ 45 80030002 1
+ 46 80030003 1
+ 47 80030004 1
+ 48 80030005 1
+ 49 80030006 1
+ 50 80030007 1
+ 51 80030008 1
+ 52 80030009 1
+ 53 8003000a 1
+ 54 8003000b 1
+ 55 8003000c 1
+ 56 8003000d 1
+ 57 8003000e 1
+ 58 8003000f 1
+ 59 80030010 1
+ 60 80030011 1
+ 61 80030012 1
+ 62 80030013 1
+ 63 80030014 1
+ 64 80030015 1
+ 65 80030016 1
+ 66 80030017 1
+ 67 80030018 1
+ 68 80030019 1
+ 69 8003001a 1
+ 70 8003001b 1
+ 71 8003001c 1
+ 72 8003001d 1
+ 73 8003001e 1
+ 74 8003001f 1
+ 75 80030020 1
+ 76 80030021 1
+ 77 80030022 1
+ 78 80030023 1
+ 79 80030024 1
+ 80 80030027 1
+ 81 80030028 1
+ 82 80030029 1
+ 83 8003002a 1
+ 84 8003002b 1
+ 85 8003002c 1
+ 86 8003002d 1
+ 87 80030001 1000
+ 88 80030002 1000
+ 89 80030003 1000
+ 90 80030004 1000
+ 91 80030005 1000
+ 92 80030006 1000
+ 93 80030007 1000
+ 94 80030008 1000
+ 95 80030009 1000
+ 96 8003000a 1000
+ 97 8003000b 1000
+ 98 8003000c 1000
+ 99 8003000d 1000
+ 100 8003000e 1000
+ 101 8003000f 1000
+ 102 80030010 1000
+ 103 80030011 1000
+ 104 80030012 1000
+ 105 80030013 1000
+ 106 80030014 1000
+ 107 80030015 1000
+ 108 80030016 1000
+ 109 80030017 1000
+ 110 80030018 1000
+ 111 80030019 1000
+ 112 8003001a 1000
+ 113 8003001b 1000
+ 114 8003001c 1000
+ 115 8003001d 1000
+ 116 8003001e 1000
+ 117 8003001f 1000
+ 118 80030020 1000
+ 119 80030021 1000
+ 120 80030022 1000
+ 121 80030023 1000
+ 122 80030024 1000
+ 123 80030027 1000
+ 124 80030028 1000
+ 125 80030029 1000
+ 126 8003002a 1000
+ 127 8003002b 1000
+ 128 8003002c 1000
+ 129 8003002d 1000
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00070013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00070014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030000
+AddParam
+
+Param.h.v.=80030001
+AddParam
+
+Param.h.v.=80030002
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80040000
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80040001
+Param.val=2.50000000000000000000
+AddParam
+
+Param.h.v.=80040002
+Param.val=-2.50000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00070001
+Entity.point[1].v=00070002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030001
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.point[1].v=80030003
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030004
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030005
+Entity.point[1].v=80030006
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030007
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030005
+Entity.point[1].v=80030002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.point[1].v=80030003
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000b
+Entity.point[1].v=8003000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000e
+Entity.point[1].v=8003000f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000e
+Entity.point[1].v=8003000b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.point[1].v=8003000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030013
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030014
+Entity.point[1].v=80030015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030014
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030015
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030016
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030017
+Entity.point[1].v=80030018
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030017
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030018
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030019
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030017
+Entity.point[1].v=80030014
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030018
+Entity.point[1].v=80030015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003001d
+Entity.point[1].v=8003001e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030020
+Entity.point[1].v=80030021
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030020
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030021
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030022
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030023
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030020
+Entity.point[1].v=8003001d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030024
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030021
+Entity.point[1].v=8003001e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030027
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030028
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030028
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030029
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003002a
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003002a
+Entity.point[1].v=80030028
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002c
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003002a
+Entity.actPoint.z=10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002d
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=80030028
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040001
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040002
+Entity.point[1].v=80040003
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040002
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040003
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040004
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040005
+Entity.point[1].v=80040006
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040005
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040007
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040008
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040005
+Entity.point[1].v=80040002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040006
+Entity.point[1].v=80040003
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004000a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004000b
+Entity.point[1].v=8004000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004000b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004000c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004000d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004000e
+Entity.point[1].v=8004000f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004000e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004000f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040010
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040011
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004000e
+Entity.point[1].v=8004000b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040012
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004000f
+Entity.point[1].v=8004000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040013
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040014
+Entity.point[1].v=80040015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040014
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040015
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040016
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040017
+Entity.point[1].v=80040018
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040017
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040018
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040019
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004001a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040017
+Entity.point[1].v=80040014
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004001b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040018
+Entity.point[1].v=80040015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004001c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004001d
+Entity.point[1].v=8004001e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004001d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004001e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004001f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040020
+Entity.point[1].v=80040021
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040020
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040021
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040022
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040023
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040020
+Entity.point[1].v=8004001d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040024
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040021
+Entity.point[1].v=8004001e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040025
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80040026
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040026
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040027
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80040028
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040028
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040029
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040028
+Entity.point[1].v=80040026
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004002a
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.z=10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004002b
+Entity.type=5003
+Entity.construction=0
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040057
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040058
+Entity.point[1].v=80040059
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040058
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040059
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004005a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004005b
+Entity.point[1].v=8004005c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004005b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004005c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004005d
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004005e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004005b
+Entity.point[1].v=80040058
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004005f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004005c
+Entity.point[1].v=80040059
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040060
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040061
+Entity.point[1].v=80040062
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040061
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040062
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040063
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040064
+Entity.point[1].v=80040065
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040064
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040065
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040066
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040067
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040064
+Entity.point[1].v=80040061
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040068
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040065
+Entity.point[1].v=80040062
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040069
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004006a
+Entity.point[1].v=8004006b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004006a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004006b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004006c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004006d
+Entity.point[1].v=8004006e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004006d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004006e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004006f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040070
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004006d
+Entity.point[1].v=8004006a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040071
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004006e
+Entity.point[1].v=8004006b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040072
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040073
+Entity.point[1].v=80040074
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040073
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040074
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040075
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040076
+Entity.point[1].v=80040077
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040076
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040077
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040078
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040079
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040076
+Entity.point[1].v=80040073
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004007a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80040077
+Entity.point[1].v=80040074
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004007b
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8004007c
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004007c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004007d
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8004007e
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004007e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8004007f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8004007e
+Entity.point[1].v=8004007c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040080
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=5.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040081
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=-5.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040001
+Constraint.ptB.v=00050002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050001
+Constraint.ptB.v=00060002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00060001
+Constraint.ptB.v=00070002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00070001
+Constraint.ptB.v=00040002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000005
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000006
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000007
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000008
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00070000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Surface 00000001 00646464 8004002a 1 1
+SCtrl 0 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000010000000827 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 -5.00000000010000000827 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000010000000827 -5.00000000010000000827 10.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000001 0 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+TrimBy 00000004 0 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+TrimBy 00000007 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+TrimBy 0000000a 0 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+AddSurface
+Surface 00000002 00646464 8004002b 1 1
+SCtrl 0 0 10.00000000010000000827 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000010000000827 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000002 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000005 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000008 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000b 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000003 00646464 80040010 1 1
+SCtrl 0 0 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000001 1 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+TrimBy 00000002 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000003 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+TrimBy 0000000c 0 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000004 00646464 80040019 1 1
+SCtrl 0 0 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000004 1 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+TrimBy 00000005 0 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000006 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+TrimBy 00000003 0 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000005 00646464 80040022 1 1
+SCtrl 0 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000007 1 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+TrimBy 00000008 0 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000009 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+TrimBy 00000006 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000006 00646464 80040007 1 1
+SCtrl 0 0 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000a 1 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+TrimBy 0000000b 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000c 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+TrimBy 00000009 0 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000007 00646464 80040080 1 1
+SCtrl 0 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 20.00000000010000178463 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 -0.00000000010000000827 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 20.00000000010000178463 -0.00000000010000000827 5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000d 0 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000010 0 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+TrimBy 00000013 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+TrimBy 00000016 0 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+AddSurface
+Surface 00000008 00646464 80040081 1 1
+SCtrl 0 0 20.00000000010000178463 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 20.00000000010000178463 -0.00000000010000000827 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 -0.00000000010000000827 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000e 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+TrimBy 00000011 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+TrimBy 00000014 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+TrimBy 00000017 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddSurface
+Surface 00000009 00646464 80040066 1 1
+SCtrl 0 0 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000d 1 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 0000000e 0 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+TrimBy 0000000f 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000018 0 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+AddSurface
+Surface 0000000a 00646464 8004006f 1 1
+SCtrl 0 0 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000010 1 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000011 0 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+TrimBy 00000012 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+TrimBy 0000000f 0 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+AddSurface
+Surface 0000000b 00646464 80040078 1 1
+SCtrl 0 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000013 1 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+TrimBy 00000014 0 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+TrimBy 00000015 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+TrimBy 00000012 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddSurface
+Surface 0000000c 00646464 8004005d 1 1
+SCtrl 0 0 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000016 1 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+TrimBy 00000017 0 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+TrimBy 00000018 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+TrimBy 00000015 0 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddSurface
+Curve 00000001 1 1 00000001 00000003
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+AddCurve
+Curve 00000002 1 1 00000002 00000003
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000003 1 1 00000003 00000004
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000004 1 1 00000001 00000004
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+AddCurve
+Curve 00000005 1 1 00000002 00000004
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000006 1 1 00000004 00000005
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000007 1 1 00000001 00000005
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+AddCurve
+Curve 00000008 1 1 00000002 00000005
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000009 1 1 00000005 00000006
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000a 1 1 00000001 00000006
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 10.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+AddCurve
+Curve 0000000b 1 1 00000002 00000006
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000c 1 1 00000006 00000003
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 10.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000d 1 1 00000007 00000009
+CCtrl 0 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+AddCurve
+Curve 0000000e 1 1 00000008 00000009
+CCtrl 0 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 0000000f 1 1 00000009 0000000a
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 00000010 1 1 00000007 0000000a
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+AddCurve
+Curve 00000011 1 1 00000008 0000000a
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 00000012 1 1 0000000a 0000000b
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 00000013 1 1 00000007 0000000b
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+CurvePt 1 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+AddCurve
+Curve 00000014 1 1 00000008 0000000b
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+CurvePt 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 00000015 1 1 0000000b 0000000c
+CCtrl 0 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+CurvePt 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 00000016 1 1 00000007 0000000c
+CCtrl 0 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 10.00000000000000000000 5.00000000000000000000
+CurvePt 1 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+AddCurve
+Curve 00000017 1 1 00000008 0000000c
+CCtrl 0 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 10.00000000000000000000 -5.00000000000000000000
+CurvePt 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+AddCurve
+Curve 00000018 1 1 0000000c 00000009
+CCtrl 0 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 0.00000000000000000000 5.00000000000000000000
+CurvePt 1 20.00000000000000000000 0.00000000000000000000 -5.00000000000000000000
+AddCurve
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER_ISO("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_inters) {
+ CHECK_LOAD("normal.slvs");
+
+ Group *g = SK.GetGroup(SS.GW.activeGroup);
+ g->GenerateDisplayItems();
+ SMesh *m = &g->displayMesh;
+
+ SEdgeList el = {};
+ bool inters, leaks;
+ SKdNode::From(m)->MakeCertainEdgesInto(&el,
+ EdgeKind::SELF_INTER, /*coplanarIsInter=*/false, &inters, &leaks);
+ el.Clear();
+
+ // The assembly is supposed to interfere.
+ CHECK_TRUE(inters);
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000003
+Group.type=5100
+Group.order=2
+Group.name=extrude
+Group.opA.v=00000002
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.predef.entityB.v=80020000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00040000 1002
+ 2 00040001 1002
+ 3 00040002 1002
+ 4 00040000 1001
+ 5 00040001 1001
+ 6 00040002 1001
+ 7 00040000 1004
+ 8 00040001 1003
+ 9 00040002 1003
+ 10 00050000 1002
+ 11 00050001 1002
+ 12 00050002 1002
+ 13 00050000 1001
+ 14 00050001 1001
+ 15 00050002 1001
+ 16 00050000 1004
+ 17 00050001 1003
+ 18 00050002 1003
+ 19 00060000 1002
+ 20 00060001 1002
+ 21 00060002 1002
+ 22 00060000 1001
+ 23 00060001 1001
+ 24 00060002 1001
+ 25 00060000 1004
+ 26 00060001 1003
+ 27 00060002 1003
+ 28 00070000 1002
+ 29 00070001 1002
+ 30 00070002 1002
+ 31 00070000 1001
+ 32 00070001 1001
+ 33 00070002 1001
+ 34 00070000 1004
+ 35 00070001 1003
+ 36 00070002 1003
+ 37 80020000 1002
+ 38 80020000 1001
+ 39 80020001 1002
+ 40 80020002 1002
+ 41 80020001 1001
+ 42 80020002 1001
+ 43 80020002 1003
+ 44 00000000 1001
+ 45 00000000 1002
+}
+AddGroup
+
+Group.h.v=00000004
+Group.type=5001
+Group.order=3
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80040000
+Group.color=00646464
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=80030028
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000005
+Group.type=5100
+Group.order=4
+Group.name=extrude
+Group.opA.v=00000004
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.meshCombine=1
+Group.predef.entityB.v=80040000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00080000 1002
+ 2 00080001 1002
+ 3 00080020 1002
+ 4 00080040 1002
+ 5 00080000 1001
+ 6 00080001 1001
+ 7 00080020 1001
+ 8 00080040 1001
+ 9 00080001 1003
+ 10 80040000 1002
+ 11 80040000 1001
+ 12 80040001 1002
+ 13 80040002 1002
+ 14 80040001 1001
+ 15 80040002 1001
+ 16 80040002 1003
+ 17 00000000 1001
+ 18 00000000 1002
+ 19 00080002 1002
+ 20 00080002 1001
+ 21 00080000 1004
+ 22 00080002 1003
+ 23 00090000 1002
+ 24 00090001 1002
+ 25 00090002 1002
+ 26 00090000 1001
+ 27 00090001 1001
+ 28 00090002 1001
+ 29 00090000 1004
+ 30 00090001 1003
+ 31 00090002 1003
+ 32 000a0000 1002
+ 33 000a0001 1002
+ 34 000a0002 1002
+ 35 000a0000 1001
+ 36 000a0001 1001
+ 37 000a0002 1001
+ 38 000a0000 1004
+ 39 000a0001 1003
+ 40 000a0002 1003
+ 41 000b0000 1002
+ 42 000b0001 1002
+ 43 000b0002 1002
+ 44 000b0000 1001
+ 45 000b0001 1001
+ 46 000b0002 1001
+ 47 000b0000 1004
+ 48 000b0001 1003
+ 49 000b0002 1003
+}
+AddGroup
+
+Group.h.v=00000006
+Group.type=5201
+Group.order=5
+Group.name=translate
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000005
+Group.valA=3.00000000000000000000
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.meshCombine=1
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 80050001 0
+ 2 80050002 0
+ 3 80050003 0
+ 4 80050004 0
+ 5 80050005 0
+ 6 80050006 0
+ 7 80050007 0
+ 8 80050008 0
+ 9 80050009 0
+ 10 8005000c 0
+ 11 8005000d 0
+ 12 8005000e 0
+ 13 8005000f 0
+ 14 80050010 0
+ 15 80050011 0
+ 16 80050012 0
+ 17 80050001 1
+ 18 80050002 1
+ 19 80050003 1
+ 20 80050004 1
+ 21 80050005 1
+ 22 80050006 1
+ 23 80050007 1
+ 24 80050008 1
+ 25 80050009 1
+ 26 8005000c 1
+ 27 8005000d 1
+ 28 8005000e 1
+ 29 8005000f 1
+ 30 80050010 1
+ 31 80050011 1
+ 32 80050012 1
+ 33 80050001 1000
+ 34 80050002 1000
+ 35 80050003 1000
+ 36 80050004 1000
+ 37 80050005 1000
+ 38 80050006 1000
+ 39 80050007 1000
+ 40 80050008 1000
+ 41 80050009 1000
+ 42 8005000c 1000
+ 43 8005000d 1000
+ 44 8005000e 1000
+ 45 8005000f 1000
+ 46 80050010 1000
+ 47 80050011 1000
+ 48 80050012 1000
+ 49 80050013 0
+ 50 80050014 0
+ 51 80050015 0
+ 52 80050016 0
+ 53 80050017 0
+ 54 80050018 0
+ 55 80050019 0
+ 56 8005001a 0
+ 57 8005001b 0
+ 58 8005001c 0
+ 59 8005001d 0
+ 60 8005001e 0
+ 61 8005001f 0
+ 62 80050020 0
+ 63 80050021 0
+ 64 80050022 0
+ 65 80050023 0
+ 66 80050024 0
+ 67 80050025 0
+ 68 80050026 0
+ 69 80050027 0
+ 70 80050028 0
+ 71 80050029 0
+ 72 8005002a 0
+ 73 8005002b 0
+ 74 8005002c 0
+ 75 8005002d 0
+ 76 8005002e 0
+ 77 8005002f 0
+ 78 80050030 0
+ 79 80050031 0
+ 80 80050013 1
+ 81 80050014 1
+ 82 80050015 1
+ 83 80050016 1
+ 84 80050017 1
+ 85 80050018 1
+ 86 80050019 1
+ 87 8005001a 1
+ 88 8005001b 1
+ 89 8005001c 1
+ 90 8005001d 1
+ 91 8005001e 1
+ 92 8005001f 1
+ 93 80050020 1
+ 94 80050021 1
+ 95 80050022 1
+ 96 80050023 1
+ 97 80050024 1
+ 98 80050025 1
+ 99 80050026 1
+ 100 80050027 1
+ 101 80050028 1
+ 102 80050029 1
+ 103 8005002a 1
+ 104 8005002b 1
+ 105 8005002c 1
+ 106 8005002d 1
+ 107 8005002e 1
+ 108 8005002f 1
+ 109 80050030 1
+ 110 80050031 1
+ 111 80050013 1000
+ 112 80050014 1000
+ 113 80050015 1000
+ 114 80050016 1000
+ 115 80050017 1000
+ 116 80050018 1000
+ 117 80050019 1000
+ 118 8005001a 1000
+ 119 8005001b 1000
+ 120 8005001c 1000
+ 121 8005001d 1000
+ 122 8005001e 1000
+ 123 8005001f 1000
+ 124 80050020 1000
+ 125 80050021 1000
+ 126 80050022 1000
+ 127 80050023 1000
+ 128 80050024 1000
+ 129 80050025 1000
+ 130 80050026 1000
+ 131 80050027 1000
+ 132 80050028 1000
+ 133 80050029 1000
+ 134 8005002a 1000
+ 135 8005002b 1000
+ 136 8005002c 1000
+ 137 8005002d 1000
+ 138 8005002e 1000
+ 139 8005002f 1000
+ 140 80050030 1000
+ 141 80050031 1000
+}
+AddGroup
+
+Group.h.v=00000007
+Group.type=5201
+Group.order=6
+Group.name=translate
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000006
+Group.valA=3.00000000000000000000
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.meshCombine=1
+Group.predef.entityB.v=00010000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 80060001 0
+ 2 80060002 0
+ 3 80060003 0
+ 4 80060004 0
+ 5 80060005 0
+ 6 80060006 0
+ 7 80060007 0
+ 8 80060008 0
+ 9 80060009 0
+ 10 8006000a 0
+ 11 8006000b 0
+ 12 8006000c 0
+ 13 8006000d 0
+ 14 8006000e 0
+ 15 8006000f 0
+ 16 80060010 0
+ 17 80060011 0
+ 18 80060012 0
+ 19 80060013 0
+ 20 80060014 0
+ 21 80060015 0
+ 22 80060016 0
+ 23 80060017 0
+ 24 80060018 0
+ 25 80060019 0
+ 26 8006001a 0
+ 27 8006001b 0
+ 28 8006001c 0
+ 29 8006001d 0
+ 30 8006001e 0
+ 31 8006001f 0
+ 32 80060020 0
+ 33 80060021 0
+ 34 80060022 0
+ 35 80060023 0
+ 36 80060024 0
+ 37 80060025 0
+ 38 80060026 0
+ 39 80060027 0
+ 40 80060028 0
+ 41 80060029 0
+ 42 8006002a 0
+ 43 8006002b 0
+ 44 8006002c 0
+ 45 8006002d 0
+ 46 8006002e 0
+ 47 8006002f 0
+ 48 80060030 0
+ 49 80060001 1
+ 50 80060002 1
+ 51 80060003 1
+ 52 80060004 1
+ 53 80060005 1
+ 54 80060006 1
+ 55 80060007 1
+ 56 80060008 1
+ 57 80060009 1
+ 58 8006000a 1
+ 59 8006000b 1
+ 60 8006000c 1
+ 61 8006000d 1
+ 62 8006000e 1
+ 63 8006000f 1
+ 64 80060010 1
+ 65 80060011 1
+ 66 80060012 1
+ 67 80060013 1
+ 68 80060014 1
+ 69 80060015 1
+ 70 80060016 1
+ 71 80060017 1
+ 72 80060018 1
+ 73 80060019 1
+ 74 8006001a 1
+ 75 8006001b 1
+ 76 8006001c 1
+ 77 8006001d 1
+ 78 8006001e 1
+ 79 8006001f 1
+ 80 80060020 1
+ 81 80060021 1
+ 82 80060022 1
+ 83 80060023 1
+ 84 80060024 1
+ 85 80060025 1
+ 86 80060026 1
+ 87 80060027 1
+ 88 80060028 1
+ 89 80060029 1
+ 90 8006002a 1
+ 91 8006002b 1
+ 92 8006002c 1
+ 93 8006002d 1
+ 94 8006002e 1
+ 95 8006002f 1
+ 96 80060030 1
+ 97 80060001 1000
+ 98 80060002 1000
+ 99 80060003 1000
+ 100 80060004 1000
+ 101 80060005 1000
+ 102 80060006 1000
+ 103 80060007 1000
+ 104 80060008 1000
+ 105 80060009 1000
+ 106 8006000a 1000
+ 107 8006000b 1000
+ 108 8006000c 1000
+ 109 8006000d 1000
+ 110 8006000e 1000
+ 111 8006000f 1000
+ 112 80060010 1000
+ 113 80060011 1000
+ 114 80060012 1000
+ 115 80060013 1000
+ 116 80060014 1000
+ 117 80060015 1000
+ 118 80060016 1000
+ 119 80060017 1000
+ 120 80060018 1000
+ 121 80060019 1000
+ 122 8006001a 1000
+ 123 8006001b 1000
+ 124 8006001c 1000
+ 125 8006001d 1000
+ 126 8006001e 1000
+ 127 8006001f 1000
+ 128 80060020 1000
+ 129 80060021 1000
+ 130 80060022 1000
+ 131 80060023 1000
+ 132 80060024 1000
+ 133 80060025 1000
+ 134 80060026 1000
+ 135 80060027 1000
+ 136 80060028 1000
+ 137 80060029 1000
+ 138 8006002a 1000
+ 139 8006002b 1000
+ 140 8006002c 1000
+ 141 8006002d 1000
+ 142 8006002e 1000
+ 143 8006002f 1000
+ 144 80060030 1000
+ 145 80060031 0
+ 146 80060032 0
+ 147 80060050 0
+ 148 80060051 0
+ 149 8006006f 0
+ 150 80060070 0
+ 151 80060033 0
+ 152 80060034 0
+ 153 80060035 0
+ 154 80060036 0
+ 155 80060037 0
+ 156 80060038 0
+ 157 80060039 0
+ 158 8006003a 0
+ 159 8006003b 0
+ 160 8006003c 0
+ 161 8006003d 0
+ 162 8006003e 0
+ 163 8006003f 0
+ 164 80060040 0
+ 165 80060041 0
+ 166 80060042 0
+ 167 80060043 0
+ 168 80060044 0
+ 169 80060045 0
+ 170 80060046 0
+ 171 80060047 0
+ 172 80060048 0
+ 173 80060049 0
+ 174 8006004a 0
+ 175 8006004b 0
+ 176 8006004c 0
+ 177 8006004d 0
+ 178 8006004e 0
+ 179 8006004f 0
+ 180 80060052 0
+ 181 80060053 0
+ 182 80060054 0
+ 183 80060055 0
+ 184 80060056 0
+ 185 80060057 0
+ 186 80060058 0
+ 187 80060059 0
+ 188 8006005a 0
+ 189 8006005b 0
+ 190 8006005c 0
+ 191 8006005d 0
+ 192 8006005e 0
+ 193 8006005f 0
+ 194 80060060 0
+ 195 80060061 0
+ 196 80060062 0
+ 197 80060063 0
+ 198 80060064 0
+ 199 80060065 0
+ 200 80060066 0
+ 201 80060067 0
+ 202 80060068 0
+ 203 80060069 0
+ 204 8006006a 0
+ 205 8006006b 0
+ 206 8006006c 0
+ 207 8006006d 0
+ 208 8006006e 0
+ 209 80060071 0
+ 210 80060072 0
+ 211 80060073 0
+ 212 80060074 0
+ 213 80060075 0
+ 214 80060076 0
+ 215 80060077 0
+ 216 80060078 0
+ 217 80060079 0
+ 218 8006007a 0
+ 219 8006007b 0
+ 220 8006007c 0
+ 221 8006007d 0
+ 222 8006007e 0
+ 223 8006007f 0
+ 224 80060080 0
+ 225 80060081 0
+ 226 80060082 0
+ 227 80060083 0
+ 228 80060084 0
+ 229 80060085 0
+ 230 80060086 0
+ 231 80060087 0
+ 232 80060088 0
+ 233 80060089 0
+ 234 8006008a 0
+ 235 8006008b 0
+ 236 8006008c 0
+ 237 8006008d 0
+ 238 80060031 1
+ 239 80060032 1
+ 240 80060050 1
+ 241 80060051 1
+ 242 8006006f 1
+ 243 80060070 1
+ 244 80060033 1
+ 245 80060034 1
+ 246 80060035 1
+ 247 80060036 1
+ 248 80060037 1
+ 249 80060038 1
+ 250 80060039 1
+ 251 8006003a 1
+ 252 8006003b 1
+ 253 8006003c 1
+ 254 8006003d 1
+ 255 8006003e 1
+ 256 8006003f 1
+ 257 80060040 1
+ 258 80060041 1
+ 259 80060042 1
+ 260 80060043 1
+ 261 80060044 1
+ 262 80060045 1
+ 263 80060046 1
+ 264 80060047 1
+ 265 80060048 1
+ 266 80060049 1
+ 267 8006004a 1
+ 268 8006004b 1
+ 269 8006004c 1
+ 270 8006004d 1
+ 271 8006004e 1
+ 272 8006004f 1
+ 273 80060052 1
+ 274 80060053 1
+ 275 80060054 1
+ 276 80060055 1
+ 277 80060056 1
+ 278 80060057 1
+ 279 80060058 1
+ 280 80060059 1
+ 281 8006005a 1
+ 282 8006005b 1
+ 283 8006005c 1
+ 284 8006005d 1
+ 285 8006005e 1
+ 286 8006005f 1
+ 287 80060060 1
+ 288 80060061 1
+ 289 80060062 1
+ 290 80060063 1
+ 291 80060064 1
+ 292 80060065 1
+ 293 80060066 1
+ 294 80060067 1
+ 295 80060068 1
+ 296 80060069 1
+ 297 8006006a 1
+ 298 8006006b 1
+ 299 8006006c 1
+ 300 8006006d 1
+ 301 8006006e 1
+ 302 80060071 1
+ 303 80060072 1
+ 304 80060073 1
+ 305 80060074 1
+ 306 80060075 1
+ 307 80060076 1
+ 308 80060077 1
+ 309 80060078 1
+ 310 80060079 1
+ 311 8006007a 1
+ 312 8006007b 1
+ 313 8006007c 1
+ 314 8006007d 1
+ 315 8006007e 1
+ 316 8006007f 1
+ 317 80060080 1
+ 318 80060081 1
+ 319 80060082 1
+ 320 80060083 1
+ 321 80060084 1
+ 322 80060085 1
+ 323 80060086 1
+ 324 80060087 1
+ 325 80060088 1
+ 326 80060089 1
+ 327 8006008a 1
+ 328 8006008b 1
+ 329 8006008c 1
+ 330 8006008d 1
+ 331 80060031 1000
+ 332 80060032 1000
+ 333 80060050 1000
+ 334 80060051 1000
+ 335 8006006f 1000
+ 336 80060070 1000
+ 337 80060033 1000
+ 338 80060034 1000
+ 339 80060035 1000
+ 340 80060036 1000
+ 341 80060037 1000
+ 342 80060038 1000
+ 343 80060039 1000
+ 344 8006003a 1000
+ 345 8006003b 1000
+ 346 8006003c 1000
+ 347 8006003d 1000
+ 348 8006003e 1000
+ 349 8006003f 1000
+ 350 80060040 1000
+ 351 80060041 1000
+ 352 80060042 1000
+ 353 80060043 1000
+ 354 80060044 1000
+ 355 80060045 1000
+ 356 80060046 1000
+ 357 80060047 1000
+ 358 80060048 1000
+ 359 80060049 1000
+ 360 8006004a 1000
+ 361 8006004b 1000
+ 362 8006004c 1000
+ 363 8006004d 1000
+ 364 8006004e 1000
+ 365 8006004f 1000
+ 366 80060052 1000
+ 367 80060053 1000
+ 368 80060054 1000
+ 369 80060055 1000
+ 370 80060056 1000
+ 371 80060057 1000
+ 372 80060058 1000
+ 373 80060059 1000
+ 374 8006005a 1000
+ 375 8006005b 1000
+ 376 8006005c 1000
+ 377 8006005d 1000
+ 378 8006005e 1000
+ 379 8006005f 1000
+ 380 80060060 1000
+ 381 80060061 1000
+ 382 80060062 1000
+ 383 80060063 1000
+ 384 80060064 1000
+ 385 80060065 1000
+ 386 80060066 1000
+ 387 80060067 1000
+ 388 80060068 1000
+ 389 80060069 1000
+ 390 8006006a 1000
+ 391 8006006b 1000
+ 392 8006006c 1000
+ 393 8006006d 1000
+ 394 8006006e 1000
+ 395 80060071 1000
+ 396 80060072 1000
+ 397 80060073 1000
+ 398 80060074 1000
+ 399 80060075 1000
+ 400 80060076 1000
+ 401 80060077 1000
+ 402 80060078 1000
+ 403 80060079 1000
+ 404 8006007a 1000
+ 405 8006007b 1000
+ 406 8006007c 1000
+ 407 8006007d 1000
+ 408 8006007e 1000
+ 409 8006007f 1000
+ 410 80060080 1000
+ 411 80060081 1000
+ 412 80060082 1000
+ 413 80060083 1000
+ 414 80060084 1000
+ 415 80060085 1000
+ 416 80060086 1000
+ 417 80060087 1000
+ 418 80060088 1000
+ 419 80060089 1000
+ 420 8006008a 1000
+ 421 8006008b 1000
+ 422 8006008c 1000
+ 423 8006008d 1000
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=20.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=-20.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=20.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=-20.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=20.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=-20.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=-20.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=20.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00070013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00070014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00080010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00080011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00080013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00080014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00090010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00090011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00090013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00090014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=000a0010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=000a0011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=000a0013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=000a0014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=000b0010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=000b0011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=000b0013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=000b0014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=80030000
+AddParam
+
+Param.h.v.=80030001
+AddParam
+
+Param.h.v.=80030002
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=80050000
+AddParam
+
+Param.h.v.=80050001
+AddParam
+
+Param.h.v.=80050002
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=80060000
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80060001
+AddParam
+
+Param.h.v.=80060002
+AddParam
+
+Param.h.v.=80070000
+AddParam
+
+Param.h.v.=80070001
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=80070002
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000008
+Request.type=200
+Request.workplane.v=80040000
+Request.group.v=00000004
+Request.construction=0
+AddRequest
+
+Request.h.v=00000009
+Request.type=200
+Request.workplane.v=80040000
+Request.group.v=00000004
+Request.construction=0
+AddRequest
+
+Request.h.v=0000000a
+Request.type=200
+Request.workplane.v=80040000
+Request.group.v=00000004
+Request.construction=0
+AddRequest
+
+Request.h.v=0000000b
+Request.type=200
+Request.workplane.v=80040000
+Request.group.v=00000004
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00070001
+Entity.point[1].v=00070002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00080000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00080001
+Entity.point[1].v=00080002
+Entity.workplane.v=80040000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00080001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00080002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00090000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00090001
+Entity.point[1].v=00090002
+Entity.workplane.v=80040000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00090001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00090002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=000a0000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=000a0001
+Entity.point[1].v=000a0002
+Entity.workplane.v=80040000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=000a0001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=000a0002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=000b0000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=000b0001
+Entity.point[1].v=000b0002
+Entity.workplane.v=80040000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=000b0001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=000b0002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030001
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.point[1].v=80030003
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030004
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030005
+Entity.point[1].v=80030006
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030007
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030005
+Entity.point[1].v=80030002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.point[1].v=80030003
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000b
+Entity.point[1].v=8003000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000e
+Entity.point[1].v=8003000f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000e
+Entity.point[1].v=8003000b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.point[1].v=8003000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030013
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030014
+Entity.point[1].v=80030015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030014
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030015
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030016
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030017
+Entity.point[1].v=80030018
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030017
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030018
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030019
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030017
+Entity.point[1].v=80030014
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030018
+Entity.point[1].v=80030015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003001d
+Entity.point[1].v=8003001e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030020
+Entity.point[1].v=80030021
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030020
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030021
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030022
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030023
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030020
+Entity.point[1].v=8003001d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030024
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030021
+Entity.point[1].v=8003001e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030027
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030028
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030028
+Entity.type=2010
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030029
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003002a
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002a
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002b
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8003002a
+Entity.point[1].v=80030028
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002c
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003002a
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002d
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=80030028
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80040002
+Entity.normal.v=80040001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80040001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80040002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050001
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050002
+Entity.point[1].v=80050013
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050002
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050005
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050006
+Entity.point[1].v=80050014
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050006
+Entity.point[1].v=80050002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8005000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005000d
+Entity.type=2010
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005000e
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8005000f
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005000f
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050010
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8005000f
+Entity.point[1].v=8005000d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050011
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8005000f
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050012
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8005000d
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050013
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050014
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050015
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050016
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050014
+Entity.point[1].v=80050013
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050017
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050018
+Entity.point[1].v=80050019
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050018
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050019
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005001a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005001b
+Entity.point[1].v=8005001c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005001b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005001c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005001d
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005001e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005001b
+Entity.point[1].v=80050018
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005001f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005001c
+Entity.point[1].v=80050019
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050020
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050021
+Entity.point[1].v=80050022
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050021
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050022
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050023
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050024
+Entity.point[1].v=80050025
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050024
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050025
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050026
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050027
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050024
+Entity.point[1].v=80050021
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050028
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050025
+Entity.point[1].v=80050022
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050029
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005002a
+Entity.point[1].v=8005002b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005002a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005002b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005002c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005002d
+Entity.point[1].v=8005002e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005002d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005002e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005002f
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050030
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005002d
+Entity.point[1].v=8005002a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050031
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005002e
+Entity.point[1].v=8005002b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060001
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060002
+Entity.point[1].v=80060031
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060002
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060005
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060006
+Entity.point[1].v=80060032
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060006
+Entity.point[1].v=80060002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006000a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8006000b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006000b
+Entity.type=2010
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8006000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006000d
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006000e
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8006000d
+Entity.point[1].v=8006000b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006000f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060010
+Entity.type=5003
+Entity.construction=0
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060011
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060012
+Entity.point[1].v=80060050
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060012
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060015
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060016
+Entity.point[1].v=80060051
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060016
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060019
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060016
+Entity.point[1].v=80060012
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006001a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8006001b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006001b
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006001c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8006001d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006001d
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006001e
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8006001d
+Entity.point[1].v=8006001b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006001f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060020
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060021
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060022
+Entity.point[1].v=8006006f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060022
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060025
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060026
+Entity.point[1].v=80060070
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060026
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060029
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060026
+Entity.point[1].v=80060022
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006002a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8006002b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006002b
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006002c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8006002d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006002d
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006002e
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8006002d
+Entity.point[1].v=8006002b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006002f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060030
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060031
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060032
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060033
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060034
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060032
+Entity.point[1].v=80060031
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060035
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060036
+Entity.point[1].v=80060037
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060036
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060037
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060038
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060039
+Entity.point[1].v=8006003a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060039
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006003a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006003b
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006003c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060039
+Entity.point[1].v=80060036
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006003d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006003a
+Entity.point[1].v=80060037
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006003e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006003f
+Entity.point[1].v=80060040
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006003f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060040
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060041
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060042
+Entity.point[1].v=80060043
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060042
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060043
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060044
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060045
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060042
+Entity.point[1].v=8006003f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060046
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060043
+Entity.point[1].v=80060040
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060047
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060048
+Entity.point[1].v=80060049
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060048
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060049
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006004a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006004b
+Entity.point[1].v=8006004c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006004b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006004c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006004d
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006004e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006004b
+Entity.point[1].v=80060048
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006004f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006004c
+Entity.point[1].v=80060049
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060050
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060051
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060052
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060053
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060051
+Entity.point[1].v=80060050
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060054
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060055
+Entity.point[1].v=80060056
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060055
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060056
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060057
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060058
+Entity.point[1].v=80060059
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060058
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060059
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006005a
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006005b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060058
+Entity.point[1].v=80060055
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006005c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060059
+Entity.point[1].v=80060056
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006005d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006005e
+Entity.point[1].v=8006005f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006005e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006005f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060060
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060061
+Entity.point[1].v=80060062
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060061
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060062
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060063
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060064
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060061
+Entity.point[1].v=8006005e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060065
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060062
+Entity.point[1].v=8006005f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060066
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060067
+Entity.point[1].v=80060068
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060067
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060068
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060069
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006006a
+Entity.point[1].v=8006006b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006006a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006006b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006006c
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006006d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006006a
+Entity.point[1].v=80060067
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006006e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006006b
+Entity.point[1].v=80060068
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006006f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060070
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060071
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060072
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060070
+Entity.point[1].v=8006006f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060073
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060074
+Entity.point[1].v=80060075
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060074
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060075
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060076
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060077
+Entity.point[1].v=80060078
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060077
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060078
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060079
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006007a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060077
+Entity.point[1].v=80060074
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006007b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060078
+Entity.point[1].v=80060075
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006007c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006007d
+Entity.point[1].v=8006007e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006007d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006007e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006007f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060080
+Entity.point[1].v=80060081
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060080
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060081
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060082
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060083
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060080
+Entity.point[1].v=8006007d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060084
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060081
+Entity.point[1].v=8006007e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060085
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060086
+Entity.point[1].v=80060087
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060086
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060087
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060088
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060089
+Entity.point[1].v=8006008a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060089
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006008a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006008b
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006008c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060089
+Entity.point[1].v=80060086
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006008d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006008a
+Entity.point[1].v=80060087
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070001
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070002
+Entity.point[1].v=80070091
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070002
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070005
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070006
+Entity.point[1].v=80070092
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070006
+Entity.point[1].v=80070002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007000a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007000b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007000b
+Entity.type=2010
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007000d
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007000e
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8007000d
+Entity.point[1].v=8007000b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007000f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070010
+Entity.type=5003
+Entity.construction=0
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070011
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070012
+Entity.point[1].v=80070093
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070012
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070015
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070016
+Entity.point[1].v=80070094
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070016
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070019
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070016
+Entity.point[1].v=80070012
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007001a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007001b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007001b
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007001c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007001d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007001d
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007001e
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8007001d
+Entity.point[1].v=8007001b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007001f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070020
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070021
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070022
+Entity.point[1].v=80070095
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070022
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070025
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070026
+Entity.point[1].v=80070096
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070026
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070029
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070026
+Entity.point[1].v=80070022
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007002a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007002b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007002b
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007002c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007002d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007002d
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007002e
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8007002d
+Entity.point[1].v=8007002b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007002f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070030
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070031
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070032
+Entity.point[1].v=800700ee
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070032
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070035
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070036
+Entity.point[1].v=800700ef
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070036
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070039
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070036
+Entity.point[1].v=80070032
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007003a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007003b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007003b
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007003c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007003d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007003d
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007003e
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8007003d
+Entity.point[1].v=8007003b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007003f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070040
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070041
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070042
+Entity.point[1].v=800700f0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070042
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070045
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070046
+Entity.point[1].v=800700f1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070046
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070049
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070046
+Entity.point[1].v=80070042
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007004a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007004b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007004b
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007004c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007004d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007004d
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007004e
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8007004d
+Entity.point[1].v=8007004b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007004f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070050
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070051
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070052
+Entity.point[1].v=800700f2
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070052
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070055
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070056
+Entity.point[1].v=800700f3
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070056
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070059
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070056
+Entity.point[1].v=80070052
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007005a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007005b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007005b
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007005c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007005d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007005d
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007005e
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8007005d
+Entity.point[1].v=8007005b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007005f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070060
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070061
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070062
+Entity.point[1].v=8007014b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070062
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070065
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070066
+Entity.point[1].v=8007014c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070066
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070069
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070066
+Entity.point[1].v=80070062
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007006a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007006b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007006b
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007006c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007006d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007006d
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007006e
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8007006d
+Entity.point[1].v=8007006b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007006f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070070
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070071
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070072
+Entity.point[1].v=8007014d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070072
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070075
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070076
+Entity.point[1].v=8007014e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070076
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070079
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070076
+Entity.point[1].v=80070072
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007007a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007007b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007007b
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007007c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007007d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007007d
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007007e
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8007007d
+Entity.point[1].v=8007007b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007007f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070080
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070081
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070082
+Entity.point[1].v=8007014f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070082
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070085
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070086
+Entity.point[1].v=80070150
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070086
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070089
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070086
+Entity.point[1].v=80070082
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007008a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007008b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007008b
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007008c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007008d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007008d
+Entity.type=2010
+Entity.construction=1
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007008e
+Entity.type=11000
+Entity.construction=1
+Entity.point[0].v=8007008d
+Entity.point[1].v=8007008b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007008f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070090
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070091
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070092
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070093
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070094
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070095
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070096
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070097
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070098
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070092
+Entity.point[1].v=80070091
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070099
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007009a
+Entity.point[1].v=8007009b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007009a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007009b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007009c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007009d
+Entity.point[1].v=8007009e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007009d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007009e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007009f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a0
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007009d
+Entity.point[1].v=8007009a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a1
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007009e
+Entity.point[1].v=8007009b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a2
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700a3
+Entity.point[1].v=800700a4
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a3
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a4
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a5
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700a6
+Entity.point[1].v=800700a7
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a6
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a7
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a8
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a9
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700a6
+Entity.point[1].v=800700a3
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700aa
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700a7
+Entity.point[1].v=800700a4
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ab
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700ac
+Entity.point[1].v=800700ad
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ac
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ad
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ae
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700af
+Entity.point[1].v=800700b0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700af
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b0
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b1
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b2
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700af
+Entity.point[1].v=800700ac
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b3
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700b0
+Entity.point[1].v=800700ad
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b4
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b5
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070094
+Entity.point[1].v=80070093
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b6
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700b7
+Entity.point[1].v=800700b8
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b7
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b8
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b9
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700ba
+Entity.point[1].v=800700bb
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ba
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700bb
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700bc
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700bd
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700ba
+Entity.point[1].v=800700b7
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700be
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700bb
+Entity.point[1].v=800700b8
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700bf
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700c0
+Entity.point[1].v=800700c1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c0
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c1
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c2
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700c3
+Entity.point[1].v=800700c4
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c3
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c4
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c5
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c6
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700c3
+Entity.point[1].v=800700c0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c7
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700c4
+Entity.point[1].v=800700c1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c8
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700c9
+Entity.point[1].v=800700ca
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c9
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ca
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700cb
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700cc
+Entity.point[1].v=800700cd
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700cc
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700cd
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ce
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700cf
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700cc
+Entity.point[1].v=800700c9
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d0
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700cd
+Entity.point[1].v=800700ca
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d1
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d2
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070096
+Entity.point[1].v=80070095
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d3
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700d4
+Entity.point[1].v=800700d5
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d4
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d5
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d6
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700d7
+Entity.point[1].v=800700d8
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d7
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d8
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d9
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700da
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700d7
+Entity.point[1].v=800700d4
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700db
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700d8
+Entity.point[1].v=800700d5
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700dc
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700dd
+Entity.point[1].v=800700de
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700dd
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700de
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700df
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700e0
+Entity.point[1].v=800700e1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e0
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e1
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e2
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e3
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700e0
+Entity.point[1].v=800700dd
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e4
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700e1
+Entity.point[1].v=800700de
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e5
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700e6
+Entity.point[1].v=800700e7
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e6
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e7
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e8
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700e9
+Entity.point[1].v=800700ea
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e9
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ea
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700eb
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ec
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700e9
+Entity.point[1].v=800700e6
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ed
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700ea
+Entity.point[1].v=800700e7
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ee
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ef
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f0
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f1
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f2
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f3
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f4
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f5
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700ef
+Entity.point[1].v=800700ee
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f6
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700f7
+Entity.point[1].v=800700f8
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f7
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f8
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f9
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700fa
+Entity.point[1].v=800700fb
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700fa
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700fb
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700fc
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700fd
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700fa
+Entity.point[1].v=800700f7
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700fe
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700fb
+Entity.point[1].v=800700f8
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ff
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070100
+Entity.point[1].v=80070101
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070100
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070101
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070102
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070103
+Entity.point[1].v=80070104
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070103
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070104
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070105
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070106
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070103
+Entity.point[1].v=80070100
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070107
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070104
+Entity.point[1].v=80070101
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070108
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070109
+Entity.point[1].v=8007010a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070109
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007010a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007010b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007010c
+Entity.point[1].v=8007010d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007010c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007010d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007010e
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007010f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007010c
+Entity.point[1].v=80070109
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070110
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007010d
+Entity.point[1].v=8007010a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070111
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070112
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700f1
+Entity.point[1].v=800700f0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070113
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070114
+Entity.point[1].v=80070115
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070114
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070115
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070116
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070117
+Entity.point[1].v=80070118
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070117
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070118
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070119
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007011a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070117
+Entity.point[1].v=80070114
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007011b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070118
+Entity.point[1].v=80070115
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007011c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007011d
+Entity.point[1].v=8007011e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007011d
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007011e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007011f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070120
+Entity.point[1].v=80070121
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070120
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070121
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070122
+Entity.type=5003
+Entity.construction=0
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070123
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070120
+Entity.point[1].v=8007011d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070124
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070121
+Entity.point[1].v=8007011e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070125
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070126
+Entity.point[1].v=80070127
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070126
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070127
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070128
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070129
+Entity.point[1].v=8007012a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070129
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007012a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007012b
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007012c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070129
+Entity.point[1].v=80070126
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007012d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007012a
+Entity.point[1].v=80070127
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007012e
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007012f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700f3
+Entity.point[1].v=800700f2
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070130
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070131
+Entity.point[1].v=80070132
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070131
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070132
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070133
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070134
+Entity.point[1].v=80070135
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070134
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070135
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070136
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070137
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070134
+Entity.point[1].v=80070131
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070138
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070135
+Entity.point[1].v=80070132
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070139
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007013a
+Entity.point[1].v=8007013b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007013a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007013b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007013c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007013d
+Entity.point[1].v=8007013e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007013d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007013e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007013f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070140
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007013d
+Entity.point[1].v=8007013a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070141
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007013e
+Entity.point[1].v=8007013b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070142
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070143
+Entity.point[1].v=80070144
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070143
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070144
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070145
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070146
+Entity.point[1].v=80070147
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070146
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070147
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070148
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070149
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070146
+Entity.point[1].v=80070143
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007014a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070147
+Entity.point[1].v=80070144
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007014b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007014c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007014d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007014e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007014f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070150
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070151
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070152
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007014c
+Entity.point[1].v=8007014b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070153
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070154
+Entity.point[1].v=80070155
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070154
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070155
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070156
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070157
+Entity.point[1].v=80070158
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070157
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070158
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070159
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007015a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070157
+Entity.point[1].v=80070154
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007015b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070158
+Entity.point[1].v=80070155
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007015c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007015d
+Entity.point[1].v=8007015e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007015d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007015e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007015f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070160
+Entity.point[1].v=80070161
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070160
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070161
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070162
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070163
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070160
+Entity.point[1].v=8007015d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070164
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070161
+Entity.point[1].v=8007015e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070165
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070166
+Entity.point[1].v=80070167
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070166
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070167
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070168
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070169
+Entity.point[1].v=8007016a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070169
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007016a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007016b
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007016c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070169
+Entity.point[1].v=80070166
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007016d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007016a
+Entity.point[1].v=80070167
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007016e
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007016f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007014e
+Entity.point[1].v=8007014d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070170
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070171
+Entity.point[1].v=80070172
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070171
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070172
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070173
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070174
+Entity.point[1].v=80070175
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070174
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070175
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070176
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070177
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070174
+Entity.point[1].v=80070171
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070178
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070175
+Entity.point[1].v=80070172
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070179
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007017a
+Entity.point[1].v=8007017b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007017a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007017b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007017c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007017d
+Entity.point[1].v=8007017e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007017d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007017e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007017f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070180
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007017d
+Entity.point[1].v=8007017a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070181
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007017e
+Entity.point[1].v=8007017b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070182
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070183
+Entity.point[1].v=80070184
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070183
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070184
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070185
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070186
+Entity.point[1].v=80070187
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070186
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070187
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070188
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070189
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070186
+Entity.point[1].v=80070183
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007018a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070187
+Entity.point[1].v=80070184
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007018b
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007018c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070150
+Entity.point[1].v=8007014f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007018d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007018e
+Entity.point[1].v=8007018f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007018e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007018f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070190
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070191
+Entity.point[1].v=80070192
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070191
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070192
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070193
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070194
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070191
+Entity.point[1].v=8007018e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070195
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070192
+Entity.point[1].v=8007018f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070196
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070197
+Entity.point[1].v=80070198
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070197
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070198
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070199
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007019a
+Entity.point[1].v=8007019b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007019a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007019b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007019c
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007019d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007019a
+Entity.point[1].v=80070197
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007019e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007019b
+Entity.point[1].v=80070198
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007019f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800701a0
+Entity.point[1].v=800701a1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a0
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a1
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a2
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800701a3
+Entity.point[1].v=800701a4
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a3
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a4
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a5
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a6
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800701a3
+Entity.point[1].v=800701a0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a7
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800701a4
+Entity.point[1].v=800701a1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040001
+Constraint.ptB.v=00050002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050001
+Constraint.ptB.v=00060002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00060001
+Constraint.ptB.v=00070002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00070001
+Constraint.ptB.v=00040002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000005
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000006
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000007
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000008
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00070000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=0000000a
+Constraint.type=30
+Constraint.group.v=00000003
+Constraint.valA=1.00000000000000000000
+Constraint.ptA.v=80030018
+Constraint.ptB.v=80030015
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=-1.11433618504280240735
+Constraint.disp.offset.y=-1.07366998595325924271
+AddConstraint
+
+Constraint.h.v=0000000b
+Constraint.type=20
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.ptA.v=00080001
+Constraint.ptB.v=00090002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=0000000c
+Constraint.type=20
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.ptA.v=00090001
+Constraint.ptB.v=000a0002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=0000000d
+Constraint.type=20
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.ptA.v=000a0001
+Constraint.ptB.v=000b0002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=0000000e
+Constraint.type=20
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.ptA.v=000b0001
+Constraint.ptB.v=00080002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=0000000f
+Constraint.type=81
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.entityA.v=00080000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000010
+Constraint.type=80
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.entityA.v=00090000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000011
+Constraint.type=81
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.entityA.v=000a0000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000012
+Constraint.type=80
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.entityA.v=000b0000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Surface 00000001 00646464 8003002c 1 1
+SCtrl 0 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 20.00000000010000178463 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -15.00000000000000000000 -20.00000000010000178463 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 20.00000000010000178463 -20.00000000010000178463 1.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000004 0 -15.00000000000000000000 -20.00000000010000178463 1.00000000000000000000 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+TrimBy 0000000a 0 20.00000000010000178463 15.00000000000000000000 1.00000000000000000000 20.00000000010000178463 -20.00000000010000178463 1.00000000000000000000
+TrimBy 00000001 0 20.00000000010000178463 -20.00000000010000178463 1.00000000000000000000 -15.00000000000000000000 -20.00000000010000178463 1.00000000000000000000
+TrimBy 00000007 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 20.00000000010000178463 15.00000000000000000000 1.00000000000000000000
+TrimBy 0000007c 0 -5.00000000000000088818 5.00000000000000088818 1.00000000000000000000 -5.00000000000000177636 10.00000000000000177636 1.00000000000000000000
+TrimBy 00000079 0 -10.00000000000000177636 5.00000000000000266454 1.00000000000000000000 -5.00000000000000088818 5.00000000000000088818 1.00000000000000000000
+TrimBy 0000007a 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 -10.00000000000000177636 5.00000000000000266454 1.00000000000000000000
+TrimBy 0000007b 0 -5.00000000000000177636 10.00000000000000177636 1.00000000000000000000 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000080 0 4.99999999999999644729 5.00000000000000088818 1.00000000000000000000 4.99999999999999822364 10.00000000000000000000 1.00000000000000000000
+TrimBy 0000007d 0 -0.00000000000000133227 5.00000000000000088818 1.00000000000000000000 4.99999999999999644729 5.00000000000000088818 1.00000000000000000000
+TrimBy 0000007e 0 -0.00000000000000111022 10.00000000000000000000 1.00000000000000000000 -0.00000000000000133227 5.00000000000000088818 1.00000000000000000000
+TrimBy 0000007f 0 4.99999999999999822364 10.00000000000000000000 1.00000000000000000000 -0.00000000000000111022 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000084 0 14.99999999999999822364 5.00000000000000000000 1.00000000000000000000 14.99999999999999644729 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000081 0 10.00000000000000000000 5.00000000000000177636 1.00000000000000000000 14.99999999999999822364 5.00000000000000000000 1.00000000000000000000
+TrimBy 00000082 0 10.00000000000000177636 10.00000000000000000000 1.00000000000000000000 10.00000000000000000000 5.00000000000000177636 1.00000000000000000000
+TrimBy 00000083 0 14.99999999999999644729 10.00000000000000000000 1.00000000000000000000 10.00000000000000177636 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000088 0 -5.00000000000000088818 -4.99999999999999644729 1.00000000000000000000 -5.00000000000000088818 0.00000000000000177636 1.00000000000000000000
+TrimBy 00000085 0 -10.00000000000000177636 -4.99999999999999911182 1.00000000000000000000 -5.00000000000000088818 -4.99999999999999644729 1.00000000000000000000
+TrimBy 00000086 0 -10.00000000000000000000 0.00000000000000044409 1.00000000000000000000 -10.00000000000000177636 -4.99999999999999911182 1.00000000000000000000
+TrimBy 00000087 0 -5.00000000000000088818 0.00000000000000177636 1.00000000000000000000 -10.00000000000000000000 0.00000000000000044409 1.00000000000000000000
+TrimBy 0000008c 0 4.99999999999999822364 -4.99999999999999822364 1.00000000000000000000 4.99999999999999911182 0.00000000000000177636 1.00000000000000000000
+TrimBy 00000089 0 -0.00000000000000177636 -4.99999999999999911182 1.00000000000000000000 4.99999999999999822364 -4.99999999999999822364 1.00000000000000000000
+TrimBy 0000008a 0 -0.00000000000000088818 0.00000000000000177636 1.00000000000000000000 -0.00000000000000177636 -4.99999999999999911182 1.00000000000000000000
+TrimBy 0000008b 0 4.99999999999999911182 0.00000000000000177636 1.00000000000000000000 -0.00000000000000088818 0.00000000000000177636 1.00000000000000000000
+TrimBy 00000090 0 14.99999999999999644729 -4.99999999999999822364 1.00000000000000000000 15.00000000000000000000 0.00000000000000266454 1.00000000000000000000
+TrimBy 0000008d 0 10.00000000000000177636 -4.99999999999999822364 1.00000000000000000000 14.99999999999999644729 -4.99999999999999822364 1.00000000000000000000
+TrimBy 0000008e 0 10.00000000000000177636 0.00000000000000266454 1.00000000000000000000 10.00000000000000177636 -4.99999999999999822364 1.00000000000000000000
+TrimBy 0000008f 0 15.00000000000000000000 0.00000000000000266454 1.00000000000000000000 10.00000000000000177636 0.00000000000000266454 1.00000000000000000000
+TrimBy 00000094 0 -5.00000000000000000000 -14.99999999999999822364 1.00000000000000000000 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000091 0 -10.00000000000000000000 -14.99999999999999644729 1.00000000000000000000 -5.00000000000000000000 -14.99999999999999822364 1.00000000000000000000
+TrimBy 00000092 0 -10.00000000000000000000 -10.00000000000000177636 1.00000000000000000000 -10.00000000000000000000 -14.99999999999999644729 1.00000000000000000000
+TrimBy 00000093 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -10.00000000000000177636 1.00000000000000000000
+TrimBy 00000098 0 4.99999999999999911182 -14.99999999999999644729 1.00000000000000000000 4.99999999999999822364 -10.00000000000000177636 1.00000000000000000000
+TrimBy 00000095 0 -0.00000000000000177636 -14.99999999999999822364 1.00000000000000000000 4.99999999999999911182 -14.99999999999999644729 1.00000000000000000000
+TrimBy 00000096 0 -0.00000000000000266454 -10.00000000000000000000 1.00000000000000000000 -0.00000000000000177636 -14.99999999999999822364 1.00000000000000000000
+TrimBy 00000097 0 4.99999999999999822364 -10.00000000000000177636 1.00000000000000000000 -0.00000000000000266454 -10.00000000000000000000 1.00000000000000000000
+TrimBy 0000009c 0 14.99999999999999644729 -14.99999999999999644729 1.00000000000000000000 14.99999999999999644729 -10.00000000000000177636 1.00000000000000000000
+TrimBy 00000099 0 10.00000000000000177636 -14.99999999999999644729 1.00000000000000000000 14.99999999999999644729 -14.99999999999999644729 1.00000000000000000000
+TrimBy 0000009a 0 10.00000000000000355271 -10.00000000000000355271 1.00000000000000000000 10.00000000000000177636 -14.99999999999999644729 1.00000000000000000000
+TrimBy 0000009b 0 14.99999999999999644729 -10.00000000000000177636 1.00000000000000000000 10.00000000000000355271 -10.00000000000000355271 1.00000000000000000000
+AddSurface
+Surface 00000002 00646464 8003002d 1 1
+SCtrl 0 0 20.00000000010000178463 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 20.00000000010000178463 -20.00000000010000178463 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -15.00000000000000000000 -20.00000000010000178463 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000008 1 20.00000000010000178463 15.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+TrimBy 0000000b 1 20.00000000010000178463 -20.00000000010000178463 0.00000000000000000000 20.00000000010000178463 15.00000000000000000000 0.00000000000000000000
+TrimBy 00000002 1 -15.00000000000000000000 -20.00000000010000178463 0.00000000000000000000 20.00000000010000178463 -20.00000000010000178463 0.00000000000000000000
+TrimBy 00000005 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 -20.00000000010000178463 0.00000000000000000000
+TrimBy 000000a0 0 -5.00000000000000177636 10.00000000000000177636 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000009e 0 -10.00000000000000177636 5.00000000000000266454 0.00000000000000000000 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 0000009f 0 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 -5.00000000000000177636 10.00000000000000177636 0.00000000000000000000
+TrimBy 0000009d 0 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -10.00000000000000177636 5.00000000000000266454 0.00000000000000000000
+TrimBy 000000a4 0 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 4.99999999999999822364 5.00000000000000000000 0.00000000000000000000
+TrimBy 000000a2 0 0.00000000000000133227 5.00000000000000000000 0.00000000000000000000 0.00000000000000377476 10.00000000000000000000 0.00000000000000000000
+TrimBy 000000a3 0 0.00000000000000377476 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 000000a1 0 4.99999999999999822364 5.00000000000000000000 0.00000000000000000000 0.00000000000000133227 5.00000000000000000000 0.00000000000000000000
+TrimBy 000000a8 0 14.99999999999999822364 10.00000000000000000000 0.00000000000000000000 15.00000000000000000000 4.99999999999999911182 0.00000000000000000000
+TrimBy 000000a6 0 10.00000000000000000000 5.00000000000000088818 0.00000000000000000000 9.99999999999999822364 10.00000000000000177636 0.00000000000000000000
+TrimBy 000000a7 0 9.99999999999999822364 10.00000000000000177636 0.00000000000000000000 14.99999999999999822364 10.00000000000000000000 0.00000000000000000000
+TrimBy 000000a5 0 15.00000000000000000000 4.99999999999999911182 0.00000000000000000000 10.00000000000000000000 5.00000000000000088818 0.00000000000000000000
+TrimBy 000000ac 0 -5.00000000000000000000 0.00000000000000177636 0.00000000000000000000 -5.00000000000000088818 -4.99999999999999644729 0.00000000000000000000
+TrimBy 000000aa 0 -10.00000000000000000000 -4.99999999999999822364 0.00000000000000000000 -10.00000000000000000000 0.00000000000000088818 0.00000000000000000000
+TrimBy 000000ab 0 -10.00000000000000000000 0.00000000000000088818 0.00000000000000000000 -5.00000000000000000000 0.00000000000000177636 0.00000000000000000000
+TrimBy 000000a9 0 -5.00000000000000088818 -4.99999999999999644729 0.00000000000000000000 -10.00000000000000000000 -4.99999999999999822364 0.00000000000000000000
+TrimBy 000000b0 0 5.00000000000000000000 0.00000000000000266454 0.00000000000000000000 4.99999999999999911182 -4.99999999999999733546 0.00000000000000000000
+TrimBy 000000ae 0 0.00000000000000177636 -4.99999999999999822364 0.00000000000000000000 0.00000000000000266454 0.00000000000000266454 0.00000000000000000000
+TrimBy 000000af 0 0.00000000000000266454 0.00000000000000266454 0.00000000000000000000 5.00000000000000000000 0.00000000000000266454 0.00000000000000000000
+TrimBy 000000ad 0 4.99999999999999911182 -4.99999999999999733546 0.00000000000000000000 0.00000000000000177636 -4.99999999999999822364 0.00000000000000000000
+TrimBy 000000b4 0 15.00000000000000000000 0.00000000000000155431 0.00000000000000000000 15.00000000000000355271 -4.99999999999999911182 0.00000000000000000000
+TrimBy 000000b2 0 10.00000000000000000000 -4.99999999999999911182 0.00000000000000000000 10.00000000000000000000 0.00000000000000266454 0.00000000000000000000
+TrimBy 000000b3 0 10.00000000000000000000 0.00000000000000266454 0.00000000000000000000 15.00000000000000000000 0.00000000000000155431 0.00000000000000000000
+TrimBy 000000b1 0 15.00000000000000355271 -4.99999999999999911182 0.00000000000000000000 10.00000000000000000000 -4.99999999999999911182 0.00000000000000000000
+TrimBy 000000b8 0 -5.00000000000000088818 -10.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -14.99999999999999822364 0.00000000000000000000
+TrimBy 000000b6 0 -10.00000000000000177636 -14.99999999999999644729 0.00000000000000000000 -10.00000000000000000000 -10.00000000000000177636 0.00000000000000000000
+TrimBy 000000b7 0 -10.00000000000000000000 -10.00000000000000177636 0.00000000000000000000 -5.00000000000000088818 -10.00000000000000000000 0.00000000000000000000
+TrimBy 000000b5 0 -5.00000000000000000000 -14.99999999999999822364 0.00000000000000000000 -10.00000000000000177636 -14.99999999999999644729 0.00000000000000000000
+TrimBy 000000bc 0 5.00000000000000000000 -10.00000000000000177636 0.00000000000000000000 5.00000000000000088818 -15.00000000000000000000 0.00000000000000000000
+TrimBy 000000ba 0 0.00000000000000177636 -14.99999999999999644729 0.00000000000000000000 0.00000000000000355271 -10.00000000000000355271 0.00000000000000000000
+TrimBy 000000bb 0 0.00000000000000355271 -10.00000000000000355271 0.00000000000000000000 5.00000000000000000000 -10.00000000000000177636 0.00000000000000000000
+TrimBy 000000b9 0 5.00000000000000088818 -15.00000000000000000000 0.00000000000000000000 0.00000000000000177636 -14.99999999999999644729 0.00000000000000000000
+TrimBy 000000c0 0 15.00000000000000000000 -10.00000000000000177636 0.00000000000000000000 14.99999999999999822364 -14.99999999999999822364 0.00000000000000000000
+TrimBy 000000be 0 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 9.99999999999999822364 -10.00000000000000177636 0.00000000000000000000
+TrimBy 000000bf 0 9.99999999999999822364 -10.00000000000000177636 0.00000000000000000000 15.00000000000000000000 -10.00000000000000177636 0.00000000000000000000
+TrimBy 000000bd 0 14.99999999999999822364 -14.99999999999999822364 0.00000000000000000000 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000003 00646464 80030010 1 1
+SCtrl 0 0 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000001 1 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+TrimBy 00000002 0 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+TrimBy 00000003 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+TrimBy 0000000c 0 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000004 00646464 80030019 1 1
+SCtrl 0 0 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000004 1 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+TrimBy 00000005 0 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+TrimBy 00000006 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+TrimBy 00000003 0 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000005 00646464 80030022 1 1
+SCtrl 0 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000007 1 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+TrimBy 00000008 0 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+TrimBy 00000009 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+TrimBy 00000006 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000006 00646464 80030007 1 1
+SCtrl 0 0 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000a 1 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+TrimBy 0000000b 0 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+TrimBy 0000000c 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+TrimBy 00000009 0 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000007 00646464 8007000f 1 1
+SCtrl 0 0 -10.00000000000000000000 4.99999999989999999173 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -4.99999999989999999173 4.99999999989999999173 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -4.99999999989999999173 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000008 00646464 80070010 1 1
+SCtrl 0 0 -4.99999999989999999173 4.99999999989999999173 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 4.99999999989999999173 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -4.99999999989999999173 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000009 00646464 8007009f 1 1
+SCtrl 0 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000d 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 0000000e 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000f 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000018 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000000a 00646464 800700a8 1 1
+SCtrl 0 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000010 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000011 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000012 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 0000000f 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000000b 00646464 800700b1 1 1
+SCtrl 0 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000013 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000014 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 00000015 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 00000012 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000000c 00646464 80070097 1 1
+SCtrl 0 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000016 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 00000017 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 00000018 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000015 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000000d 00646464 8007001f 1 1
+SCtrl 0 0 0.00000000000000000000 4.99999999989999999173 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000010000000827 4.99999999989999999173 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000010000000827 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 0000000e 00646464 80070020 1 1
+SCtrl 0 0 5.00000000010000000827 4.99999999989999999173 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 4.99999999989999999173 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000010000000827 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 0000000f 00646464 800700bc 1 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000019 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 0000001a 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000001b 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000024 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000010 00646464 800700c5 1 1
+SCtrl 0 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000001c 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 0000001d 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000001e 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 0000001b 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000011 00646464 800700ce 1 1
+SCtrl 0 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000001f 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000020 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 00000021 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 0000001e 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000012 00646464 800700b4 1 1
+SCtrl 0 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000022 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 00000023 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 00000024 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000021 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000013 00646464 8007002f 1 1
+SCtrl 0 0 10.00000000000000000000 4.99999999989999999173 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000010000000827 4.99999999989999999173 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000010000000827 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000014 00646464 80070030 1 1
+SCtrl 0 0 15.00000000010000000827 4.99999999989999999173 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 4.99999999989999999173 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000010000000827 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000015 00646464 800700d9 1 1
+SCtrl 0 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000026 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000025 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 00000030 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 00000027 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000016 00646464 800700e2 1 1
+SCtrl 0 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000029 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000028 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000027 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 0000002a 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000017 00646464 800700eb 1 1
+SCtrl 0 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000002c 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 0000002b 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 0000002a 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 0000002d 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000018 00646464 800700d1 1 1
+SCtrl 0 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000002f 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 0000002e 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 0000002d 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000030 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000019 00646464 8007003f 1 1
+SCtrl 0 0 -10.00000000000000000000 -5.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -4.99999999989999999173 -5.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -4.99999999989999999173 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 0000001a 00646464 80070040 1 1
+SCtrl 0 0 -4.99999999989999999173 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -4.99999999989999999173 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 0000001b 00646464 800700fc 1 1
+SCtrl 0 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000031 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 00000032 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000033 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 0000003c 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000001c 00646464 80070105 1 1
+SCtrl 0 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000034 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 00000035 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000036 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000033 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000001d 00646464 8007010e 1 1
+SCtrl 0 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000037 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 00000038 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000039 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000036 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000001e 00646464 800700f4 1 1
+SCtrl 0 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000003a 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 0000003b 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 0000003c 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000039 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000001f 00646464 8007004f 1 1
+SCtrl 0 0 0.00000000000000000000 -5.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000010000000827 -5.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000010000000827 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000020 00646464 80070050 1 1
+SCtrl 0 0 5.00000000010000000827 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000010000000827 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000021 00646464 80070119 1 1
+SCtrl 0 0 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000003d 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 0000003e 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 0000003f 0 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000048 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000022 00646464 80070122 1 1
+SCtrl 0 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000040 0 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 00000041 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000042 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 0000003f 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000023 00646464 8007012b 1 1
+SCtrl 0 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000043 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 00000044 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000045 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000042 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000024 00646464 80070111 1 1
+SCtrl 0 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000046 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 00000047 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000048 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000045 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000025 00646464 8007005f 1 1
+SCtrl 0 0 10.00000000000000000000 -5.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000010000000827 -5.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000010000000827 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000026 00646464 80070060 1 1
+SCtrl 0 0 15.00000000010000000827 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000010000000827 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000027 00646464 80070136 1 1
+SCtrl 0 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000004a 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000049 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 00000054 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 0000004b 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000028 00646464 8007013f 1 1
+SCtrl 0 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000004d 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 0000004c 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 0000004b 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 0000004e 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000029 00646464 80070148 1 1
+SCtrl 0 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000050 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 0000004f 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 0000004e 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 00000051 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 0000002a 00646464 8007012e 1 1
+SCtrl 0 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000053 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000052 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 00000051 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 00000054 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 0000002b 00646464 8007006f 1 1
+SCtrl 0 0 -10.00000000000000000000 -15.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -4.99999999989999999173 -15.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -4.99999999989999999173 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 0000002c 00646464 80070070 1 1
+SCtrl 0 0 -4.99999999989999999173 -15.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 -15.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -4.99999999989999999173 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 0000002d 00646464 80070159 1 1
+SCtrl 0 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000056 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000055 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 00000060 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 00000057 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 0000002e 00646464 80070162 1 1
+SCtrl 0 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000059 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000058 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000057 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 0000005a 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 0000002f 00646464 8007016b 1 1
+SCtrl 0 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000005c 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 0000005b 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 0000005a 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 0000005d 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000030 00646464 80070151 1 1
+SCtrl 0 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000005f 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 0000005e 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 0000005d 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000060 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000031 00646464 8007007f 1 1
+SCtrl 0 0 0.00000000000000000000 -15.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000010000000827 -15.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000010000000827 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000032 00646464 80070080 1 1
+SCtrl 0 0 5.00000000010000000827 -15.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -15.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000010000000827 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000033 00646464 80070176 1 1
+SCtrl 0 0 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000062 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000061 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 0000006c 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 00000063 0 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000034 00646464 8007017f 1 1
+SCtrl 0 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000065 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000064 0 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000063 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 00000066 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000035 00646464 80070188 1 1
+SCtrl 0 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000068 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 00000067 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000066 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000069 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000036 00646464 8007016e 1 1
+SCtrl 0 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000006b 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 0000006a 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 00000069 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 0000006c 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000037 00646464 8007008f 1 1
+SCtrl 0 0 10.00000000000000000000 -15.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000010000000827 -15.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000010000000827 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000038 00646464 80070090 1 1
+SCtrl 0 0 15.00000000010000000827 -15.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -15.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000010000000827 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000039 00646464 80070193 1 1
+SCtrl 0 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000006d 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 0000006e 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 0000006f 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000078 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000003a 00646464 8007019c 1 1
+SCtrl 0 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000070 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000071 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000072 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 0000006f 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000003b 00646464 800701a5 1 1
+SCtrl 0 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000073 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000074 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 00000075 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 00000072 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000003c 00646464 8007018b 1 1
+SCtrl 0 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000076 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 00000077 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 00000078 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000075 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddSurface
+Curve 00000001 1 1 00000001 00000003
+CCtrl 0 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000002 1 1 00000002 00000003
+CCtrl 0 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+CurvePt 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000003 1 1 00000003 00000004
+CCtrl 0 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000004 1 1 00000001 00000004
+CCtrl 0 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000005 1 1 00000002 00000004
+CCtrl 0 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+CurvePt 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000006 1 1 00000004 00000005
+CCtrl 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000007 1 1 00000001 00000005
+CCtrl 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000008 1 1 00000002 00000005
+CCtrl 0 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+CurvePt 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000009 1 1 00000005 00000006
+CCtrl 0 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000a 1 1 00000001 00000006
+CCtrl 0 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000000b 1 1 00000002 00000006
+CCtrl 0 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+CurvePt 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000c 1 1 00000006 00000003
+CCtrl 0 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000d 1 1 00000007 00000009
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000000e 1 1 00000008 00000009
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000f 1 1 00000009 0000000a
+CCtrl 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000010 1 1 00000007 0000000a
+CCtrl 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000011 1 1 00000008 0000000a
+CCtrl 0 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000012 1 1 0000000a 0000000b
+CCtrl 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000013 1 1 00000007 0000000b
+CCtrl 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000014 1 1 00000008 0000000b
+CCtrl 0 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000015 1 1 0000000b 0000000c
+CCtrl 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000016 1 1 00000007 0000000c
+CCtrl 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000017 1 1 00000008 0000000c
+CCtrl 0 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000018 1 1 0000000c 00000009
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000019 1 1 0000000d 0000000f
+CCtrl 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000001a 1 1 0000000e 0000000f
+CCtrl 0 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000001b 1 1 0000000f 00000010
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000001c 1 1 0000000d 00000010
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000001d 1 1 0000000e 00000010
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000001e 1 1 00000010 00000011
+CCtrl 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000001f 1 1 0000000d 00000011
+CCtrl 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000020 1 1 0000000e 00000011
+CCtrl 0 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000021 1 1 00000011 00000012
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000022 1 1 0000000d 00000012
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000023 1 1 0000000e 00000012
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000024 1 1 00000012 0000000f
+CCtrl 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000025 1 1 00000013 00000015
+CCtrl 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000026 1 1 00000014 00000015
+CCtrl 0 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000027 1 1 00000015 00000016
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000028 1 1 00000013 00000016
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000029 1 1 00000014 00000016
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000002a 1 1 00000016 00000017
+CCtrl 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000002b 1 1 00000013 00000017
+CCtrl 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000002c 1 1 00000014 00000017
+CCtrl 0 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000002d 1 1 00000017 00000018
+CCtrl 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000002e 1 1 00000013 00000018
+CCtrl 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000002f 1 1 00000014 00000018
+CCtrl 0 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000030 1 1 00000018 00000015
+CCtrl 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000031 1 1 00000019 0000001b
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000032 1 1 0000001a 0000001b
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000033 1 1 0000001b 0000001c
+CCtrl 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000034 1 1 00000019 0000001c
+CCtrl 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000035 1 1 0000001a 0000001c
+CCtrl 0 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000036 1 1 0000001c 0000001d
+CCtrl 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000037 1 1 00000019 0000001d
+CCtrl 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000038 1 1 0000001a 0000001d
+CCtrl 0 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000039 1 1 0000001d 0000001e
+CCtrl 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000003a 1 1 00000019 0000001e
+CCtrl 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000003b 1 1 0000001a 0000001e
+CCtrl 0 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000003c 1 1 0000001e 0000001b
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000003d 1 1 0000001f 00000021
+CCtrl 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000003e 1 1 00000020 00000021
+CCtrl 0 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000003f 1 1 00000021 00000022
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000040 1 1 0000001f 00000022
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000041 1 1 00000020 00000022
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000042 1 1 00000022 00000023
+CCtrl 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000043 1 1 0000001f 00000023
+CCtrl 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000044 1 1 00000020 00000023
+CCtrl 0 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000045 1 1 00000023 00000024
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000046 1 1 0000001f 00000024
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000047 1 1 00000020 00000024
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000048 1 1 00000024 00000021
+CCtrl 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000049 1 1 00000025 00000027
+CCtrl 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000004a 1 1 00000026 00000027
+CCtrl 0 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000004b 1 1 00000027 00000028
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000004c 1 1 00000025 00000028
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000004d 1 1 00000026 00000028
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000004e 1 1 00000028 00000029
+CCtrl 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000004f 1 1 00000025 00000029
+CCtrl 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000050 1 1 00000026 00000029
+CCtrl 0 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000051 1 1 00000029 0000002a
+CCtrl 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000052 1 1 00000025 0000002a
+CCtrl 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000053 1 1 00000026 0000002a
+CCtrl 0 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000054 1 1 0000002a 00000027
+CCtrl 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000055 1 1 0000002b 0000002d
+CCtrl 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000056 1 1 0000002c 0000002d
+CCtrl 0 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000057 1 1 0000002d 0000002e
+CCtrl 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000058 1 1 0000002b 0000002e
+CCtrl 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000059 1 1 0000002c 0000002e
+CCtrl 0 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000005a 1 1 0000002e 0000002f
+CCtrl 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000005b 1 1 0000002b 0000002f
+CCtrl 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000005c 1 1 0000002c 0000002f
+CCtrl 0 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000005d 1 1 0000002f 00000030
+CCtrl 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000005e 1 1 0000002b 00000030
+CCtrl 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000005f 1 1 0000002c 00000030
+CCtrl 0 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000060 1 1 00000030 0000002d
+CCtrl 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000061 1 1 00000031 00000033
+CCtrl 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000062 1 1 00000032 00000033
+CCtrl 0 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000063 1 1 00000033 00000034
+CCtrl 0 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000064 1 1 00000031 00000034
+CCtrl 0 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000065 1 1 00000032 00000034
+CCtrl 0 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000066 1 1 00000034 00000035
+CCtrl 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000067 1 1 00000031 00000035
+CCtrl 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000068 1 1 00000032 00000035
+CCtrl 0 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000069 1 1 00000035 00000036
+CCtrl 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000006a 1 1 00000031 00000036
+CCtrl 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000006b 1 1 00000032 00000036
+CCtrl 0 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000006c 1 1 00000036 00000033
+CCtrl 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000006d 1 1 00000037 00000039
+CCtrl 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000006e 1 1 00000038 00000039
+CCtrl 0 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000006f 1 1 00000039 0000003a
+CCtrl 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000070 1 1 00000037 0000003a
+CCtrl 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000071 1 1 00000038 0000003a
+CCtrl 0 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000072 1 1 0000003a 0000003b
+CCtrl 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000073 1 1 00000037 0000003b
+CCtrl 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000074 1 1 00000038 0000003b
+CCtrl 0 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000075 1 1 0000003b 0000003c
+CCtrl 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000076 1 1 00000037 0000003c
+CCtrl 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000077 1 1 00000038 0000003c
+CCtrl 0 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000078 1 1 0000003c 00000039
+CCtrl 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000079 1 1 00000001 00000009
+CCtrl 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000007a 1 1 00000001 0000000a
+CCtrl 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000007b 1 1 00000001 0000000b
+CCtrl 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000007c 1 1 00000001 0000000c
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000007d 1 1 00000001 0000000f
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000007e 1 1 00000001 00000010
+CCtrl 0 -0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000007f 1 1 00000001 00000011
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000080 1 1 00000001 00000012
+CCtrl 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000081 1 1 00000001 00000015
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000082 1 1 00000001 00000016
+CCtrl 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000083 1 1 00000001 00000017
+CCtrl 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000084 1 1 00000001 00000018
+CCtrl 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000085 1 1 00000001 0000001b
+CCtrl 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000086 1 1 00000001 0000001c
+CCtrl 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000087 1 1 00000001 0000001d
+CCtrl 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000088 1 1 00000001 0000001e
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000089 1 1 00000001 00000021
+CCtrl 0 -0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000008a 1 1 00000001 00000022
+CCtrl 0 -0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000008b 1 1 00000001 00000023
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000008c 1 1 00000001 00000024
+CCtrl 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000008d 1 1 00000001 00000027
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000008e 1 1 00000001 00000028
+CCtrl 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000008f 1 1 00000001 00000029
+CCtrl 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000090 1 1 00000001 0000002a
+CCtrl 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000091 1 1 00000001 0000002d
+CCtrl 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000092 1 1 00000001 0000002e
+CCtrl 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000093 1 1 00000001 0000002f
+CCtrl 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000094 1 1 00000001 00000030
+CCtrl 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000095 1 1 00000001 00000033
+CCtrl 0 -0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000096 1 1 00000001 00000034
+CCtrl 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000097 1 1 00000001 00000035
+CCtrl 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000098 1 1 00000001 00000036
+CCtrl 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000099 1 1 00000001 00000039
+CCtrl 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000009a 1 1 00000001 0000003a
+CCtrl 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000009b 1 1 00000001 0000003b
+CCtrl 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000009c 1 1 00000001 0000003c
+CCtrl 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000009d 1 1 00000002 00000009
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000009e 1 1 00000002 0000000a
+CCtrl 0 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000009f 1 1 00000002 0000000b
+CCtrl 0 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a0 1 1 00000002 0000000c
+CCtrl 0 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a1 1 1 00000002 0000000f
+CCtrl 0 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a2 1 1 00000002 00000010
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a3 1 1 00000002 00000011
+CCtrl 0 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a4 1 1 00000002 00000012
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a5 1 1 00000002 00000015
+CCtrl 0 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a6 1 1 00000002 00000016
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 -0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 10.00000000000000000000 -0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a7 1 1 00000002 00000017
+CCtrl 0 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a8 1 1 00000002 00000018
+CCtrl 0 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a9 1 1 00000002 0000001b
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000aa 1 1 00000002 0000001c
+CCtrl 0 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000ab 1 1 00000002 0000001d
+CCtrl 0 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000ac 1 1 00000002 0000001e
+CCtrl 0 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000ad 1 1 00000002 00000021
+CCtrl 0 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000ae 1 1 00000002 00000022
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000af 1 1 00000002 00000023
+CCtrl 0 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b0 1 1 00000002 00000024
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b1 1 1 00000002 00000027
+CCtrl 0 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b2 1 1 00000002 00000028
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000000000 -0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b3 1 1 00000002 00000029
+CCtrl 0 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b4 1 1 00000002 0000002a
+CCtrl 0 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b5 1 1 00000002 0000002d
+CCtrl 0 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b6 1 1 00000002 0000002e
+CCtrl 0 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b7 1 1 00000002 0000002f
+CCtrl 0 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b8 1 1 00000002 00000030
+CCtrl 0 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b9 1 1 00000002 00000033
+CCtrl 0 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000ba 1 1 00000002 00000034
+CCtrl 0 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000bb 1 1 00000002 00000035
+CCtrl 0 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -10.00000000000000000000 -0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000bc 1 1 00000002 00000036
+CCtrl 0 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000bd 1 1 00000002 00000039
+CCtrl 0 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000be 1 1 00000002 0000003a
+CCtrl 0 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000bf 1 1 00000002 0000003b
+CCtrl 0 10.00000000000000000000 -10.00000000000000000000 -0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -10.00000000000000000000 -0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000c0 1 1 00000002 0000003c
+CCtrl 0 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000003
+Group.type=5100
+Group.order=2
+Group.name=extrude
+Group.opA.v=00000002
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.predef.entityB.v=80020000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00040000 1002
+ 2 00040001 1002
+ 3 00040002 1002
+ 4 00040000 1001
+ 5 00040001 1001
+ 6 00040002 1001
+ 7 00040000 1004
+ 8 00040001 1003
+ 9 00040002 1003
+ 10 00050000 1002
+ 11 00050001 1002
+ 12 00050002 1002
+ 13 00050000 1001
+ 14 00050001 1001
+ 15 00050002 1001
+ 16 00050000 1004
+ 17 00050001 1003
+ 18 00050002 1003
+ 19 00060000 1002
+ 20 00060001 1002
+ 21 00060002 1002
+ 22 00060000 1001
+ 23 00060001 1001
+ 24 00060002 1001
+ 25 00060000 1004
+ 26 00060001 1003
+ 27 00060002 1003
+ 28 00070000 1002
+ 29 00070001 1002
+ 30 00070002 1002
+ 31 00070000 1001
+ 32 00070001 1001
+ 33 00070002 1001
+ 34 00070000 1004
+ 35 00070001 1003
+ 36 00070002 1003
+ 37 80020000 1002
+ 38 80020000 1001
+ 39 80020001 1002
+ 40 80020002 1002
+ 41 80020001 1001
+ 42 80020002 1001
+ 43 80020002 1003
+ 44 00000000 1001
+ 45 00000000 1002
+}
+AddGroup
+
+Group.h.v=00000004
+Group.type=5001
+Group.order=3
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80040000
+Group.color=00646464
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=80030028
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000005
+Group.type=5100
+Group.order=4
+Group.name=extrude
+Group.opA.v=00000004
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.meshCombine=1
+Group.predef.entityB.v=80040000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00080000 1002
+ 2 00080001 1002
+ 3 00080020 1002
+ 4 00080040 1002
+ 5 00080000 1001
+ 6 00080001 1001
+ 7 00080020 1001
+ 8 00080040 1001
+ 9 00080001 1003
+ 10 80040000 1002
+ 11 80040000 1001
+ 12 80040001 1002
+ 13 80040002 1002
+ 14 80040001 1001
+ 15 80040002 1001
+ 16 80040002 1003
+ 17 00000000 1001
+ 18 00000000 1002
+ 19 00080002 1002
+ 20 00080002 1001
+ 21 00080000 1004
+ 22 00080002 1003
+ 23 00090000 1002
+ 24 00090001 1002
+ 25 00090002 1002
+ 26 00090000 1001
+ 27 00090001 1001
+ 28 00090002 1001
+ 29 00090000 1004
+ 30 00090001 1003
+ 31 00090002 1003
+ 32 000a0000 1002
+ 33 000a0001 1002
+ 34 000a0002 1002
+ 35 000a0000 1001
+ 36 000a0001 1001
+ 37 000a0002 1001
+ 38 000a0000 1004
+ 39 000a0001 1003
+ 40 000a0002 1003
+ 41 000b0000 1002
+ 42 000b0001 1002
+ 43 000b0002 1002
+ 44 000b0000 1001
+ 45 000b0001 1001
+ 46 000b0002 1001
+ 47 000b0000 1004
+ 48 000b0001 1003
+ 49 000b0002 1003
+}
+AddGroup
+
+Group.h.v=00000006
+Group.type=5201
+Group.order=5
+Group.name=translate
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000005
+Group.valA=3.00000000000000000000
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.meshCombine=1
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 80050001 0
+ 2 80050002 0
+ 3 80050003 0
+ 4 80050004 0
+ 5 80050005 0
+ 6 80050006 0
+ 7 80050007 0
+ 8 80050008 0
+ 9 80050009 0
+ 10 8005000c 0
+ 11 8005000d 0
+ 12 8005000e 0
+ 13 8005000f 0
+ 14 80050010 0
+ 15 80050011 0
+ 16 80050012 0
+ 17 80050001 1
+ 18 80050002 1
+ 19 80050003 1
+ 20 80050004 1
+ 21 80050005 1
+ 22 80050006 1
+ 23 80050007 1
+ 24 80050008 1
+ 25 80050009 1
+ 26 8005000c 1
+ 27 8005000d 1
+ 28 8005000e 1
+ 29 8005000f 1
+ 30 80050010 1
+ 31 80050011 1
+ 32 80050012 1
+ 33 80050001 1000
+ 34 80050002 1000
+ 35 80050003 1000
+ 36 80050004 1000
+ 37 80050005 1000
+ 38 80050006 1000
+ 39 80050007 1000
+ 40 80050008 1000
+ 41 80050009 1000
+ 42 8005000c 1000
+ 43 8005000d 1000
+ 44 8005000e 1000
+ 45 8005000f 1000
+ 46 80050010 1000
+ 47 80050011 1000
+ 48 80050012 1000
+ 49 80050013 0
+ 50 80050014 0
+ 51 80050015 0
+ 52 80050016 0
+ 53 80050017 0
+ 54 80050018 0
+ 55 80050019 0
+ 56 8005001a 0
+ 57 8005001b 0
+ 58 8005001c 0
+ 59 8005001d 0
+ 60 8005001e 0
+ 61 8005001f 0
+ 62 80050020 0
+ 63 80050021 0
+ 64 80050022 0
+ 65 80050023 0
+ 66 80050024 0
+ 67 80050025 0
+ 68 80050026 0
+ 69 80050027 0
+ 70 80050028 0
+ 71 80050029 0
+ 72 8005002a 0
+ 73 8005002b 0
+ 74 8005002c 0
+ 75 8005002d 0
+ 76 8005002e 0
+ 77 8005002f 0
+ 78 80050030 0
+ 79 80050031 0
+ 80 80050013 1
+ 81 80050014 1
+ 82 80050015 1
+ 83 80050016 1
+ 84 80050017 1
+ 85 80050018 1
+ 86 80050019 1
+ 87 8005001a 1
+ 88 8005001b 1
+ 89 8005001c 1
+ 90 8005001d 1
+ 91 8005001e 1
+ 92 8005001f 1
+ 93 80050020 1
+ 94 80050021 1
+ 95 80050022 1
+ 96 80050023 1
+ 97 80050024 1
+ 98 80050025 1
+ 99 80050026 1
+ 100 80050027 1
+ 101 80050028 1
+ 102 80050029 1
+ 103 8005002a 1
+ 104 8005002b 1
+ 105 8005002c 1
+ 106 8005002d 1
+ 107 8005002e 1
+ 108 8005002f 1
+ 109 80050030 1
+ 110 80050031 1
+ 111 80050013 1000
+ 112 80050014 1000
+ 113 80050015 1000
+ 114 80050016 1000
+ 115 80050017 1000
+ 116 80050018 1000
+ 117 80050019 1000
+ 118 8005001a 1000
+ 119 8005001b 1000
+ 120 8005001c 1000
+ 121 8005001d 1000
+ 122 8005001e 1000
+ 123 8005001f 1000
+ 124 80050020 1000
+ 125 80050021 1000
+ 126 80050022 1000
+ 127 80050023 1000
+ 128 80050024 1000
+ 129 80050025 1000
+ 130 80050026 1000
+ 131 80050027 1000
+ 132 80050028 1000
+ 133 80050029 1000
+ 134 8005002a 1000
+ 135 8005002b 1000
+ 136 8005002c 1000
+ 137 8005002d 1000
+ 138 8005002e 1000
+ 139 8005002f 1000
+ 140 80050030 1000
+ 141 80050031 1000
+}
+AddGroup
+
+Group.h.v=00000007
+Group.type=5201
+Group.order=6
+Group.name=translate
+Group.activeWorkplane.v=00010000
+Group.opA.v=00000006
+Group.valA=3.00000000000000000000
+Group.color=00646464
+Group.subtype=7000
+Group.skipFirst=0
+Group.meshCombine=1
+Group.predef.entityB.v=00010000
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 80060001 0
+ 2 80060002 0
+ 3 80060003 0
+ 4 80060004 0
+ 5 80060005 0
+ 6 80060006 0
+ 7 80060007 0
+ 8 80060008 0
+ 9 80060009 0
+ 10 8006000a 0
+ 11 8006000b 0
+ 12 8006000c 0
+ 13 8006000d 0
+ 14 8006000e 0
+ 15 8006000f 0
+ 16 80060010 0
+ 17 80060011 0
+ 18 80060012 0
+ 19 80060013 0
+ 20 80060014 0
+ 21 80060015 0
+ 22 80060016 0
+ 23 80060017 0
+ 24 80060018 0
+ 25 80060019 0
+ 26 8006001a 0
+ 27 8006001b 0
+ 28 8006001c 0
+ 29 8006001d 0
+ 30 8006001e 0
+ 31 8006001f 0
+ 32 80060020 0
+ 33 80060021 0
+ 34 80060022 0
+ 35 80060023 0
+ 36 80060024 0
+ 37 80060025 0
+ 38 80060026 0
+ 39 80060027 0
+ 40 80060028 0
+ 41 80060029 0
+ 42 8006002a 0
+ 43 8006002b 0
+ 44 8006002c 0
+ 45 8006002d 0
+ 46 8006002e 0
+ 47 8006002f 0
+ 48 80060030 0
+ 49 80060001 1
+ 50 80060002 1
+ 51 80060003 1
+ 52 80060004 1
+ 53 80060005 1
+ 54 80060006 1
+ 55 80060007 1
+ 56 80060008 1
+ 57 80060009 1
+ 58 8006000a 1
+ 59 8006000b 1
+ 60 8006000c 1
+ 61 8006000d 1
+ 62 8006000e 1
+ 63 8006000f 1
+ 64 80060010 1
+ 65 80060011 1
+ 66 80060012 1
+ 67 80060013 1
+ 68 80060014 1
+ 69 80060015 1
+ 70 80060016 1
+ 71 80060017 1
+ 72 80060018 1
+ 73 80060019 1
+ 74 8006001a 1
+ 75 8006001b 1
+ 76 8006001c 1
+ 77 8006001d 1
+ 78 8006001e 1
+ 79 8006001f 1
+ 80 80060020 1
+ 81 80060021 1
+ 82 80060022 1
+ 83 80060023 1
+ 84 80060024 1
+ 85 80060025 1
+ 86 80060026 1
+ 87 80060027 1
+ 88 80060028 1
+ 89 80060029 1
+ 90 8006002a 1
+ 91 8006002b 1
+ 92 8006002c 1
+ 93 8006002d 1
+ 94 8006002e 1
+ 95 8006002f 1
+ 96 80060030 1
+ 97 80060001 1000
+ 98 80060002 1000
+ 99 80060003 1000
+ 100 80060004 1000
+ 101 80060005 1000
+ 102 80060006 1000
+ 103 80060007 1000
+ 104 80060008 1000
+ 105 80060009 1000
+ 106 8006000a 1000
+ 107 8006000b 1000
+ 108 8006000c 1000
+ 109 8006000d 1000
+ 110 8006000e 1000
+ 111 8006000f 1000
+ 112 80060010 1000
+ 113 80060011 1000
+ 114 80060012 1000
+ 115 80060013 1000
+ 116 80060014 1000
+ 117 80060015 1000
+ 118 80060016 1000
+ 119 80060017 1000
+ 120 80060018 1000
+ 121 80060019 1000
+ 122 8006001a 1000
+ 123 8006001b 1000
+ 124 8006001c 1000
+ 125 8006001d 1000
+ 126 8006001e 1000
+ 127 8006001f 1000
+ 128 80060020 1000
+ 129 80060021 1000
+ 130 80060022 1000
+ 131 80060023 1000
+ 132 80060024 1000
+ 133 80060025 1000
+ 134 80060026 1000
+ 135 80060027 1000
+ 136 80060028 1000
+ 137 80060029 1000
+ 138 8006002a 1000
+ 139 8006002b 1000
+ 140 8006002c 1000
+ 141 8006002d 1000
+ 142 8006002e 1000
+ 143 8006002f 1000
+ 144 80060030 1000
+ 145 80060031 0
+ 146 80060032 0
+ 147 80060050 0
+ 148 80060051 0
+ 149 8006006f 0
+ 150 80060070 0
+ 151 80060033 0
+ 152 80060034 0
+ 153 80060035 0
+ 154 80060036 0
+ 155 80060037 0
+ 156 80060038 0
+ 157 80060039 0
+ 158 8006003a 0
+ 159 8006003b 0
+ 160 8006003c 0
+ 161 8006003d 0
+ 162 8006003e 0
+ 163 8006003f 0
+ 164 80060040 0
+ 165 80060041 0
+ 166 80060042 0
+ 167 80060043 0
+ 168 80060044 0
+ 169 80060045 0
+ 170 80060046 0
+ 171 80060047 0
+ 172 80060048 0
+ 173 80060049 0
+ 174 8006004a 0
+ 175 8006004b 0
+ 176 8006004c 0
+ 177 8006004d 0
+ 178 8006004e 0
+ 179 8006004f 0
+ 180 80060052 0
+ 181 80060053 0
+ 182 80060054 0
+ 183 80060055 0
+ 184 80060056 0
+ 185 80060057 0
+ 186 80060058 0
+ 187 80060059 0
+ 188 8006005a 0
+ 189 8006005b 0
+ 190 8006005c 0
+ 191 8006005d 0
+ 192 8006005e 0
+ 193 8006005f 0
+ 194 80060060 0
+ 195 80060061 0
+ 196 80060062 0
+ 197 80060063 0
+ 198 80060064 0
+ 199 80060065 0
+ 200 80060066 0
+ 201 80060067 0
+ 202 80060068 0
+ 203 80060069 0
+ 204 8006006a 0
+ 205 8006006b 0
+ 206 8006006c 0
+ 207 8006006d 0
+ 208 8006006e 0
+ 209 80060071 0
+ 210 80060072 0
+ 211 80060073 0
+ 212 80060074 0
+ 213 80060075 0
+ 214 80060076 0
+ 215 80060077 0
+ 216 80060078 0
+ 217 80060079 0
+ 218 8006007a 0
+ 219 8006007b 0
+ 220 8006007c 0
+ 221 8006007d 0
+ 222 8006007e 0
+ 223 8006007f 0
+ 224 80060080 0
+ 225 80060081 0
+ 226 80060082 0
+ 227 80060083 0
+ 228 80060084 0
+ 229 80060085 0
+ 230 80060086 0
+ 231 80060087 0
+ 232 80060088 0
+ 233 80060089 0
+ 234 8006008a 0
+ 235 8006008b 0
+ 236 8006008c 0
+ 237 8006008d 0
+ 238 80060031 1
+ 239 80060032 1
+ 240 80060050 1
+ 241 80060051 1
+ 242 8006006f 1
+ 243 80060070 1
+ 244 80060033 1
+ 245 80060034 1
+ 246 80060035 1
+ 247 80060036 1
+ 248 80060037 1
+ 249 80060038 1
+ 250 80060039 1
+ 251 8006003a 1
+ 252 8006003b 1
+ 253 8006003c 1
+ 254 8006003d 1
+ 255 8006003e 1
+ 256 8006003f 1
+ 257 80060040 1
+ 258 80060041 1
+ 259 80060042 1
+ 260 80060043 1
+ 261 80060044 1
+ 262 80060045 1
+ 263 80060046 1
+ 264 80060047 1
+ 265 80060048 1
+ 266 80060049 1
+ 267 8006004a 1
+ 268 8006004b 1
+ 269 8006004c 1
+ 270 8006004d 1
+ 271 8006004e 1
+ 272 8006004f 1
+ 273 80060052 1
+ 274 80060053 1
+ 275 80060054 1
+ 276 80060055 1
+ 277 80060056 1
+ 278 80060057 1
+ 279 80060058 1
+ 280 80060059 1
+ 281 8006005a 1
+ 282 8006005b 1
+ 283 8006005c 1
+ 284 8006005d 1
+ 285 8006005e 1
+ 286 8006005f 1
+ 287 80060060 1
+ 288 80060061 1
+ 289 80060062 1
+ 290 80060063 1
+ 291 80060064 1
+ 292 80060065 1
+ 293 80060066 1
+ 294 80060067 1
+ 295 80060068 1
+ 296 80060069 1
+ 297 8006006a 1
+ 298 8006006b 1
+ 299 8006006c 1
+ 300 8006006d 1
+ 301 8006006e 1
+ 302 80060071 1
+ 303 80060072 1
+ 304 80060073 1
+ 305 80060074 1
+ 306 80060075 1
+ 307 80060076 1
+ 308 80060077 1
+ 309 80060078 1
+ 310 80060079 1
+ 311 8006007a 1
+ 312 8006007b 1
+ 313 8006007c 1
+ 314 8006007d 1
+ 315 8006007e 1
+ 316 8006007f 1
+ 317 80060080 1
+ 318 80060081 1
+ 319 80060082 1
+ 320 80060083 1
+ 321 80060084 1
+ 322 80060085 1
+ 323 80060086 1
+ 324 80060087 1
+ 325 80060088 1
+ 326 80060089 1
+ 327 8006008a 1
+ 328 8006008b 1
+ 329 8006008c 1
+ 330 8006008d 1
+ 331 80060031 1000
+ 332 80060032 1000
+ 333 80060050 1000
+ 334 80060051 1000
+ 335 8006006f 1000
+ 336 80060070 1000
+ 337 80060033 1000
+ 338 80060034 1000
+ 339 80060035 1000
+ 340 80060036 1000
+ 341 80060037 1000
+ 342 80060038 1000
+ 343 80060039 1000
+ 344 8006003a 1000
+ 345 8006003b 1000
+ 346 8006003c 1000
+ 347 8006003d 1000
+ 348 8006003e 1000
+ 349 8006003f 1000
+ 350 80060040 1000
+ 351 80060041 1000
+ 352 80060042 1000
+ 353 80060043 1000
+ 354 80060044 1000
+ 355 80060045 1000
+ 356 80060046 1000
+ 357 80060047 1000
+ 358 80060048 1000
+ 359 80060049 1000
+ 360 8006004a 1000
+ 361 8006004b 1000
+ 362 8006004c 1000
+ 363 8006004d 1000
+ 364 8006004e 1000
+ 365 8006004f 1000
+ 366 80060052 1000
+ 367 80060053 1000
+ 368 80060054 1000
+ 369 80060055 1000
+ 370 80060056 1000
+ 371 80060057 1000
+ 372 80060058 1000
+ 373 80060059 1000
+ 374 8006005a 1000
+ 375 8006005b 1000
+ 376 8006005c 1000
+ 377 8006005d 1000
+ 378 8006005e 1000
+ 379 8006005f 1000
+ 380 80060060 1000
+ 381 80060061 1000
+ 382 80060062 1000
+ 383 80060063 1000
+ 384 80060064 1000
+ 385 80060065 1000
+ 386 80060066 1000
+ 387 80060067 1000
+ 388 80060068 1000
+ 389 80060069 1000
+ 390 8006006a 1000
+ 391 8006006b 1000
+ 392 8006006c 1000
+ 393 8006006d 1000
+ 394 8006006e 1000
+ 395 80060071 1000
+ 396 80060072 1000
+ 397 80060073 1000
+ 398 80060074 1000
+ 399 80060075 1000
+ 400 80060076 1000
+ 401 80060077 1000
+ 402 80060078 1000
+ 403 80060079 1000
+ 404 8006007a 1000
+ 405 8006007b 1000
+ 406 8006007c 1000
+ 407 8006007d 1000
+ 408 8006007e 1000
+ 409 8006007f 1000
+ 410 80060080 1000
+ 411 80060081 1000
+ 412 80060082 1000
+ 413 80060083 1000
+ 414 80060084 1000
+ 415 80060085 1000
+ 416 80060086 1000
+ 417 80060087 1000
+ 418 80060088 1000
+ 419 80060089 1000
+ 420 8006008a 1000
+ 421 8006008b 1000
+ 422 8006008c 1000
+ 423 8006008d 1000
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=20.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=-20.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=20.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00050010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00050011
+Param.val=-20.00000000000000000000
+AddParam
+
+Param.h.v.=00050013
+Param.val=20.00000000000000000000
+AddParam
+
+Param.h.v.=00050014
+Param.val=-20.00000000000000000000
+AddParam
+
+Param.h.v.=00060010
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00060011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00060013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00060014
+Param.val=-20.00000000000000000000
+AddParam
+
+Param.h.v.=00070010
+Param.val=20.00000000000000000000
+AddParam
+
+Param.h.v.=00070011
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00070013
+Param.val=-15.00000000000000000000
+AddParam
+
+Param.h.v.=00070014
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00080010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00080011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00080013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00080014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00090010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00090011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00090013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00090014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=000a0010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=000a0011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=000a0013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=000a0014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=000b0010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=000b0011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=000b0013
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=000b0014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=80030000
+AddParam
+
+Param.h.v.=80030001
+AddParam
+
+Param.h.v.=80030002
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=80050000
+AddParam
+
+Param.h.v.=80050001
+AddParam
+
+Param.h.v.=80050002
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=80060000
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80060001
+AddParam
+
+Param.h.v.=80060002
+AddParam
+
+Param.h.v.=80070000
+AddParam
+
+Param.h.v.=80070001
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=80070002
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000005
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000006
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000007
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Request.h.v=00000008
+Request.type=200
+Request.workplane.v=80040000
+Request.group.v=00000004
+Request.construction=0
+AddRequest
+
+Request.h.v=00000009
+Request.type=200
+Request.workplane.v=80040000
+Request.group.v=00000004
+Request.construction=0
+AddRequest
+
+Request.h.v=0000000a
+Request.type=200
+Request.workplane.v=80040000
+Request.group.v=00000004
+Request.construction=0
+AddRequest
+
+Request.h.v=0000000b
+Request.type=200
+Request.workplane.v=80040000
+Request.group.v=00000004
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00050001
+Entity.point[1].v=00050002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00050002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00060001
+Entity.point[1].v=00060002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00060002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00070001
+Entity.point[1].v=00070002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00070002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00080000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00080001
+Entity.point[1].v=00080002
+Entity.workplane.v=80040000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00080001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00080002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00090000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00090001
+Entity.point[1].v=00090002
+Entity.workplane.v=80040000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00090001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00090002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=000a0000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=000a0001
+Entity.point[1].v=000a0002
+Entity.workplane.v=80040000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=000a0001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=000a0002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=000b0000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=000b0001
+Entity.point[1].v=000b0002
+Entity.workplane.v=80040000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=000b0001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=000b0002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80040000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030001
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.point[1].v=80030003
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030004
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030005
+Entity.point[1].v=80030006
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030007
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030005
+Entity.point[1].v=80030002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030006
+Entity.point[1].v=80030003
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000b
+Entity.point[1].v=8003000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000e
+Entity.point[1].v=8003000f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030010
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000e
+Entity.point[1].v=8003000b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003000f
+Entity.point[1].v=8003000c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030013
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030014
+Entity.point[1].v=80030015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030014
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030015
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030016
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030017
+Entity.point[1].v=80030018
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030017
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030018
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030019
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030017
+Entity.point[1].v=80030014
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030018
+Entity.point[1].v=80030015
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003001d
+Entity.point[1].v=8003001e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003001f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030020
+Entity.point[1].v=80030021
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030020
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030021
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-15.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030022
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030023
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030020
+Entity.point[1].v=8003001d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030024
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80030021
+Entity.point[1].v=8003001e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030027
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80030028
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030028
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030029
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8003002a
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8003002a
+Entity.point[1].v=80030028
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002c
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8003002a
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003002d
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=80030028
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80040002
+Entity.normal.v=80040001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80040001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80040002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80040002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050001
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050002
+Entity.point[1].v=80050013
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050002
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050005
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050006
+Entity.point[1].v=80050014
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050006
+Entity.point[1].v=80050002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8005000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005000d
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005000e
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8005000f
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005000f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050010
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005000f
+Entity.point[1].v=8005000d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050011
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8005000f
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050012
+Entity.type=5000
+Entity.construction=0
+Entity.point[0].v=8005000d
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050013
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050014
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050015
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050016
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050014
+Entity.point[1].v=80050013
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050017
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050018
+Entity.point[1].v=80050019
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050018
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050019
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005001a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005001b
+Entity.point[1].v=8005001c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005001b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005001c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005001d
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005001e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005001b
+Entity.point[1].v=80050018
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005001f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005001c
+Entity.point[1].v=80050019
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050020
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050021
+Entity.point[1].v=80050022
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050021
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050022
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050023
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050024
+Entity.point[1].v=80050025
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050024
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050025
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050026
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050027
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050024
+Entity.point[1].v=80050021
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050028
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80050025
+Entity.point[1].v=80050022
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050029
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005002a
+Entity.point[1].v=8005002b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005002a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005002b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005002c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005002d
+Entity.point[1].v=8005002e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005002d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005002e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8005002f
+Entity.type=5001
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050030
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005002d
+Entity.point[1].v=8005002a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80050031
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8005002e
+Entity.point[1].v=8005002b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060001
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060002
+Entity.point[1].v=80060031
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060002
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060005
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060006
+Entity.point[1].v=80060032
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060006
+Entity.point[1].v=80060002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006000a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8006000b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006000b
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8006000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006000d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006000e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006000d
+Entity.point[1].v=8006000b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006000f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060010
+Entity.type=5003
+Entity.construction=0
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060011
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060012
+Entity.point[1].v=80060050
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060012
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060015
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060016
+Entity.point[1].v=80060051
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060016
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060019
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060016
+Entity.point[1].v=80060012
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006001a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8006001b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006001b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006001c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8006001d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006001d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006001e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006001d
+Entity.point[1].v=8006001b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006001f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060020
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060021
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060022
+Entity.point[1].v=8006006f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060022
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060025
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060026
+Entity.point[1].v=80060070
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060026
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060029
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060026
+Entity.point[1].v=80060022
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006002a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8006002b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006002b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006002c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8006002d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006002d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006002e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006002d
+Entity.point[1].v=8006002b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006002f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060030
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060031
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060032
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060033
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060034
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060032
+Entity.point[1].v=80060031
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060035
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060036
+Entity.point[1].v=80060037
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060036
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060037
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060038
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060039
+Entity.point[1].v=8006003a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060039
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006003a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006003b
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006003c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060039
+Entity.point[1].v=80060036
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006003d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006003a
+Entity.point[1].v=80060037
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006003e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006003f
+Entity.point[1].v=80060040
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006003f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060040
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060041
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060042
+Entity.point[1].v=80060043
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060042
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060043
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060044
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060045
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060042
+Entity.point[1].v=8006003f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060046
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060043
+Entity.point[1].v=80060040
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060047
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060048
+Entity.point[1].v=80060049
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060048
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060049
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006004a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006004b
+Entity.point[1].v=8006004c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006004b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006004c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006004d
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006004e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006004b
+Entity.point[1].v=80060048
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006004f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006004c
+Entity.point[1].v=80060049
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060050
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060051
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060052
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060053
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060051
+Entity.point[1].v=80060050
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060054
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060055
+Entity.point[1].v=80060056
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060055
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060056
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060057
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060058
+Entity.point[1].v=80060059
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060058
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060059
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006005a
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006005b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060058
+Entity.point[1].v=80060055
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006005c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060059
+Entity.point[1].v=80060056
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006005d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006005e
+Entity.point[1].v=8006005f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006005e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006005f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060060
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060061
+Entity.point[1].v=80060062
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060061
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060062
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060063
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060064
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060061
+Entity.point[1].v=8006005e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060065
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060062
+Entity.point[1].v=8006005f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060066
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060067
+Entity.point[1].v=80060068
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060067
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060068
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060069
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006006a
+Entity.point[1].v=8006006b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006006a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006006b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006006c
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006006d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006006a
+Entity.point[1].v=80060067
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006006e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006006b
+Entity.point[1].v=80060068
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006006f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060070
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060071
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060072
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060070
+Entity.point[1].v=8006006f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060073
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060074
+Entity.point[1].v=80060075
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060074
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060075
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060076
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060077
+Entity.point[1].v=80060078
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060077
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060078
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060079
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006007a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060077
+Entity.point[1].v=80060074
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006007b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060078
+Entity.point[1].v=80060075
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006007c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006007d
+Entity.point[1].v=8006007e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006007d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006007e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006007f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060080
+Entity.point[1].v=80060081
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060080
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060081
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060082
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060083
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060080
+Entity.point[1].v=8006007d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060084
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060081
+Entity.point[1].v=8006007e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060085
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060086
+Entity.point[1].v=80060087
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060086
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060087
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060088
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060089
+Entity.point[1].v=8006008a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80060089
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006008a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006008b
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006008c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80060089
+Entity.point[1].v=80060086
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8006008d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8006008a
+Entity.point[1].v=80060087
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070001
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070002
+Entity.point[1].v=80070091
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070002
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070005
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070006
+Entity.point[1].v=80070092
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070006
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070009
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070006
+Entity.point[1].v=80070002
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007000a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007000b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007000b
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007000c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007000d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007000d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007000e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007000d
+Entity.point[1].v=8007000b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007000f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070010
+Entity.type=5003
+Entity.construction=0
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070011
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070012
+Entity.point[1].v=80070093
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070012
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070015
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070016
+Entity.point[1].v=80070094
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070016
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070019
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070016
+Entity.point[1].v=80070012
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007001a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007001b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007001b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007001c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007001d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007001d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007001e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007001d
+Entity.point[1].v=8007001b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007001f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070020
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070021
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070022
+Entity.point[1].v=80070095
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070022
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070025
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070026
+Entity.point[1].v=80070096
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070026
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070029
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070026
+Entity.point[1].v=80070022
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007002a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007002b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007002b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007002c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007002d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007002d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007002e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007002d
+Entity.point[1].v=8007002b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007002f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070030
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070031
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070032
+Entity.point[1].v=800700ee
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070032
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070035
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070036
+Entity.point[1].v=800700ef
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070036
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070039
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070036
+Entity.point[1].v=80070032
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007003a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007003b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007003b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007003c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007003d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007003d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007003e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007003d
+Entity.point[1].v=8007003b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007003f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070040
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070041
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070042
+Entity.point[1].v=800700f0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070042
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070045
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070046
+Entity.point[1].v=800700f1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070046
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070049
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070046
+Entity.point[1].v=80070042
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007004a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007004b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007004b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007004c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007004d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007004d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007004e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007004d
+Entity.point[1].v=8007004b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007004f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070050
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070051
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070052
+Entity.point[1].v=800700f2
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070052
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070055
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070056
+Entity.point[1].v=800700f3
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070056
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070059
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070056
+Entity.point[1].v=80070052
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007005a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007005b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007005b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007005c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007005d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007005d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007005e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007005d
+Entity.point[1].v=8007005b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007005f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070060
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070061
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070062
+Entity.point[1].v=8007014b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070062
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070065
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070066
+Entity.point[1].v=8007014c
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070066
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070069
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070066
+Entity.point[1].v=80070062
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007006a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007006b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007006b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007006c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007006d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007006d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007006e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007006d
+Entity.point[1].v=8007006b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007006f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070070
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070071
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070072
+Entity.point[1].v=8007014d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070072
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070075
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070076
+Entity.point[1].v=8007014e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070076
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070079
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070076
+Entity.point[1].v=80070072
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007007a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007007b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007007b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007007c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007007d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007007d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007007e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007007d
+Entity.point[1].v=8007007b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007007f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070080
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070081
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070082
+Entity.point[1].v=8007014f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070082
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070085
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070086
+Entity.point[1].v=80070150
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070086
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070089
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070086
+Entity.point[1].v=80070082
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007008a
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007008b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007008b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007008c
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=8007008d
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007008d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007008e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007008d
+Entity.point[1].v=8007008b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007008f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070090
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=-20.00000000000000000000
+Entity.actNormal.vz=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070091
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070092
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070093
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070094
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070095
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070096
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070097
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070098
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070092
+Entity.point[1].v=80070091
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070099
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007009a
+Entity.point[1].v=8007009b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007009a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007009b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007009c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007009d
+Entity.point[1].v=8007009e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007009d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007009e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007009f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a0
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007009d
+Entity.point[1].v=8007009a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a1
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007009e
+Entity.point[1].v=8007009b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a2
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700a3
+Entity.point[1].v=800700a4
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a3
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a4
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a5
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700a6
+Entity.point[1].v=800700a7
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a6
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a7
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a8
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700a9
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700a6
+Entity.point[1].v=800700a3
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700aa
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700a7
+Entity.point[1].v=800700a4
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ab
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700ac
+Entity.point[1].v=800700ad
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ac
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ad
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ae
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700af
+Entity.point[1].v=800700b0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700af
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b0
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b1
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b2
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700af
+Entity.point[1].v=800700ac
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b3
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700b0
+Entity.point[1].v=800700ad
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b4
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b5
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070094
+Entity.point[1].v=80070093
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b6
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700b7
+Entity.point[1].v=800700b8
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b7
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b8
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700b9
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700ba
+Entity.point[1].v=800700bb
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ba
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700bb
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700bc
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700bd
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700ba
+Entity.point[1].v=800700b7
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700be
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700bb
+Entity.point[1].v=800700b8
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700bf
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700c0
+Entity.point[1].v=800700c1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c0
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c1
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c2
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700c3
+Entity.point[1].v=800700c4
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c3
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c4
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c5
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c6
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700c3
+Entity.point[1].v=800700c0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c7
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700c4
+Entity.point[1].v=800700c1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c8
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700c9
+Entity.point[1].v=800700ca
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700c9
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ca
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700cb
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700cc
+Entity.point[1].v=800700cd
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700cc
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700cd
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ce
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700cf
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700cc
+Entity.point[1].v=800700c9
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d0
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700cd
+Entity.point[1].v=800700ca
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d1
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d2
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070096
+Entity.point[1].v=80070095
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d3
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700d4
+Entity.point[1].v=800700d5
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d4
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d5
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d6
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700d7
+Entity.point[1].v=800700d8
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d7
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d8
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700d9
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700da
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700d7
+Entity.point[1].v=800700d4
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700db
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700d8
+Entity.point[1].v=800700d5
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700dc
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700dd
+Entity.point[1].v=800700de
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700dd
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700de
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700df
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700e0
+Entity.point[1].v=800700e1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e0
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e1
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e2
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e3
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700e0
+Entity.point[1].v=800700dd
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e4
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700e1
+Entity.point[1].v=800700de
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e5
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700e6
+Entity.point[1].v=800700e7
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e6
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e7
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e8
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700e9
+Entity.point[1].v=800700ea
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700e9
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ea
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700eb
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ec
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700e9
+Entity.point[1].v=800700e6
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ed
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700ea
+Entity.point[1].v=800700e7
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ee
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ef
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f0
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f1
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f2
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f3
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f4
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f5
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700ef
+Entity.point[1].v=800700ee
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f6
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700f7
+Entity.point[1].v=800700f8
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f7
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f8
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700f9
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700fa
+Entity.point[1].v=800700fb
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700fa
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700fb
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700fc
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700fd
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700fa
+Entity.point[1].v=800700f7
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700fe
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700fb
+Entity.point[1].v=800700f8
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800700ff
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070100
+Entity.point[1].v=80070101
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070100
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070101
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070102
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070103
+Entity.point[1].v=80070104
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070103
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070104
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070105
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070106
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070103
+Entity.point[1].v=80070100
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070107
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070104
+Entity.point[1].v=80070101
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070108
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070109
+Entity.point[1].v=8007010a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070109
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007010a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007010b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007010c
+Entity.point[1].v=8007010d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007010c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007010d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007010e
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007010f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007010c
+Entity.point[1].v=80070109
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070110
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007010d
+Entity.point[1].v=8007010a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070111
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070112
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700f1
+Entity.point[1].v=800700f0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070113
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070114
+Entity.point[1].v=80070115
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070114
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070115
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070116
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070117
+Entity.point[1].v=80070118
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070117
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070118
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070119
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007011a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070117
+Entity.point[1].v=80070114
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007011b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070118
+Entity.point[1].v=80070115
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007011c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007011d
+Entity.point[1].v=8007011e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007011d
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007011e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007011f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070120
+Entity.point[1].v=80070121
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070120
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070121
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070122
+Entity.type=5003
+Entity.construction=0
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070123
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070120
+Entity.point[1].v=8007011d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070124
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070121
+Entity.point[1].v=8007011e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070125
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070126
+Entity.point[1].v=80070127
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070126
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070127
+Entity.type=2010
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070128
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070129
+Entity.point[1].v=8007012a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070129
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007012a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007012b
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007012c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070129
+Entity.point[1].v=80070126
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007012d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007012a
+Entity.point[1].v=80070127
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007012e
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007012f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800700f3
+Entity.point[1].v=800700f2
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070130
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070131
+Entity.point[1].v=80070132
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070131
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070132
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070133
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070134
+Entity.point[1].v=80070135
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070134
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070135
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070136
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070137
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070134
+Entity.point[1].v=80070131
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070138
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070135
+Entity.point[1].v=80070132
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070139
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007013a
+Entity.point[1].v=8007013b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007013a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007013b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007013c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007013d
+Entity.point[1].v=8007013e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007013d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007013e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007013f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070140
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007013d
+Entity.point[1].v=8007013a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070141
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007013e
+Entity.point[1].v=8007013b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070142
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070143
+Entity.point[1].v=80070144
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070143
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070144
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070145
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070146
+Entity.point[1].v=80070147
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070146
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070147
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070148
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070149
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070146
+Entity.point[1].v=80070143
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007014a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070147
+Entity.point[1].v=80070144
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007014b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007014c
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007014d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007014e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007014f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070150
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070151
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070152
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007014c
+Entity.point[1].v=8007014b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070153
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070154
+Entity.point[1].v=80070155
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070154
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070155
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070156
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070157
+Entity.point[1].v=80070158
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070157
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070158
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070159
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007015a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070157
+Entity.point[1].v=80070154
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007015b
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070158
+Entity.point[1].v=80070155
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007015c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007015d
+Entity.point[1].v=8007015e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007015d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007015e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007015f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070160
+Entity.point[1].v=80070161
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070160
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070161
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070162
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070163
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070160
+Entity.point[1].v=8007015d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070164
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070161
+Entity.point[1].v=8007015e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070165
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070166
+Entity.point[1].v=80070167
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070166
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070167
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070168
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070169
+Entity.point[1].v=8007016a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070169
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007016a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007016b
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007016c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070169
+Entity.point[1].v=80070166
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007016d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007016a
+Entity.point[1].v=80070167
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007016e
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007016f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007014e
+Entity.point[1].v=8007014d
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070170
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070171
+Entity.point[1].v=80070172
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070171
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070172
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070173
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070174
+Entity.point[1].v=80070175
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070174
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070175
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070176
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070177
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070174
+Entity.point[1].v=80070171
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070178
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070175
+Entity.point[1].v=80070172
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070179
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007017a
+Entity.point[1].v=8007017b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007017a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007017b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007017c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007017d
+Entity.point[1].v=8007017e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007017d
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007017e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007017f
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070180
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007017d
+Entity.point[1].v=8007017a
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070181
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007017e
+Entity.point[1].v=8007017b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070182
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070183
+Entity.point[1].v=80070184
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070183
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070184
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070185
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070186
+Entity.point[1].v=80070187
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070186
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070187
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070188
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070189
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070186
+Entity.point[1].v=80070183
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007018a
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070187
+Entity.point[1].v=80070184
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007018b
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actNormal.vx=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007018c
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070150
+Entity.point[1].v=8007014f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007018d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007018e
+Entity.point[1].v=8007018f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007018e
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007018f
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070190
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070191
+Entity.point[1].v=80070192
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070191
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070192
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070193
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actNormal.vy=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070194
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070191
+Entity.point[1].v=8007018e
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070195
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070192
+Entity.point[1].v=8007018f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070196
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=80070197
+Entity.point[1].v=80070198
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070197
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070198
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80070199
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007019a
+Entity.point[1].v=8007019b
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007019a
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007019b
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-15.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007019c
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vx=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007019d
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007019a
+Entity.point[1].v=80070197
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007019e
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=8007019b
+Entity.point[1].v=80070198
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8007019f
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800701a0
+Entity.point[1].v=800701a1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a0
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a1
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a2
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800701a3
+Entity.point[1].v=800701a4
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a3
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a4
+Entity.type=2010
+Entity.construction=0
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actPoint.z=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a5
+Entity.type=5003
+Entity.construction=0
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actNormal.vy=-1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a6
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800701a3
+Entity.point[1].v=800701a0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=800701a7
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=800701a4
+Entity.point[1].v=800701a1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00040001
+Constraint.ptB.v=00050002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000002
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00050001
+Constraint.ptB.v=00060002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000003
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00060001
+Constraint.ptB.v=00070002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000004
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.ptA.v=00070001
+Constraint.ptB.v=00040002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000005
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00040000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000006
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00050000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000007
+Constraint.type=81
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00060000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000008
+Constraint.type=80
+Constraint.group.v=00000002
+Constraint.workplane.v=80020000
+Constraint.entityA.v=00070000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=0000000a
+Constraint.type=30
+Constraint.group.v=00000003
+Constraint.valA=1.00000000000000000000
+Constraint.ptA.v=80030018
+Constraint.ptB.v=80030015
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+Constraint.disp.offset.x=-1.11433618504280240735
+Constraint.disp.offset.y=-1.07366998595325924271
+AddConstraint
+
+Constraint.h.v=0000000b
+Constraint.type=20
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.ptA.v=00080001
+Constraint.ptB.v=00090002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=0000000c
+Constraint.type=20
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.ptA.v=00090001
+Constraint.ptB.v=000a0002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=0000000d
+Constraint.type=20
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.ptA.v=000a0001
+Constraint.ptB.v=000b0002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=0000000e
+Constraint.type=20
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.ptA.v=000b0001
+Constraint.ptB.v=00080002
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=0000000f
+Constraint.type=81
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.entityA.v=00080000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000010
+Constraint.type=80
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.entityA.v=00090000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000011
+Constraint.type=81
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.entityA.v=000a0000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Constraint.h.v=00000012
+Constraint.type=80
+Constraint.group.v=00000004
+Constraint.workplane.v=80040000
+Constraint.entityA.v=000b0000
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
+Surface 00000001 00646464 8003002c 1 1
+SCtrl 0 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 20.00000000010000178463 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -15.00000000000000000000 -20.00000000010000178463 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 20.00000000010000178463 -20.00000000010000178463 1.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000004 0 -15.00000000000000000000 -20.00000000010000178463 1.00000000000000000000 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+TrimBy 0000000a 0 20.00000000010000178463 15.00000000000000000000 1.00000000000000000000 20.00000000010000178463 -20.00000000010000178463 1.00000000000000000000
+TrimBy 00000001 0 20.00000000010000178463 -20.00000000010000178463 1.00000000000000000000 -15.00000000000000000000 -20.00000000010000178463 1.00000000000000000000
+TrimBy 00000007 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 20.00000000010000178463 15.00000000000000000000 1.00000000000000000000
+TrimBy 0000007c 0 -5.00000000000000088818 5.00000000000000088818 1.00000000000000000000 -5.00000000000000177636 10.00000000000000177636 1.00000000000000000000
+TrimBy 00000079 0 -10.00000000000000177636 5.00000000000000266454 1.00000000000000000000 -5.00000000000000088818 5.00000000000000088818 1.00000000000000000000
+TrimBy 0000007a 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 -10.00000000000000177636 5.00000000000000266454 1.00000000000000000000
+TrimBy 0000007b 0 -5.00000000000000177636 10.00000000000000177636 1.00000000000000000000 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000080 0 4.99999999999999644729 5.00000000000000088818 1.00000000000000000000 4.99999999999999822364 10.00000000000000000000 1.00000000000000000000
+TrimBy 0000007d 0 -0.00000000000000133227 5.00000000000000088818 1.00000000000000000000 4.99999999999999644729 5.00000000000000088818 1.00000000000000000000
+TrimBy 0000007e 0 -0.00000000000000111022 10.00000000000000000000 1.00000000000000000000 -0.00000000000000133227 5.00000000000000088818 1.00000000000000000000
+TrimBy 0000007f 0 4.99999999999999822364 10.00000000000000000000 1.00000000000000000000 -0.00000000000000111022 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000084 0 14.99999999999999822364 5.00000000000000000000 1.00000000000000000000 14.99999999999999644729 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000081 0 10.00000000000000000000 5.00000000000000177636 1.00000000000000000000 14.99999999999999822364 5.00000000000000000000 1.00000000000000000000
+TrimBy 00000082 0 10.00000000000000177636 10.00000000000000000000 1.00000000000000000000 10.00000000000000000000 5.00000000000000177636 1.00000000000000000000
+TrimBy 00000083 0 14.99999999999999644729 10.00000000000000000000 1.00000000000000000000 10.00000000000000177636 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000088 0 -5.00000000000000088818 -4.99999999999999644729 1.00000000000000000000 -5.00000000000000088818 0.00000000000000177636 1.00000000000000000000
+TrimBy 00000085 0 -10.00000000000000177636 -4.99999999999999911182 1.00000000000000000000 -5.00000000000000088818 -4.99999999999999644729 1.00000000000000000000
+TrimBy 00000086 0 -10.00000000000000000000 0.00000000000000044409 1.00000000000000000000 -10.00000000000000177636 -4.99999999999999911182 1.00000000000000000000
+TrimBy 00000087 0 -5.00000000000000088818 0.00000000000000177636 1.00000000000000000000 -10.00000000000000000000 0.00000000000000044409 1.00000000000000000000
+TrimBy 0000008c 0 4.99999999999999822364 -4.99999999999999822364 1.00000000000000000000 4.99999999999999911182 0.00000000000000177636 1.00000000000000000000
+TrimBy 00000089 0 -0.00000000000000177636 -4.99999999999999911182 1.00000000000000000000 4.99999999999999822364 -4.99999999999999822364 1.00000000000000000000
+TrimBy 0000008a 0 -0.00000000000000088818 0.00000000000000177636 1.00000000000000000000 -0.00000000000000177636 -4.99999999999999911182 1.00000000000000000000
+TrimBy 0000008b 0 4.99999999999999911182 0.00000000000000177636 1.00000000000000000000 -0.00000000000000088818 0.00000000000000177636 1.00000000000000000000
+TrimBy 00000090 0 14.99999999999999644729 -4.99999999999999822364 1.00000000000000000000 15.00000000000000000000 0.00000000000000266454 1.00000000000000000000
+TrimBy 0000008d 0 10.00000000000000177636 -4.99999999999999822364 1.00000000000000000000 14.99999999999999644729 -4.99999999999999822364 1.00000000000000000000
+TrimBy 0000008e 0 10.00000000000000177636 0.00000000000000266454 1.00000000000000000000 10.00000000000000177636 -4.99999999999999822364 1.00000000000000000000
+TrimBy 0000008f 0 15.00000000000000000000 0.00000000000000266454 1.00000000000000000000 10.00000000000000177636 0.00000000000000266454 1.00000000000000000000
+TrimBy 00000094 0 -5.00000000000000000000 -14.99999999999999822364 1.00000000000000000000 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000091 0 -10.00000000000000000000 -14.99999999999999644729 1.00000000000000000000 -5.00000000000000000000 -14.99999999999999822364 1.00000000000000000000
+TrimBy 00000092 0 -10.00000000000000000000 -10.00000000000000177636 1.00000000000000000000 -10.00000000000000000000 -14.99999999999999644729 1.00000000000000000000
+TrimBy 00000093 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -10.00000000000000177636 1.00000000000000000000
+TrimBy 00000098 0 4.99999999999999911182 -14.99999999999999644729 1.00000000000000000000 4.99999999999999822364 -10.00000000000000177636 1.00000000000000000000
+TrimBy 00000095 0 -0.00000000000000177636 -14.99999999999999822364 1.00000000000000000000 4.99999999999999911182 -14.99999999999999644729 1.00000000000000000000
+TrimBy 00000096 0 -0.00000000000000266454 -10.00000000000000000000 1.00000000000000000000 -0.00000000000000177636 -14.99999999999999822364 1.00000000000000000000
+TrimBy 00000097 0 4.99999999999999822364 -10.00000000000000177636 1.00000000000000000000 -0.00000000000000266454 -10.00000000000000000000 1.00000000000000000000
+TrimBy 0000009c 0 14.99999999999999644729 -14.99999999999999644729 1.00000000000000000000 14.99999999999999644729 -10.00000000000000177636 1.00000000000000000000
+TrimBy 00000099 0 10.00000000000000177636 -14.99999999999999644729 1.00000000000000000000 14.99999999999999644729 -14.99999999999999644729 1.00000000000000000000
+TrimBy 0000009a 0 10.00000000000000355271 -10.00000000000000355271 1.00000000000000000000 10.00000000000000177636 -14.99999999999999644729 1.00000000000000000000
+TrimBy 0000009b 0 14.99999999999999644729 -10.00000000000000177636 1.00000000000000000000 10.00000000000000355271 -10.00000000000000355271 1.00000000000000000000
+AddSurface
+Surface 00000002 00646464 8003002d 1 1
+SCtrl 0 0 20.00000000010000178463 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 20.00000000010000178463 -20.00000000010000178463 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -15.00000000000000000000 -20.00000000010000178463 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000008 1 20.00000000010000178463 15.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+TrimBy 0000000b 1 20.00000000010000178463 -20.00000000010000178463 0.00000000000000000000 20.00000000010000178463 15.00000000000000000000 0.00000000000000000000
+TrimBy 00000002 1 -15.00000000000000000000 -20.00000000010000178463 0.00000000000000000000 20.00000000010000178463 -20.00000000010000178463 0.00000000000000000000
+TrimBy 00000005 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 -20.00000000010000178463 0.00000000000000000000
+TrimBy 000000a0 0 -5.00000000000000177636 10.00000000000000177636 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000009e 0 -10.00000000000000177636 5.00000000000000266454 0.00000000000000000000 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 0000009f 0 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 -5.00000000000000177636 10.00000000000000177636 0.00000000000000000000
+TrimBy 0000009d 0 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -10.00000000000000177636 5.00000000000000266454 0.00000000000000000000
+TrimBy 000000a4 0 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 4.99999999999999822364 5.00000000000000000000 0.00000000000000000000
+TrimBy 000000a2 0 0.00000000000000133227 5.00000000000000000000 0.00000000000000000000 0.00000000000000377476 10.00000000000000000000 0.00000000000000000000
+TrimBy 000000a3 0 0.00000000000000377476 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 000000a1 0 4.99999999999999822364 5.00000000000000000000 0.00000000000000000000 0.00000000000000133227 5.00000000000000000000 0.00000000000000000000
+TrimBy 000000a8 0 14.99999999999999822364 10.00000000000000000000 0.00000000000000000000 15.00000000000000000000 4.99999999999999911182 0.00000000000000000000
+TrimBy 000000a6 0 10.00000000000000000000 5.00000000000000088818 0.00000000000000000000 9.99999999999999822364 10.00000000000000177636 0.00000000000000000000
+TrimBy 000000a7 0 9.99999999999999822364 10.00000000000000177636 0.00000000000000000000 14.99999999999999822364 10.00000000000000000000 0.00000000000000000000
+TrimBy 000000a5 0 15.00000000000000000000 4.99999999999999911182 0.00000000000000000000 10.00000000000000000000 5.00000000000000088818 0.00000000000000000000
+TrimBy 000000ac 0 -5.00000000000000000000 0.00000000000000177636 0.00000000000000000000 -5.00000000000000088818 -4.99999999999999644729 0.00000000000000000000
+TrimBy 000000aa 0 -10.00000000000000000000 -4.99999999999999822364 0.00000000000000000000 -10.00000000000000000000 0.00000000000000088818 0.00000000000000000000
+TrimBy 000000ab 0 -10.00000000000000000000 0.00000000000000088818 0.00000000000000000000 -5.00000000000000000000 0.00000000000000177636 0.00000000000000000000
+TrimBy 000000a9 0 -5.00000000000000088818 -4.99999999999999644729 0.00000000000000000000 -10.00000000000000000000 -4.99999999999999822364 0.00000000000000000000
+TrimBy 000000b0 0 5.00000000000000000000 0.00000000000000266454 0.00000000000000000000 4.99999999999999911182 -4.99999999999999733546 0.00000000000000000000
+TrimBy 000000ae 0 0.00000000000000177636 -4.99999999999999822364 0.00000000000000000000 0.00000000000000266454 0.00000000000000266454 0.00000000000000000000
+TrimBy 000000af 0 0.00000000000000266454 0.00000000000000266454 0.00000000000000000000 5.00000000000000000000 0.00000000000000266454 0.00000000000000000000
+TrimBy 000000ad 0 4.99999999999999911182 -4.99999999999999733546 0.00000000000000000000 0.00000000000000177636 -4.99999999999999822364 0.00000000000000000000
+TrimBy 000000b4 0 15.00000000000000000000 0.00000000000000155431 0.00000000000000000000 15.00000000000000355271 -4.99999999999999911182 0.00000000000000000000
+TrimBy 000000b2 0 10.00000000000000000000 -4.99999999999999911182 0.00000000000000000000 10.00000000000000000000 0.00000000000000266454 0.00000000000000000000
+TrimBy 000000b3 0 10.00000000000000000000 0.00000000000000266454 0.00000000000000000000 15.00000000000000000000 0.00000000000000155431 0.00000000000000000000
+TrimBy 000000b1 0 15.00000000000000355271 -4.99999999999999911182 0.00000000000000000000 10.00000000000000000000 -4.99999999999999911182 0.00000000000000000000
+TrimBy 000000b8 0 -5.00000000000000088818 -10.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -14.99999999999999822364 0.00000000000000000000
+TrimBy 000000b6 0 -10.00000000000000177636 -14.99999999999999644729 0.00000000000000000000 -10.00000000000000000000 -10.00000000000000177636 0.00000000000000000000
+TrimBy 000000b7 0 -10.00000000000000000000 -10.00000000000000177636 0.00000000000000000000 -5.00000000000000088818 -10.00000000000000000000 0.00000000000000000000
+TrimBy 000000b5 0 -5.00000000000000000000 -14.99999999999999822364 0.00000000000000000000 -10.00000000000000177636 -14.99999999999999644729 0.00000000000000000000
+TrimBy 000000bc 0 5.00000000000000000000 -10.00000000000000177636 0.00000000000000000000 5.00000000000000088818 -15.00000000000000000000 0.00000000000000000000
+TrimBy 000000ba 0 0.00000000000000177636 -14.99999999999999644729 0.00000000000000000000 0.00000000000000355271 -10.00000000000000355271 0.00000000000000000000
+TrimBy 000000bb 0 0.00000000000000355271 -10.00000000000000355271 0.00000000000000000000 5.00000000000000000000 -10.00000000000000177636 0.00000000000000000000
+TrimBy 000000b9 0 5.00000000000000088818 -15.00000000000000000000 0.00000000000000000000 0.00000000000000177636 -14.99999999999999644729 0.00000000000000000000
+TrimBy 000000c0 0 15.00000000000000000000 -10.00000000000000177636 0.00000000000000000000 14.99999999999999822364 -14.99999999999999822364 0.00000000000000000000
+TrimBy 000000be 0 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 9.99999999999999822364 -10.00000000000000177636 0.00000000000000000000
+TrimBy 000000bf 0 9.99999999999999822364 -10.00000000000000177636 0.00000000000000000000 15.00000000000000000000 -10.00000000000000177636 0.00000000000000000000
+TrimBy 000000bd 0 14.99999999999999822364 -14.99999999999999822364 0.00000000000000000000 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000003 00646464 80030010 1 1
+SCtrl 0 0 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000001 1 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+TrimBy 00000002 0 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+TrimBy 00000003 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+TrimBy 0000000c 0 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000004 00646464 80030019 1 1
+SCtrl 0 0 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000004 1 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+TrimBy 00000005 0 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+TrimBy 00000006 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+TrimBy 00000003 0 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000005 00646464 80030022 1 1
+SCtrl 0 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000007 1 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+TrimBy 00000008 0 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+TrimBy 00000009 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+TrimBy 00000006 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000006 00646464 80030007 1 1
+SCtrl 0 0 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000a 1 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+TrimBy 0000000b 0 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+TrimBy 0000000c 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+TrimBy 00000009 0 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000007 00646464 8007000f 1 1
+SCtrl 0 0 -10.00000000000000000000 4.99999999989999999173 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -4.99999999989999999173 4.99999999989999999173 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -4.99999999989999999173 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000008 00646464 80070010 1 1
+SCtrl 0 0 -4.99999999989999999173 4.99999999989999999173 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 4.99999999989999999173 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -4.99999999989999999173 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000009 00646464 8007009f 1 1
+SCtrl 0 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000000d 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 0000000e 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000000f 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000018 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000000a 00646464 800700a8 1 1
+SCtrl 0 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000010 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000011 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000012 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 0000000f 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000000b 00646464 800700b1 1 1
+SCtrl 0 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000013 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000014 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 00000015 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 00000012 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000000c 00646464 80070097 1 1
+SCtrl 0 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000016 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 00000017 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 00000018 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000015 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000000d 00646464 8007001f 1 1
+SCtrl 0 0 0.00000000000000000000 4.99999999989999999173 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000010000000827 4.99999999989999999173 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000010000000827 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 0000000e 00646464 80070020 1 1
+SCtrl 0 0 5.00000000010000000827 4.99999999989999999173 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 4.99999999989999999173 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000010000000827 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 0000000f 00646464 800700bc 1 1
+SCtrl 0 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000019 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 0000001a 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000001b 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000024 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000010 00646464 800700c5 1 1
+SCtrl 0 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000001c 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 0000001d 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 0000001e 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 0000001b 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000011 00646464 800700ce 1 1
+SCtrl 0 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000001f 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000020 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 00000021 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 0000001e 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000012 00646464 800700b4 1 1
+SCtrl 0 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000022 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 00000023 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 00000024 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000021 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000013 00646464 8007002f 1 1
+SCtrl 0 0 10.00000000000000000000 4.99999999989999999173 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000010000000827 4.99999999989999999173 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000010000000827 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000014 00646464 80070030 1 1
+SCtrl 0 0 15.00000000010000000827 4.99999999989999999173 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 4.99999999989999999173 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000010000000827 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000015 00646464 800700d9 1 1
+SCtrl 0 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000026 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000025 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 00000030 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 00000027 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000016 00646464 800700e2 1 1
+SCtrl 0 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000029 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+TrimBy 00000028 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000027 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 0000002a 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000017 00646464 800700eb 1 1
+SCtrl 0 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000002c 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 0000002b 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 0000002a 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 0000002d 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000018 00646464 800700d1 1 1
+SCtrl 0 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000002f 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+TrimBy 0000002e 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+TrimBy 0000002d 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+TrimBy 00000030 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000019 00646464 8007003f 1 1
+SCtrl 0 0 -10.00000000000000000000 -5.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -4.99999999989999999173 -5.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -4.99999999989999999173 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 0000001a 00646464 80070040 1 1
+SCtrl 0 0 -4.99999999989999999173 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -4.99999999989999999173 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 0000001b 00646464 800700fc 1 1
+SCtrl 0 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000031 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 00000032 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000033 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 0000003c 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000001c 00646464 80070105 1 1
+SCtrl 0 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000034 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 00000035 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000036 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000033 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000001d 00646464 8007010e 1 1
+SCtrl 0 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000037 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 00000038 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000039 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000036 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000001e 00646464 800700f4 1 1
+SCtrl 0 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000003a 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 0000003b 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 0000003c 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000039 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000001f 00646464 8007004f 1 1
+SCtrl 0 0 0.00000000000000000000 -5.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000010000000827 -5.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000010000000827 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000020 00646464 80070050 1 1
+SCtrl 0 0 5.00000000010000000827 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000010000000827 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000021 00646464 80070119 1 1
+SCtrl 0 0 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000003d 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 0000003e 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 0000003f 0 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000048 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000022 00646464 80070122 1 1
+SCtrl 0 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000040 0 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 00000041 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000042 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 0000003f 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000023 00646464 8007012b 1 1
+SCtrl 0 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000043 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 00000044 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000045 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000042 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000024 00646464 80070111 1 1
+SCtrl 0 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000046 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 00000047 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000048 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000045 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 00000025 00646464 8007005f 1 1
+SCtrl 0 0 10.00000000000000000000 -5.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000010000000827 -5.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000010000000827 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000026 00646464 80070060 1 1
+SCtrl 0 0 15.00000000010000000827 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -5.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000010000000827 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000027 00646464 80070136 1 1
+SCtrl 0 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000004a 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 00000049 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 00000054 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 0000004b 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000028 00646464 8007013f 1 1
+SCtrl 0 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000004d 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+TrimBy 0000004c 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 0000004b 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 0000004e 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000029 00646464 80070148 1 1
+SCtrl 0 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000050 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 0000004f 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 0000004e 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 00000051 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 0000002a 00646464 8007012e 1 1
+SCtrl 0 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000053 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+TrimBy 00000052 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+TrimBy 00000051 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+TrimBy 00000054 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 0000002b 00646464 8007006f 1 1
+SCtrl 0 0 -10.00000000000000000000 -15.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -4.99999999989999999173 -15.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -4.99999999989999999173 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 0000002c 00646464 80070070 1 1
+SCtrl 0 0 -4.99999999989999999173 -15.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 -15.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -4.99999999989999999173 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 0000002d 00646464 80070159 1 1
+SCtrl 0 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000056 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000055 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 00000060 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 00000057 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 0000002e 00646464 80070162 1 1
+SCtrl 0 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000059 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000058 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000057 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 0000005a 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 0000002f 00646464 8007016b 1 1
+SCtrl 0 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000005c 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 0000005b 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 0000005a 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 0000005d 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000030 00646464 80070151 1 1
+SCtrl 0 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000005f 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 0000005e 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 0000005d 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000060 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000031 00646464 8007007f 1 1
+SCtrl 0 0 0.00000000000000000000 -15.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000010000000827 -15.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000010000000827 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000032 00646464 80070080 1 1
+SCtrl 0 0 5.00000000010000000827 -15.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -15.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000010000000827 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000033 00646464 80070176 1 1
+SCtrl 0 0 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000062 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000061 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 0000006c 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 00000063 0 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000034 00646464 8007017f 1 1
+SCtrl 0 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000065 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000064 0 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000063 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 00000066 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000035 00646464 80070188 1 1
+SCtrl 0 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000068 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 00000067 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000066 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000069 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000036 00646464 8007016e 1 1
+SCtrl 0 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000006b 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 0000006a 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 00000069 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 0000006c 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddSurface
+Surface 00000037 00646464 8007008f 1 1
+SCtrl 0 0 10.00000000000000000000 -15.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000010000000827 -15.00000000010000000827 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000010000000827 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000038 00646464 80070090 1 1
+SCtrl 0 0 15.00000000010000000827 -15.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -15.00000000010000000827 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000010000000827 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+AddSurface
+Surface 00000039 00646464 80070193 1 1
+SCtrl 0 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 0000006d 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 0000006e 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 0000006f 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000078 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000003a 00646464 8007019c 1 1
+SCtrl 0 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000070 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000071 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000072 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 0000006f 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000003b 00646464 800701a5 1 1
+SCtrl 0 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000073 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+TrimBy 00000074 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 00000075 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 00000072 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddSurface
+Surface 0000003c 00646464 8007018b 1 1
+SCtrl 0 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 0 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+SCtrl 1 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+TrimBy 00000076 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+TrimBy 00000077 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+TrimBy 00000078 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+TrimBy 00000075 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddSurface
+Curve 00000001 1 1 00000001 00000003
+CCtrl 0 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000002 1 1 00000002 00000003
+CCtrl 0 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+CurvePt 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000003 1 1 00000003 00000004
+CCtrl 0 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000004 1 1 00000001 00000004
+CCtrl 0 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000005 1 1 00000002 00000004
+CCtrl 0 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+CurvePt 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000006 1 1 00000004 00000005
+CCtrl 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000007 1 1 00000001 00000005
+CCtrl 0 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000008 1 1 00000002 00000005
+CCtrl 0 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -15.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+CurvePt 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000009 1 1 00000005 00000006
+CCtrl 0 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000a 1 1 00000001 00000006
+CCtrl 0 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 15.00000000000000000000 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000000b 1 1 00000002 00000006
+CCtrl 0 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 15.00000000000000000000 0.00000000000000000000
+CurvePt 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000c 1 1 00000006 00000003
+CCtrl 0 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 -20.00000000000000000000 1.00000000000000000000
+CurvePt 1 20.00000000000000000000 -20.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000d 1 1 00000007 00000009
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000000e 1 1 00000008 00000009
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000000f 1 1 00000009 0000000a
+CCtrl 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000010 1 1 00000007 0000000a
+CCtrl 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000011 1 1 00000008 0000000a
+CCtrl 0 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000012 1 1 0000000a 0000000b
+CCtrl 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000013 1 1 00000007 0000000b
+CCtrl 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000014 1 1 00000008 0000000b
+CCtrl 0 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000015 1 1 0000000b 0000000c
+CCtrl 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000016 1 1 00000007 0000000c
+CCtrl 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000017 1 1 00000008 0000000c
+CCtrl 0 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000018 1 1 0000000c 00000009
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000019 1 1 0000000d 0000000f
+CCtrl 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000001a 1 1 0000000e 0000000f
+CCtrl 0 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000001b 1 1 0000000f 00000010
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000001c 1 1 0000000d 00000010
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000001d 1 1 0000000e 00000010
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000001e 1 1 00000010 00000011
+CCtrl 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000001f 1 1 0000000d 00000011
+CCtrl 0 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000020 1 1 0000000e 00000011
+CCtrl 0 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000021 1 1 00000011 00000012
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000022 1 1 0000000d 00000012
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000023 1 1 0000000e 00000012
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000024 1 1 00000012 0000000f
+CCtrl 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000025 1 1 00000013 00000015
+CCtrl 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000026 1 1 00000014 00000015
+CCtrl 0 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000027 1 1 00000015 00000016
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000028 1 1 00000013 00000016
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000029 1 1 00000014 00000016
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000002a 1 1 00000016 00000017
+CCtrl 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000002b 1 1 00000013 00000017
+CCtrl 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000002c 1 1 00000014 00000017
+CCtrl 0 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000002d 1 1 00000017 00000018
+CCtrl 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000002e 1 1 00000013 00000018
+CCtrl 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000002f 1 1 00000014 00000018
+CCtrl 0 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000030 1 1 00000018 00000015
+CCtrl 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000031 1 1 00000019 0000001b
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000032 1 1 0000001a 0000001b
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000033 1 1 0000001b 0000001c
+CCtrl 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000034 1 1 00000019 0000001c
+CCtrl 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000035 1 1 0000001a 0000001c
+CCtrl 0 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000036 1 1 0000001c 0000001d
+CCtrl 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000037 1 1 00000019 0000001d
+CCtrl 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000038 1 1 0000001a 0000001d
+CCtrl 0 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000039 1 1 0000001d 0000001e
+CCtrl 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000003a 1 1 00000019 0000001e
+CCtrl 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000003b 1 1 0000001a 0000001e
+CCtrl 0 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000003c 1 1 0000001e 0000001b
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000003d 1 1 0000001f 00000021
+CCtrl 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000003e 1 1 00000020 00000021
+CCtrl 0 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000003f 1 1 00000021 00000022
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000040 1 1 0000001f 00000022
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000041 1 1 00000020 00000022
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000042 1 1 00000022 00000023
+CCtrl 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000043 1 1 0000001f 00000023
+CCtrl 0 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000044 1 1 00000020 00000023
+CCtrl 0 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000045 1 1 00000023 00000024
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000046 1 1 0000001f 00000024
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000047 1 1 00000020 00000024
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000048 1 1 00000024 00000021
+CCtrl 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000049 1 1 00000025 00000027
+CCtrl 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000004a 1 1 00000026 00000027
+CCtrl 0 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000004b 1 1 00000027 00000028
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000004c 1 1 00000025 00000028
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000004d 1 1 00000026 00000028
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000004e 1 1 00000028 00000029
+CCtrl 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000004f 1 1 00000025 00000029
+CCtrl 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000050 1 1 00000026 00000029
+CCtrl 0 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000051 1 1 00000029 0000002a
+CCtrl 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000052 1 1 00000025 0000002a
+CCtrl 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000053 1 1 00000026 0000002a
+CCtrl 0 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000054 1 1 0000002a 00000027
+CCtrl 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000055 1 1 0000002b 0000002d
+CCtrl 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000056 1 1 0000002c 0000002d
+CCtrl 0 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000057 1 1 0000002d 0000002e
+CCtrl 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000058 1 1 0000002b 0000002e
+CCtrl 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000059 1 1 0000002c 0000002e
+CCtrl 0 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000005a 1 1 0000002e 0000002f
+CCtrl 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000005b 1 1 0000002b 0000002f
+CCtrl 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000005c 1 1 0000002c 0000002f
+CCtrl 0 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000005d 1 1 0000002f 00000030
+CCtrl 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000005e 1 1 0000002b 00000030
+CCtrl 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000005f 1 1 0000002c 00000030
+CCtrl 0 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000060 1 1 00000030 0000002d
+CCtrl 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000061 1 1 00000031 00000033
+CCtrl 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000062 1 1 00000032 00000033
+CCtrl 0 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000063 1 1 00000033 00000034
+CCtrl 0 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000064 1 1 00000031 00000034
+CCtrl 0 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000065 1 1 00000032 00000034
+CCtrl 0 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000066 1 1 00000034 00000035
+CCtrl 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000067 1 1 00000031 00000035
+CCtrl 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000068 1 1 00000032 00000035
+CCtrl 0 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000069 1 1 00000035 00000036
+CCtrl 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000006a 1 1 00000031 00000036
+CCtrl 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000006b 1 1 00000032 00000036
+CCtrl 0 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000006c 1 1 00000036 00000033
+CCtrl 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000006d 1 1 00000037 00000039
+CCtrl 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000006e 1 1 00000038 00000039
+CCtrl 0 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000006f 1 1 00000039 0000003a
+CCtrl 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000070 1 1 00000037 0000003a
+CCtrl 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000071 1 1 00000038 0000003a
+CCtrl 0 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000072 1 1 0000003a 0000003b
+CCtrl 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000073 1 1 00000037 0000003b
+CCtrl 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000074 1 1 00000038 0000003b
+CCtrl 0 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000075 1 1 0000003b 0000003c
+CCtrl 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000076 1 1 00000037 0000003c
+CCtrl 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000077 1 1 00000038 0000003c
+CCtrl 0 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000078 1 1 0000003c 00000039
+CCtrl 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 00000079 1 1 00000001 00000009
+CCtrl 0 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000007a 1 1 00000001 0000000a
+CCtrl 0 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000007b 1 1 00000001 0000000b
+CCtrl 0 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000007c 1 1 00000001 0000000c
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000007d 1 1 00000001 0000000f
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000007e 1 1 00000001 00000010
+CCtrl 0 -0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -0.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000007f 1 1 00000001 00000011
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -0.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000080 1 1 00000001 00000012
+CCtrl 0 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000081 1 1 00000001 00000015
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000082 1 1 00000001 00000016
+CCtrl 0 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000083 1 1 00000001 00000017
+CCtrl 0 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000084 1 1 00000001 00000018
+CCtrl 0 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000085 1 1 00000001 0000001b
+CCtrl 0 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000086 1 1 00000001 0000001c
+CCtrl 0 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000087 1 1 00000001 0000001d
+CCtrl 0 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000088 1 1 00000001 0000001e
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000089 1 1 00000001 00000021
+CCtrl 0 -0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000008a 1 1 00000001 00000022
+CCtrl 0 -0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000008b 1 1 00000001 00000023
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -0.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000008c 1 1 00000001 00000024
+CCtrl 0 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000008d 1 1 00000001 00000027
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000008e 1 1 00000001 00000028
+CCtrl 0 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000008f 1 1 00000001 00000029
+CCtrl 0 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000090 1 1 00000001 0000002a
+CCtrl 0 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000091 1 1 00000001 0000002d
+CCtrl 0 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000092 1 1 00000001 0000002e
+CCtrl 0 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000093 1 1 00000001 0000002f
+CCtrl 0 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000094 1 1 00000001 00000030
+CCtrl 0 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000095 1 1 00000001 00000033
+CCtrl 0 -0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000096 1 1 00000001 00000034
+CCtrl 0 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000097 1 1 00000001 00000035
+CCtrl 0 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000098 1 1 00000001 00000036
+CCtrl 0 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 00000099 1 1 00000001 00000039
+CCtrl 0 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000009a 1 1 00000001 0000003a
+CCtrl 0 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000009b 1 1 00000001 0000003b
+CCtrl 0 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000009c 1 1 00000001 0000003c
+CCtrl 0 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 1.00000000000000000000
+AddCurve
+Curve 0000009d 1 1 00000002 00000009
+CCtrl 0 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000009e 1 1 00000002 0000000a
+CCtrl 0 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 0000009f 1 1 00000002 0000000b
+CCtrl 0 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a0 1 1 00000002 0000000c
+CCtrl 0 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a1 1 1 00000002 0000000f
+CCtrl 0 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a2 1 1 00000002 00000010
+CCtrl 0 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a3 1 1 00000002 00000011
+CCtrl 0 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a4 1 1 00000002 00000012
+CCtrl 0 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a5 1 1 00000002 00000015
+CCtrl 0 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a6 1 1 00000002 00000016
+CCtrl 0 10.00000000000000000000 5.00000000000000000000 -0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 10.00000000000000000000 -0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a7 1 1 00000002 00000017
+CCtrl 0 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a8 1 1 00000002 00000018
+CCtrl 0 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000a9 1 1 00000002 0000001b
+CCtrl 0 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000aa 1 1 00000002 0000001c
+CCtrl 0 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000ab 1 1 00000002 0000001d
+CCtrl 0 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000ac 1 1 00000002 0000001e
+CCtrl 0 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000ad 1 1 00000002 00000021
+CCtrl 0 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000ae 1 1 00000002 00000022
+CCtrl 0 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000af 1 1 00000002 00000023
+CCtrl 0 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b0 1 1 00000002 00000024
+CCtrl 0 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b1 1 1 00000002 00000027
+CCtrl 0 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b2 1 1 00000002 00000028
+CCtrl 0 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 0.00000000000000000000 -0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b3 1 1 00000002 00000029
+CCtrl 0 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b4 1 1 00000002 0000002a
+CCtrl 0 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 0.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 -5.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b5 1 1 00000002 0000002d
+CCtrl 0 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b6 1 1 00000002 0000002e
+CCtrl 0 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b7 1 1 00000002 0000002f
+CCtrl 0 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b8 1 1 00000002 00000030
+CCtrl 0 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 -5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000b9 1 1 00000002 00000033
+CCtrl 0 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000ba 1 1 00000002 00000034
+CCtrl 0 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000bb 1 1 00000002 00000035
+CCtrl 0 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -10.00000000000000000000 -0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 0.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000bc 1 1 00000002 00000036
+CCtrl 0 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 5.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 5.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000bd 1 1 00000002 00000039
+CCtrl 0 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000be 1 1 00000002 0000003a
+CCtrl 0 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000bf 1 1 00000002 0000003b
+CCtrl 0 10.00000000000000000000 -10.00000000000000000000 -0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -10.00000000000000000000 -0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 10.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+AddCurve
+Curve 000000c0 1 1 00000002 0000003c
+CCtrl 0 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CCtrl 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000 Weight 1.00000000000000000000
+CurvePt 1 15.00000000000000000000 -10.00000000000000000000 0.00000000000000000000
+CurvePt 1 15.00000000000000000000 -15.00000000000000000000 0.00000000000000000000
+AddCurve
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Our harness for running test cases, and reusable checks.
+//
+// Copyright 2016 whitequark
+//-----------------------------------------------------------------------------
+#include <regex>
+#include <cairo.h>
+
+#include "harness.h"
+
+#if defined(WIN32)
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+namespace SolveSpace {
+namespace Platform {
+ // These are defined in headless.cpp, and aren't exposed in solvespace.h.
+ extern std::vector<Platform::Path> fontFiles;
+}
+}
+
+
+#ifdef TEST_BUILD_ON_WINDOWS
+static const char *VALID_BUILD_PATH_SEPS = "/\\";
+static char BUILD_PATH_SEP = '\\';
+#else
+static const char *VALID_BUILD_PATH_SEPS = "/";
+static char BUILD_PATH_SEP = '/';
+#endif
+
+static std::string BuildRoot() {
+ static std::string rootDir;
+ if(!rootDir.empty()) return rootDir;
+
+ rootDir = __FILE__;
+ rootDir.erase(rootDir.find_last_of(VALID_BUILD_PATH_SEPS) + 1);
+ return rootDir;
+}
+
+static Platform::Path HostRoot() {
+ static Platform::Path rootDir;
+ if(!rootDir.IsEmpty()) return rootDir;
+
+ // No especially good way to do this, so let's assume somewhere up from
+ // the current directory there's our repository, with CMakeLists.txt, and
+ // pivot from there.
+ rootDir = Platform::Path::CurrentDirectory();
+
+ // We're never more than four levels deep.
+ for(size_t i = 0; i < 4; i++) {
+ FILE *f = OpenFile(rootDir.Join("CMakeLists.txt"), "r");
+ if(f) {
+ fclose(f);
+ rootDir = rootDir.Join("test");
+ return rootDir;
+ }
+ rootDir = rootDir.Parent();
+ }
+
+ ssassert(false, "Couldn't locate repository root");
+}
+
+enum class Color {
+ Red,
+ Green,
+ DarkGreen
+};
+
+static std::string Colorize(Color color, std::string input) {
+#if !defined(WIN32)
+ if(isatty(fileno(stdout))) {
+ switch(color) {
+ case Color::Red:
+ return "\e[1;31m" + input + "\e[0m";
+ case Color::Green:
+ return "\e[1;32m" + input + "\e[0m";
+ case Color::DarkGreen:
+ return "\e[36m" + input + "\e[0m";
+ }
+ }
+#endif
+ return input;
+}
+
+// Normalizes a savefile. Different platforms have slightly different floating-point
+// behavior, so if we want to compare savefiles byte-by-byte, we need to do something
+// to get rid of irrelevant differences in LSB.
+static std::string PrepareSavefile(std::string data) {
+ // Round everything to 2**30 ~ 1e9
+ const double precision = pow(2, 30);
+
+ size_t lineBegin = 0;
+ while(lineBegin < data.length()) {
+ size_t nextLineBegin = data.find('\n', lineBegin);
+ if(nextLineBegin == std::string::npos) {
+ nextLineBegin = data.length();
+ } else {
+ nextLineBegin++;
+ }
+
+ size_t eqPos = data.find('=', lineBegin);
+ if(eqPos < nextLineBegin) {
+ std::string key = data.substr(lineBegin, eqPos - lineBegin),
+ value = data.substr(eqPos + 1, nextLineBegin - eqPos - 2);
+
+ for(int i = 0; SolveSpaceUI::SAVED[i].type != 0; i++) {
+ if(SolveSpaceUI::SAVED[i].fmt != 'f') continue;
+ if(SolveSpaceUI::SAVED[i].desc != key) continue;
+ double f = strtod(value.c_str(), NULL);
+ f = round(f * precision) / precision;
+ std::string newValue = ssprintf("%.20f", f);
+ ssassert(value.size() == newValue.size(), "Expected no change in value length");
+ std::copy(newValue.begin(), newValue.end(),
+ data.begin() + eqPos + 1);
+ }
+
+ if(key == "Group.impFile") {
+ data.erase(lineBegin, nextLineBegin - lineBegin);
+ nextLineBegin = lineBegin;
+ }
+ }
+
+ size_t spPos = data.find(' ', lineBegin);
+ if(spPos < nextLineBegin) {
+ std::string cmd = data.substr(lineBegin, spPos - lineBegin);
+ if(!cmd.empty()) {
+ if(cmd == "Surface" || cmd == "SCtrl" || cmd == "TrimBy" ||
+ cmd == "Curve" || cmd == "CCtrl" || cmd == "CurvePt") {
+ data.erase(lineBegin, nextLineBegin - lineBegin);
+ nextLineBegin = lineBegin;
+ }
+ }
+ }
+
+ lineBegin = nextLineBegin;
+ }
+ return data;
+}
+
+bool Test::Helper::RecordCheck(bool success) {
+ checkCount++;
+ if(!success) failCount++;
+ return success;
+}
+
+void Test::Helper::PrintFailure(const char *file, int line, std::string msg) {
+ std::string shortFile = file;
+ shortFile.erase(0, BuildRoot().size());
+ fprintf(stderr, "test%c%s:%d: FAILED: %s\n",
+ BUILD_PATH_SEP, shortFile.c_str(), line, msg.c_str());
+}
+
+Platform::Path Test::Helper::GetAssetPath(std::string testFile, std::string assetName,
+ std::string mangle) {
+ if(!mangle.empty()) {
+ assetName.insert(assetName.rfind('.'), "." + mangle);
+ }
+ testFile.erase(0, BuildRoot().size());
+ testFile.erase(testFile.find_last_of(VALID_BUILD_PATH_SEPS) + 1);
+ return HostRoot().Join(Platform::Path::FromPortable(testFile + assetName));
+}
+
+bool Test::Helper::CheckBool(const char *file, int line, const char *expr, bool value,
+ bool reference) {
+ if(!RecordCheck(value == reference)) {
+ std::string msg = ssprintf("(%s) = %s ≠ %s", expr,
+ value ? "true" : "false",
+ reference ? "true" : "false");
+ PrintFailure(file, line, msg);
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool Test::Helper::CheckEqualString(const char *file, int line, const char *valueExpr,
+ const std::string &value, const std::string &reference) {
+ if(!RecordCheck(value == reference)) {
+ std::string msg = ssprintf("(%s) = \"%s\" ≠ \"%s\"", valueExpr,
+ value.c_str(), reference.c_str());
+ PrintFailure(file, line, msg);
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool Test::Helper::CheckEqualEpsilon(const char *file, int line, const char *valueExpr,
+ double value, double reference) {
+ bool result = fabs(value - reference) < LENGTH_EPS;
+ if(!RecordCheck(result)) {
+ std::string msg = ssprintf("(%s) = %.4g ≉ %.4g", valueExpr,
+ value, reference);
+ PrintFailure(file, line, msg);
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool Test::Helper::CheckLoad(const char *file, int line, const char *fixture) {
+ Platform::Path fixturePath = GetAssetPath(file, fixture);
+
+ FILE *f = OpenFile(fixturePath, "rb");
+ bool fixtureExists = (f != NULL);
+ if(f) fclose(f);
+
+ bool result = fixtureExists && SS.LoadFromFile(fixturePath);
+ if(!RecordCheck(result)) {
+ PrintFailure(file, line,
+ ssprintf("loading file '%s'", fixturePath.raw.c_str()));
+ return false;
+ } else {
+ SS.AfterNewFile();
+ SS.GW.offset = {};
+ SS.GW.scale = 10.0;
+ return true;
+ }
+}
+
+bool Test::Helper::CheckSave(const char *file, int line, const char *reference) {
+ Platform::Path refPath = GetAssetPath(file, reference),
+ outPath = GetAssetPath(file, reference, "out");
+ if(!RecordCheck(SS.SaveToFile(outPath))) {
+ PrintFailure(file, line,
+ ssprintf("saving file '%s'", refPath.raw.c_str()));
+ return false;
+ } else {
+ std::string refData, outData;
+ ReadFile(refPath, &refData);
+ ReadFile(outPath, &outData);
+ if(!RecordCheck(PrepareSavefile(refData) == PrepareSavefile(outData))) {
+ PrintFailure(file, line, "savefile doesn't match reference");
+ return false;
+ }
+
+ RemoveFile(outPath);
+ return true;
+ }
+}
+
+bool Test::Helper::CheckRender(const char *file, int line, const char *reference) {
+ // First, render to a framebuffer.
+ Camera camera = {};
+ camera.pixelRatio = 1;
+ camera.gridFit = true;
+ camera.width = 600;
+ camera.height = 600;
+ camera.projUp = SS.GW.projUp;
+ camera.projRight = SS.GW.projRight;
+ camera.scale = SS.GW.scale;
+
+ CairoPixmapRenderer pixmapCanvas;
+ pixmapCanvas.SetLighting(SS.GW.GetLighting());
+ pixmapCanvas.SetCamera(camera);
+ pixmapCanvas.Init();
+
+ pixmapCanvas.StartFrame();
+ SS.GW.Draw(&pixmapCanvas);
+ pixmapCanvas.FlushFrame();
+ pixmapCanvas.FinishFrame();
+ std::shared_ptr<Pixmap> frame = pixmapCanvas.ReadFrame();
+
+ pixmapCanvas.Clear();
+
+ // Now, diff framebuffer against reference render.
+ Platform::Path refPath = GetAssetPath(file, reference),
+ outPath = GetAssetPath(file, reference, "out"),
+ diffPath = GetAssetPath(file, reference, "diff");
+
+ std::shared_ptr<Pixmap> refPixmap = Pixmap::ReadPng(refPath, /*flip=*/true);
+ if(!RecordCheck(refPixmap && refPixmap->Equals(*frame))) {
+ frame->WritePng(outPath, /*flip=*/true);
+
+ if(!refPixmap) {
+ PrintFailure(file, line, "reference render not present");
+ return false;
+ }
+
+ ssassert(refPixmap->format == frame->format, "Expected buffer formats to match");
+ if(refPixmap->width != frame->width ||
+ refPixmap->height != frame->height) {
+ PrintFailure(file, line, "render doesn't match reference; dimensions differ");
+ } else {
+ std::shared_ptr<Pixmap> diffPixmap =
+ Pixmap::Create(refPixmap->format, refPixmap->width, refPixmap->height);
+
+ int diffPixelCount = 0;
+ for(size_t j = 0; j < refPixmap->height; j++) {
+ for(size_t i = 0; i < refPixmap->width; i++) {
+ if(!refPixmap->GetPixel(i, j).Equals(frame->GetPixel(i, j))) {
+ diffPixelCount++;
+ diffPixmap->SetPixel(i, j, RgbaColor::From(255, 0, 0, 255));
+ }
+ }
+ }
+
+ diffPixmap->WritePng(diffPath, /*flip=*/true);
+ std::string message =
+ ssprintf("render doesn't match reference; %d (%.2f%%) pixels differ",
+ diffPixelCount,
+ 100.0 * diffPixelCount / (refPixmap->width * refPixmap->height));
+ PrintFailure(file, line, message);
+ }
+ return false;
+ } else {
+ RemoveFile(outPath);
+ RemoveFile(diffPath);
+ return true;
+ }
+}
+
+bool Test::Helper::CheckRenderXY(const char *file, int line, const char *fixture) {
+ SS.GW.projRight = Vector::From(1, 0, 0);
+ SS.GW.projUp = Vector::From(0, 1, 0);
+ return CheckRender(file, line, fixture);
+}
+
+bool Test::Helper::CheckRenderIso(const char *file, int line, const char *fixture) {
+ SS.GW.projRight = Vector::From(0.707, 0.000, -0.707);
+ SS.GW.projUp = Vector::From(-0.408, 0.816, -0.408);
+ return CheckRender(file, line, fixture);
+}
+
+// Avoid global constructors; using a global static vector instead of a local one
+// breaks MinGW for some obscure reason.
+static std::vector<Test::Case> *testCasesPtr;
+int Test::Case::Register(Test::Case testCase) {
+ static std::vector<Test::Case> testCases;
+ testCases.push_back(testCase);
+ testCasesPtr = &testCases;
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ std::vector<std::string> args = Platform::InitCli(argc, argv);
+
+ std::regex filter(".*");
+ if(args.size() == 1) {
+ } else if(args.size() == 2) {
+ filter = args[1];
+ } else {
+ fprintf(stderr, "Usage: %s [test filter regex]\n", args[0].c_str());
+ return 1;
+ }
+
+ Platform::fontFiles.push_back(HostRoot().Join("Gentium-R.ttf"));
+
+ // Wreck order dependencies between tests!
+ std::random_shuffle(testCasesPtr->begin(), testCasesPtr->end());
+
+ auto testStartTime = std::chrono::steady_clock::now();
+ size_t ranTally = 0, skippedTally = 0, checkTally = 0, failTally = 0;
+ for(Test::Case &testCase : *testCasesPtr) {
+ std::string testCaseName = testCase.fileName;
+ testCaseName.erase(0, BuildRoot().size());
+ testCaseName.erase(testCaseName.find_last_of(VALID_BUILD_PATH_SEPS));
+ testCaseName += BUILD_PATH_SEP + testCase.caseName;
+
+ std::smatch filterMatch;
+ if(!std::regex_search(testCaseName, filterMatch, filter)) {
+ skippedTally += 1;
+ continue;
+ }
+
+ SS.Init();
+ SS.showToolbar = false;
+ SS.checkClosedContour = false;
+
+ Test::Helper helper = {};
+ testCase.fn(&helper);
+
+ SK.Clear();
+ SS.Clear();
+
+ ranTally += 1;
+ checkTally += helper.checkCount;
+ failTally += helper.failCount;
+ if(helper.checkCount == 0) {
+ fprintf(stderr, " %s test %s (empty)\n",
+ Colorize(Color::Red, "??").c_str(),
+ Colorize(Color::DarkGreen, testCaseName).c_str());
+ } else if(helper.failCount > 0) {
+ fprintf(stderr, " %s test %s\n",
+ Colorize(Color::Red, "NG").c_str(),
+ Colorize(Color::DarkGreen, testCaseName).c_str());
+ } else {
+ fprintf(stderr, " %s test %s\n",
+ Colorize(Color::Green, "OK").c_str(),
+ Colorize(Color::DarkGreen, testCaseName).c_str());
+ }
+ }
+
+ auto testEndTime = std::chrono::steady_clock::now();
+ std::chrono::duration<double> testTime = testEndTime - testStartTime;
+
+ if(failTally > 0) {
+ fprintf(stderr, "Failure! %u checks failed\n",
+ (unsigned)failTally);
+ } else {
+ fprintf(stderr, "Success! %u test cases (%u skipped), %u checks, %.3fs\n",
+ (unsigned)ranTally, (unsigned)skippedTally,
+ (unsigned)checkTally, testTime.count());
+ }
+
+ // At last, try to reset all caches we or our dependencies have, to make SNR
+ // of memory checking tools like valgrind higher.
+ cairo_debug_reset_static_data();
+
+ return (failTally > 0);
+}
--- /dev/null
+//-----------------------------------------------------------------------------
+// Our harness for running test cases, and reusable checks.
+//
+// Copyright 2016 whitequark
+//-----------------------------------------------------------------------------
+#include "solvespace.h"
+
+// Hack... we should rename the ones in ui.h instead.
+#undef CHECK_TRUE
+#undef CHECK_FALSE
+
+namespace SolveSpace {
+namespace Test {
+
+class Helper {
+public:
+ size_t checkCount;
+ size_t failCount;
+
+ bool RecordCheck(bool success);
+ void PrintFailure(const char *file, int line, std::string msg);
+ Platform::Path GetAssetPath(std::string testFile, std::string assetName,
+ std::string mangle = "");
+
+ bool CheckBool(const char *file, int line, const char *expr,
+ bool value, bool reference);
+ bool CheckEqualString(const char *file, int line, const char *valueExpr,
+ const std::string &value, const std::string &reference);
+ bool CheckEqualEpsilon(const char *file, int line, const char *valueExpr,
+ double value, double reference);
+ bool CheckLoad(const char *file, int line, const char *fixture);
+ bool CheckSave(const char *file, int line, const char *reference);
+ bool CheckRender(const char *file, int line, const char *fixture);
+ bool CheckRenderXY(const char *file, int line, const char *fixture);
+ bool CheckRenderIso(const char *file, int line, const char *fixture);
+};
+
+class Case {
+public:
+ std::string fileName;
+ std::string caseName;
+ std::function<void(Helper *)> fn;
+
+ static int Register(Case testCase);
+};
+
+}
+}
+
+using namespace SolveSpace;
+
+#define TEST_CASE(name) \
+ static void Test_##name(Test::Helper *); \
+ static Test::Case TestCase_##name = { __FILE__, #name, Test_##name }; \
+ static int TestReg_##name = Test::Case::Register(TestCase_##name); \
+ static void Test_##name(Test::Helper *helper) // { ... }
+
+#define CHECK_TRUE(cond) \
+ do { if(!helper->CheckBool(__FILE__, __LINE__, #cond, cond, true)) return; } while(0)
+#define CHECK_FALSE(cond) \
+ do { if(!helper->CheckBool(__FILE__, __LINE__, #cond, cond, false)) return; } while(0)
+#define CHECK_EQ_STR(value, reference) \
+ do { if(!helper->CheckEqualString(__FILE__, __LINE__, \
+ #value, value, reference)) return; } while(0)
+#define CHECK_EQ_EPS(value, reference) \
+ do { if(!helper->CheckEqualEpsilon(__FILE__, __LINE__, \
+ #value, value, reference)) return; } while(0)
+#define CHECK_LOAD(fixture) \
+ do { if(!helper->CheckLoad(__FILE__, __LINE__, fixture)) return; } while(0)
+#define CHECK_SAVE(fixture) \
+ do { if(!helper->CheckSave(__FILE__, __LINE__, fixture)) return; } while(0)
+#define CHECK_RENDER(reference) \
+ do { if(!helper->CheckRenderXY(__FILE__, __LINE__, reference)) return; } while(0)
+#define CHECK_RENDER_ISO(reference) \
+ do { if(!helper->CheckRenderIso(__FILE__, __LINE__, reference)) return; } while(0)
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=500
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=14000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00040021
+AddParam
+
+Param.h.v.=00040022
+AddParam
+
+Param.h.v.=00040023
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00040021
+AddParam
+
+Param.h.v.=00040022
+AddParam
+
+Param.h.v.=00040023
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00040021
+AddParam
+
+Param.h.v.=00040022
+AddParam
+
+Param.h.v.=00040023
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040040
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=400
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=13000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.distance.v=00040040
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040040
+Entity.type=4000
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actDistance=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(free_in_3d_roundtrip) {
+ CHECK_LOAD("free_in_3d.slvs");
+ CHECK_RENDER("free_in_3d.png");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v20) {
+ CHECK_LOAD("free_in_3d_v20.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(free_in_3d_migrate_from_v22) {
+ CHECK_LOAD("free_in_3d_v22.slvs");
+ CHECK_SAVE("free_in_3d.slvs");
+}
+
+TEST_CASE(normal_dof) {
+ CHECK_LOAD("normal.slvs");
+ SS.GenerateAll(SolveSpaceUI::Generate::ALL, /*andFindFree=*/true);
+ CHECK_RENDER("normal_dof.png");
+}
+
+TEST_CASE(free_in_3d_dof) {
+ CHECK_LOAD("free_in_3d.slvs");
+ SS.GenerateAll(SolveSpaceUI::Generate::ALL, /*andFindFree=*/true);
+ CHECK_RENDER_ISO("free_in_3d_dof.png");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040019
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=0004001a
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=0004001c
+AddParam
+
+Param.h.v.=0004001d
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=0004001f
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040020
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=300
+Request.extraPoints=2
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=12000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.point[4].v=00040005
+Entity.point[5].v=00040006
+Entity.extraPoints=2
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040005
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040006
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040019
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=0004001a
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=0004001c
+AddParam
+
+Param.h.v.=0004001d
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=0004001f
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040020
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=300
+Request.extraPoints=2
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=12000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.point[4].v=00040005
+Entity.point[5].v=00040006
+Entity.extraPoints=2
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040005
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040006
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+AddParam
+
+Param.h.v.=00040014
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040019
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=0004001a
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=0004001c
+AddParam
+
+Param.h.v.=0004001d
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=0004001f
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040020
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=300
+Request.extraPoints=2
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=12000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.point[4].v=00040005
+Entity.point[5].v=00040006
+Entity.extraPoints=2
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040005
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=-10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040006
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040019
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=0004001a
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=301
+Request.extraPoints=1
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=12001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.extraPoints=1
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040019
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=0004001a
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=301
+Request.extraPoints=1
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=12001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.extraPoints=1
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040019
+Param.val=-10.00000000000000000000
+AddParam
+
+Param.h.v.=0004001a
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=301
+Request.extraPoints=1
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=12001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.extraPoints=1
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-10.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=101
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000003
+Group.type=5300
+Group.order=2
+Group.name=normal
+Group.activeWorkplane.v=00010000
+Group.color=00646464
+Group.skipFirst=0
+Group.meshCombine=2
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+ 1 00010000 0
+ 2 00010001 0
+ 3 00010020 0
+ 4 00020000 0
+ 5 00020001 0
+ 6 00020020 0
+ 7 00030000 0
+ 8 00030001 0
+ 9 00030020 0
+ 10 00040000 0
+ 11 00040001 0
+ 12 00040002 0
+ 13 00040003 0
+ 14 00040004 0
+ 15 00040020 0
+ 16 80020000 0
+ 17 80020001 0
+ 18 80020002 0
+}
+Group.impFileRel=normal.slvs
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=40000001
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=80030000
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030001
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=80030002
+AddParam
+
+Param.h.v.=80030003
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=80030004
+AddParam
+
+Param.h.v.=80030005
+AddParam
+
+Param.h.v.=80030006
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=0
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030002
+Entity.type=2011
+Entity.construction=1
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030003
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030005
+Entity.type=2011
+Entity.construction=1
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030006
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030005
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030008
+Entity.type=2011
+Entity.construction=1
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030009
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030008
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000a
+Entity.type=16000
+Entity.construction=0
+Entity.file=drawing.png
+Entity.point[0].v=8003000b
+Entity.point[1].v=8003000c
+Entity.point[2].v=8003000d
+Entity.point[3].v=8003000e
+Entity.normal.v=8003000f
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000b
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000c
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000d
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000e
+Entity.type=2011
+Entity.construction=0
+Entity.actPoint.x=20.00000000000000000000
+Entity.actPoint.y=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=8003000f
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=8003000b
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030011
+Entity.type=3011
+Entity.construction=0
+Entity.point[0].v=80030012
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80030012
+Entity.type=2011
+Entity.construction=1
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=110
+Constraint.group.v=00000003
+Constraint.workplane.v=00010000
+Constraint.valP.v=40000001
+Constraint.entityA.v=80030006
+Constraint.entityB.v=00020020
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+AddParam
+
+Param.h.v.=00040011
+Param.val=10.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+AddParam
+
+Param.h.v.=00040014
+AddParam
+
+Param.h.v.=00040016
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=00040017
+AddParam
+
+Param.h.v.=00040019
+Param.val=15.00000000000000000000
+AddParam
+
+Param.h.v.=0004001a
+Param.val=10.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=700
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+Request.file=drawing.png
+Request.aspectRatio=1.50000000000000000000
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=16000
+Entity.construction=0
+Entity.file=drawing.png
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=15.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=15.00000000000000000000
+Entity.actPoint.y=10.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ // Can't render images through cairo for now.
+ // CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(linked_roundtrip) {
+ CHECK_LOAD("linked.slvs");
+ // CHECK_RENDER("linked.png");
+ CHECK_SAVE("linked.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=200
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=11000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040016
+Param.val=23.92131768269594616072
+AddParam
+
+Param.h.v.=00040017
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040019
+Param.val=23.92131768269594616072
+AddParam
+
+Param.h.v.=0004001a
+Param.val=5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=600
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+Request.str=Text
+Request.font=Gentium-R.ttf
+Request.aspectRatio=2.89213176826959461607
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=15000
+Entity.construction=0
+Entity.str=Text
+Entity.font=Gentium-R.ttf
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.point[2].v=00040003
+Entity.point[3].v=00040004
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040003
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=23.92131768269594616072
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040004
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=23.92131768269594616072
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=600
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+Request.str=Text
+Request.font=Gentium-R.ttf
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=15000
+Entity.construction=0
+Entity.str=Text
+Entity.font=GOST.ttf
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.activeWorkplane.v=80020000
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040011
+Param.val=5.00000000000000000000
+AddParam
+
+Param.h.v.=00040013
+Param.val=-5.00000000000000000000
+AddParam
+
+Param.h.v.=00040014
+Param.val=-5.00000000000000000000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=600
+Request.workplane.v=80020000
+Request.group.v=00000002
+Request.construction=0
+Request.str=Text
+Request.font=Gentium-R.ttf
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=15000
+Entity.construction=0
+Entity.str=Text
+Entity.font=Gentium-R.ttf
+Entity.point[0].v=00040001
+Entity.point[1].v=00040002
+Entity.normal.v=00040020
+Entity.workplane.v=80020000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040002
+Entity.type=2001
+Entity.construction=0
+Entity.workplane.v=80020000
+Entity.actPoint.x=-5.00000000000000000000
+Entity.actPoint.y=-5.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3001
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.workplane.v=80020000
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040020
+Param.val=0.88047623921714945894
+AddParam
+
+Param.h.v.=00040021
+Param.val=-0.27984814233312144127
+AddParam
+
+Param.h.v.=00040022
+Param.val=-0.36470519963100095362
+AddParam
+
+Param.h.v.=00040023
+Param.val=-0.11591689595929521861
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=100
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.actNormal.w=0.88047623921714945894
+Entity.actNormal.vx=-0.27984814233312144127
+Entity.actNormal.vy=-0.36470519963100095362
+Entity.actNormal.vz=-0.11591689595929521861
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=1
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00010001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allDimsReference=0
+Group.remap={
+}
+Group.impFile=
+Group.impFileRel=
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040020
+Param.val=0.88047623921714946000
+AddParam
+
+Param.h.v.=00040021
+Param.val=-0.27984814233312144000
+AddParam
+
+Param.h.v.=00040022
+Param.val=-0.36470519963100095000
+AddParam
+
+Param.h.v.=00040023
+Param.val=-0.11591689595929522000
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=100
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.actNormal.w=0.88047623921714946000
+Entity.actNormal.vx=-0.27984814233312144000
+Entity.actNormal.vy=-0.36470519963100095000
+Entity.actNormal.vz=-0.11591689595929522000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00010001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+±²³SolveSpaceREVa
+
+
+Group.h.v=00000001
+Group.type=5000
+Group.name=#references
+Group.color=ff000000
+Group.skipFirst=0
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Group.h.v=00000002
+Group.type=5001
+Group.order=1
+Group.name=sketch-in-plane
+Group.color=ff000000
+Group.subtype=6000
+Group.skipFirst=0
+Group.predef.q.w=1.00000000000000000000
+Group.predef.origin.v=00010001
+Group.predef.swapUV=0
+Group.predef.negateU=0
+Group.predef.negateV=0
+Group.visible=1
+Group.suppress=0
+Group.relaxConstraints=0
+Group.allowRedundant=0
+Group.allDimsReference=0
+Group.scale=1.00000000000000000000
+Group.remap={
+}
+AddGroup
+
+Param.h.v.=00010010
+AddParam
+
+Param.h.v.=00010011
+AddParam
+
+Param.h.v.=00010012
+AddParam
+
+Param.h.v.=00010020
+Param.val=1.00000000000000000000
+AddParam
+
+Param.h.v.=00010021
+AddParam
+
+Param.h.v.=00010022
+AddParam
+
+Param.h.v.=00010023
+AddParam
+
+Param.h.v.=00020010
+AddParam
+
+Param.h.v.=00020011
+AddParam
+
+Param.h.v.=00020012
+AddParam
+
+Param.h.v.=00020020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020021
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020022
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00020023
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030010
+AddParam
+
+Param.h.v.=00030011
+AddParam
+
+Param.h.v.=00030012
+AddParam
+
+Param.h.v.=00030020
+Param.val=0.50000000000000000000
+AddParam
+
+Param.h.v.=00030021
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030022
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00030023
+Param.val=-0.50000000000000000000
+AddParam
+
+Param.h.v.=00040010
+AddParam
+
+Param.h.v.=00040011
+AddParam
+
+Param.h.v.=00040012
+AddParam
+
+Param.h.v.=00040020
+Param.val=0.88047623921714945894
+AddParam
+
+Param.h.v.=00040021
+Param.val=-0.27984814233312144127
+AddParam
+
+Param.h.v.=00040022
+Param.val=-0.36470519963100095362
+AddParam
+
+Param.h.v.=00040023
+Param.val=-0.11591689595929521861
+AddParam
+
+Request.h.v=00000001
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000002
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000003
+Request.type=100
+Request.group.v=00000001
+Request.construction=0
+AddRequest
+
+Request.h.v=00000004
+Request.type=100
+Request.group.v=00000002
+Request.construction=0
+AddRequest
+
+Entity.h.v=00010000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.normal.v=00010020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00010020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00010001
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.normal.v=00020020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00020020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00020001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=0.50000000000000000000
+Entity.actNormal.vy=0.50000000000000000000
+Entity.actNormal.vz=0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.normal.v=00030020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00030020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00030001
+Entity.actNormal.w=0.50000000000000000000
+Entity.actNormal.vx=-0.50000000000000000000
+Entity.actNormal.vy=-0.50000000000000000000
+Entity.actNormal.vz=-0.50000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.normal.v=00040020
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040001
+Entity.type=2000
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=00040020
+Entity.type=3000
+Entity.construction=0
+Entity.point[0].v=00040001
+Entity.actNormal.w=0.88047623921714945894
+Entity.actNormal.vx=-0.27984814233312144127
+Entity.actNormal.vy=-0.36470519963100095362
+Entity.actNormal.vz=-0.11591689595929521861
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020000
+Entity.type=10000
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.normal.v=80020001
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020001
+Entity.type=3010
+Entity.construction=0
+Entity.point[0].v=80020002
+Entity.actNormal.w=1.00000000000000000000
+Entity.actVisible=1
+AddEntity
+
+Entity.h.v=80020002
+Entity.type=2012
+Entity.construction=0
+Entity.actVisible=1
+AddEntity
+
+Constraint.h.v=00000001
+Constraint.type=20
+Constraint.group.v=00000002
+Constraint.ptA.v=00010001
+Constraint.ptB.v=00040001
+Constraint.other=0
+Constraint.other2=0
+Constraint.reference=0
+AddConstraint
+
--- /dev/null
+#include "harness.h"
+
+TEST_CASE(normal_roundtrip) {
+ CHECK_LOAD("normal.slvs");
+ CHECK_RENDER("normal.png");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v20) {
+ CHECK_LOAD("normal_v20.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+
+TEST_CASE(normal_migrate_from_v22) {
+ CHECK_LOAD("normal_v22.slvs");
+ CHECK_SAVE("normal.slvs");
+}
+++ /dev/null
-include_directories(
- ${PNG_INCLUDE_DIRS})
-
-link_directories(
- ${PNG_LIBRARY_DIRS})
-
-add_executable(png2c
- png2c.cpp)
-
-target_link_libraries(png2c
- ${PNG_LIBRARIES})
-
-add_executable(unifont2c
- unifont2c.cpp)
-
-target_link_libraries(unifont2c
- ${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
-
-add_executable(lff2c
- lff2c.cpp)
-
-target_link_libraries(lff2c
- ${ZLIB_LIBRARIES})
+++ /dev/null
-#define _USE_MATH_DEFINES
-#include <zlib.h>
-#include <cmath>
-#include <cctype>
-#include <algorithm>
-#include <vector>
-#include <string>
-#include <map>
-#include <iostream>
-#include <fstream>
-#include <sstream>
-
-#define TOLERANCE 1e-6
-
-double correctAngle(double a) {
- return M_PI + remainder(a - M_PI, 2 * M_PI);
-}
-
-struct Point {
- double x;
- double y;
-
- Point operator+(const Point &o) const { return { x + o.x, y + o.y }; }
- Point operator-(const Point &o) const { return { x - o.x, y - o.y }; }
- Point operator*(const Point &o) const { return { x * o.x, y * o.y }; }
- Point operator/(const Point &o) const { return { x / o.x, y / o.y }; }
-
- Point operator*(double k) const { return { x * k, y * k }; }
- Point operator/(double k) const { return { x / k, y / k }; }
-
- double length() const{
- return sqrt(x * x + y * y);
- }
-
- double angle() const {
- return correctAngle(atan2(y, x));
- }
-
- double distanceTo(const Point &v) const {
- return (*this - v).length();
- }
-
- double angleTo(const Point &v) const {
- return (v - *this).angle();
- }
-
- static Point polar(double radius, double angle) {
- return { radius * cos(angle), radius * sin(angle) };
- }
-
- bool operator==(const Point &o) const { return x == o.x && y == o.y; }
- bool operator!=(const Point &o) const { return x != o.x || y != o.y; }
-
-};
-
-struct Curve {
- std::vector<Point> points;
-};
-
-struct Glyph {
- char32_t character;
- char32_t baseCharacter;
- std::vector<Curve> curves;
-
- void getHorizontalBounds(double *rminx, double *rmaxx) const {
- double minx = 0;
- double maxx = 0;
- if(!curves.empty()) {
- minx = curves[0].points[0].x;
- maxx = minx;
- for(const Curve &c : curves) {
- for(const Point &p : c.points) {
- maxx = std::max(maxx, p.x);
- minx = std::min(minx, p.x);
- }
- }
- }
- if(rminx) *rminx = minx;
- if(rmaxx) *rmaxx = maxx;
- }
-
- void getVerticalBounds(double *rminy, double *rmaxy) const {
- double miny = 0;
- double maxy = 0;
- if(!curves.empty()) {
- miny = curves[0].points[0].y;
- maxy = miny;
- for(const Curve &c : curves) {
- for(const Point &p : c.points) {
- maxy = std::max(maxy, p.y);
- miny = std::min(miny, p.y);
- }
- }
- }
- if(rminy) *rminy = miny;
- if(rmaxy) *rmaxy = maxy;
- }
-
- void getHorizontalMetrics(double *leftSideBearing, double *boundingWidth) const {
- double minx, maxx;
- getHorizontalBounds(&minx, &maxx);
- *leftSideBearing = minx;
- *boundingWidth = maxx - minx;
- }
-
- bool operator<(const Glyph &o) const { return character < o.character; }
-};
-
-struct Font {
- double letterSpacing;
- double wordSpacing;
- std::vector<Glyph> glyphs;
-
- const Glyph &findGlyph(char32_t character) {
- return *std::find_if(glyphs.begin(), glyphs.end(),
- [&](const Glyph &g) { return g.character == character; });
- }
-
- void getGlyphBound(double *rminw, double *rminh, double *rmaxw, double *rmaxh) {
- if(glyphs.empty()) {
- *rminw = 0.0;
- *rmaxw = 0.0;
- *rminh = 0.0;
- *rmaxh = 0.0;
- return;
- }
-
- glyphs[0].getHorizontalBounds(rminw, rmaxw);
- glyphs[0].getVerticalBounds(rminh, rmaxh);
- for(const Glyph &g : glyphs) {
- double minw, minh, maxw, maxh;
- g.getHorizontalBounds(&minw, &maxw);
- g.getVerticalBounds(&minh, &maxh);
- *rminw = std::min(*rminw, minw);
- *rminh = std::min(*rminh, minh);
- *rmaxw = std::max(*rmaxw, maxw);
- *rmaxh = std::max(*rmaxh, maxh);
- }
- }
-
- void createArc(Curve &curve, const Point &cp, double radius,
- double a1, double a2, bool reversed) {
- if (radius < 1e-6) return;
-
- double aSign = 1.0;
- if(reversed) {
- if(a1 <= a2 + TOLERANCE) a1 += 2.0 * M_PI;
- aSign = -1.0;
- } else {
- if(a2 <= a1 + TOLERANCE) a2 += 2.0 * M_PI;
- }
-
- // Angle Step (rad)
- double da = fabs(a2 - a1);
- int numPoints = 8;
- double aStep = aSign * da / double(numPoints);
-
- for(int i = 0; i <= numPoints; i++) {
- curve.points.push_back(cp + Point::polar(radius, a1 + aStep * i));
- }
- }
-
- void createBulge(const Point &v, double bulge, Curve &curve) {
- bool reversed = bulge < 0.0;
- double alpha = atan(bulge) * 4.0;
- Point &point = curve.points.back();
-
- Point middle = (point + v) / 2.0;
- double dist = point.distanceTo(v) / 2.0;
- double angle = point.angleTo(v);
-
- // alpha can't be 0.0 at this point
- double radius = fabs(dist / sin(alpha / 2.0));
- double wu = fabs(radius*radius - dist*dist);
- double h = sqrt(wu);
-
- if(bulge > 0.0) {
- angle += M_PI_2;
- } else {
- angle -= M_PI_2;
- }
-
- if (fabs(alpha) > M_PI) {
- h *= -1.0;
- }
-
- Point center = Point::polar(h, angle);
- center = center + middle;
-
- double a1 = center.angleTo(point);
- double a2 = center.angleTo(v);
- createArc(curve, center, radius, a1, a2, reversed);
- }
-
- void readLff(const std::string &path) {
- gzFile lfffont = gzopen(path.c_str(), "rb");
- if(!lfffont) {
- std::cerr << path << ": gzopen failed" << std::endl;
- std::exit(1);
- }
-
- // Read line by line until we find a new letter:
- Glyph *currentGlyph = nullptr;
- while(!gzeof(lfffont)) {
- std::string line;
- do {
- char buf[128] = {0};
- if(!gzgets(lfffont, buf, sizeof(buf)))
- break;
- line += buf;
- } while(line.back() != '\n');
-
- if(line.empty() || line[0] == '\n') {
- continue;
- } else if(line[0] == '#') {
- // This is comment or metadata.
- std::istringstream ss(line.substr(1));
-
- std::vector<std::string> tokens;
- while(!ss.eof()) {
- std::string token;
- std::getline(ss, token, ':');
- std::istringstream(token) >> token; // trim
- if(!token.empty())
- tokens.push_back(token);
- }
-
- // If not in form of "a:b" then it's not metadata, just a comment.
- if (tokens.size() != 2)
- continue;
-
- std::string &identifier = tokens[0];
- std::string &value = tokens[1];
-
- std::transform(identifier.begin(), identifier.end(), identifier.begin(),
- ::tolower);
- if (identifier == "letterspacing") {
- std::istringstream(value) >> letterSpacing;
- } else if (identifier == "wordspacing") {
- std::istringstream(value) >> wordSpacing;
- } else if (identifier == "linespacingfactor") {
- /* don't care */
- } else if (identifier == "author") {
- /* don't care */
- } else if (identifier == "name") {
- /* don't care */
- } else if (identifier == "license") {
- /* don't care */
- } else if (identifier == "encoding") {
- /* don't care */
- } else if (identifier == "created") {
- /* don't care */
- }
- } else if(line[0] == '[') {
- // This is a glyph.
- size_t closingPos;
- char32_t chr = std::stoi(line.substr(1), &closingPos, 16);;
- if(line[closingPos + 1] != ']') {
- std::cerr << "unrecognized character number: " << line << std::endl;
- currentGlyph = nullptr;
- continue;
- }
-
- glyphs.emplace_back();
- currentGlyph = &glyphs.back();
- currentGlyph->character = chr;
- currentGlyph->baseCharacter = 0;
- } else if(currentGlyph != nullptr) {
- if (line[0] == 'C') {
- // This is a reference to another glyph.
- currentGlyph->baseCharacter = std::stoi(line.substr(1), nullptr, 16);
- } else {
- // This is a series of curves.
- currentGlyph->curves.emplace_back();
- Curve &curve = currentGlyph->curves.back();
-
- std::stringstream ss(line);
- while (!ss.eof()) {
- std::string vertex;
- std::getline(ss, vertex, ';');
-
- std::stringstream ssv(vertex);
- std::string coord;
- Point p;
-
- if(!std::getline(ssv, coord, ',')) continue;
- p.x = std::stod(coord);
-
- if(!std::getline(ssv, coord, ',')) continue;
- p.y = std::stod(coord);
-
- if(!std::getline(ssv, coord, ',') || coord[0] != 'A') {
- // This is just a point.
- curve.points.push_back(p);
- } else {
- // This is a point with a bulge.
- double bulge = std::stod(coord.substr(1));
- createBulge(p, bulge, curve);
- }
- }
- }
- } else {
- std::cerr << "unrecognized line: " << line << std::endl;
- }
- }
- gzclose(lfffont);
- }
-
- void writeCppHeader(const std::string &hName) {
- std::sort(glyphs.begin(), glyphs.end());
-
- std::ofstream ts(hName, std::ios::out);
-
- double minX, minY, maxX, maxY;
- getGlyphBound(&minX, &minY, &maxX, &maxY);
-
- double size = 32766.0;
- double scale = size / std::max({ fabs(maxX), fabs(minX), fabs(maxY), fabs(minY) });
-
- double capHeight, ascender, descender;
- findGlyph('A').getVerticalBounds(nullptr, &capHeight);
- findGlyph('h').getVerticalBounds(nullptr, &ascender);
- findGlyph('p').getVerticalBounds(&descender, nullptr);
-
- // We use tabs for indentation here to make compilation slightly faster
- ts <<
- "/**** This is a generated file - do not edit ****/\n\n"
- "#ifndef __VECTORFONT_TABLE_H\n"
- "#define __VECTORFONT_TABLE_H\n"
- "\n"
- "#define PEN_UP 32767\n"
- "#define UP PEN_UP\n"
- "\n"
- "#define FONT_CAP_HEIGHT ((int16_t)" << (int)floor(capHeight * scale) << ")\n" <<
- "#define FONT_ASCENDER ((int16_t)" << (int)floor(ascender * scale) << ")\n" <<
- "#define FONT_DESCENDER ((int16_t)" << (int)floor(descender * scale) << ")\n" <<
- "#define FONT_SIZE (FONT_ASCENDER-FONT_DESCENDER)\n"
- "\n"
- "struct VectorGlyph {\n"
- "\tchar32_t character;\n"
- "\tchar32_t baseCharacter;\n"
- "\tint leftSideBearing;\n"
- "\tint boundingWidth;\n"
- "\tint advanceWidth;\n"
- "\tconst int16_t *data;\n"
- "};\n"
- "\n"
- "const int16_t VectorFontData[] = {\n"
- "\tUP, UP,\n";
-
- std::map<char32_t, size_t> glyphIndexes;
- size_t index = 2;
- for(const Glyph &g : glyphs) {
- ts << "\t// U+" << std::hex << g.character << std::dec << "\n";
- glyphIndexes[g.character] = index;
- for(const Curve &c : g.curves) {
- for(const Point &p : c.points) {
- ts << "\t" << (int)floor(p.x * scale) << ", " <<
- (int)floor(p.y * scale) << ",\n";
- index += 2;
- }
- ts << "\tUP, UP,\n";
- index += 2;
- }
- ts << "\tUP, UP,\n"; // end-of-glyph marker
- index += 2;
- }
-
- ts <<
- "};\n"
- "\n"
- "const VectorGlyph VectorFont[] = {\n"
- "\t// U+20\n"
- "\t{ 32, 0, 0, 0, " << (int)floor(wordSpacing * scale) << ", &VectorFontData[0] },\n";
-
- for(const Glyph &g : glyphs) {
- double leftSideBearing, boundingWidth;
- g.getHorizontalMetrics(&leftSideBearing, &boundingWidth);
-
- ts << "\t// U+" << std::hex << g.character << std::dec << "\n";
- ts << "\t{ " << g.character << ", "
- << g.baseCharacter << ", "
- << (int)floor(leftSideBearing * scale) << ", "
- << (int)floor(boundingWidth * scale) << ", "
- << (int)floor((leftSideBearing + boundingWidth +
- letterSpacing) * scale) << ", ";
- ts << "&VectorFontData[" << glyphIndexes[g.character] << "] },\n";
- }
-
- ts <<
- "};\n"
- "\n"
- "#undef UP\n"
- "\n"
- "#endif\n";
- }
-};
-
-int main(int argc, char** argv) {
- if(argc != 3) {
- std::cerr << "Usage: " << argv[0] << " <header out> <lff in>\n" << std::endl;
- return 1;
- }
-
- Font font;
- font.readLff(argv[2]);
- font.writeCppHeader(argv[1]);
-
- return 0;
-}
+++ /dev/null
-#include <stdio.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-#include <png.h>
-
-#define die(msg) do { fprintf(stderr, "%s\n", msg); abort(); } while(0)
-
-void write_png(FILE *out, const char *filename) {
- FILE *fp = fopen(filename, "rb");
- if (!fp)
- die("png fopen failed");
-
- png_byte header[8] = {};
- if(fread(header, 1, 8, fp) != 8)
- die("png fread failed");
-
- if(png_sig_cmp(header, 0, 8))
- die("png_sig_cmp failed");
-
- png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
- if(!png)
- die("png_create_read_struct failed");
-
- png_set_expand(png);
- png_set_strip_alpha(png);
-
- png_infop png_info = png_create_info_struct(png);
- if (!png_info)
- die("png_create_info_struct failed");
-
- if (setjmp(png_jmpbuf(png)))
- die("png_init_io failed");
-
- png_init_io(png, fp);
- png_set_sig_bytes(png, 8);
-
- png_read_info(png, png_info);
-
- int width = png_get_image_width(png, png_info);
- int height = png_get_image_height(png, png_info);
- if(width != 24 || height != 24)
- die("not a 24x24 png");
-
- png_read_update_info(png, png_info);
-
- if (setjmp(png_jmpbuf(png)))
- die("png_read_image failed");
-
- png_bytepp image = (png_bytepp) malloc(sizeof(png_bytep) * height);
- for (int y = 0; y < height; y++)
- image[y] = (png_bytep) malloc(png_get_rowbytes(png, png_info));
-
- png_read_image(png, (png_bytepp) image);
-
- for(int y = height - 1; y >= 0; y--) {
- for(int x = 0; x < (int)png_get_rowbytes(png, png_info); x += 3) {
- unsigned char r = image[y][x + 0],
- g = image[y][x + 1],
- b = image[y][x + 2];
-
- if(r + g + b < 11)
- r = g = b = 30;
- fprintf(out, " 0x%02x, 0x%02x, 0x%02x,\n", r, g, b);
- }
- }
-
- for (int y = 0; y < height; y++)
- free(image[y]);
- free(image);
-
- fclose(fp);
-
- png_destroy_read_struct(&png, &png_info, NULL);
-}
-
-int main(int argc, char** argv) {
- if(argc < 3) {
- fprintf(stderr, "Usage: %s <source out> <header out> <png in>...\n",
- argv[0]);
- return 1;
- }
-
- FILE *source = fopen(argv[1], "wt");
- if(!source)
- die("source fopen failed");
-
- FILE *header = fopen(argv[2], "wt");
- if(!header)
- die("header fopen failed");
-
- fprintf(source, "/**** This is a generated file - do not edit ****/\n\n");
- fprintf(header, "/**** This is a generated file - do not edit ****/\n\n");
-
- for(int i = 3; i < argc; i++) {
- const char *filename = argv[i];
- const char *basename = strrchr(filename, '/'); /* cmake uses / even on Windows */
- if(basename == NULL) {
- basename = filename;
- } else {
- basename++; // skip separator
- }
-
- char *stemname = (char*) calloc(strlen(basename), 1);
- strncpy(stemname, basename, strchr(basename, '.') - basename);
- for(size_t j = 0; j < strlen(stemname); j++) {
- if(!isalnum(stemname[j]))
- stemname[j] = '_';
- }
-
- fprintf(header, "extern unsigned char Icon_%s[24*24*3];\n", stemname);
-
- fprintf(source, "unsigned char Icon_%s[24*24*3] = {\n", stemname);
- write_png(source, filename);
- fprintf(source, "};\n\n");
-
- free(stemname);
- }
-
- fclose(source);
- fclose(header);
-}
+++ /dev/null
-#include <stdio.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <zlib.h>
-#include <png.h>
-
-#define die(msg) do { fprintf(stderr, "%s\n", msg); abort(); } while(0)
-
-#ifdef NDEBUG
-#define COMPRESSION_LEVEL 9
-#else
-#define COMPRESSION_LEVEL 5
-#endif
-
-unsigned short* read_png(const char *filename) {
- FILE *fp = fopen(filename, "rb");
- if (!fp)
- die("png fopen failed");
-
- png_byte header[8] = {};
- if(fread(header, 1, 8, fp) != 8)
- die("png fread failed");
-
- if(png_sig_cmp(header, 0, 8))
- die("png_sig_cmp failed");
-
- png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
- if(!png)
- die("png_create_read_struct failed");
-
- png_set_expand(png);
- png_set_strip_alpha(png);
-
- png_infop png_info = png_create_info_struct(png);
- if (!png_info)
- die("png_create_info_struct failed");
-
- if (setjmp(png_jmpbuf(png)))
- die("png_init_io failed");
-
- png_init_io(png, fp);
- png_set_sig_bytes(png, 8);
-
- png_read_info(png, png_info);
-
- int width = png_get_image_width(png, png_info);
- int height = png_get_image_height(png, png_info);
- if(width != 16 || height != 16)
- die("not a 16x16 png");
-
- png_read_update_info(png, png_info);
-
- if (setjmp(png_jmpbuf(png)))
- die("png_read_image failed");
-
- png_bytepp image = (png_bytepp) malloc(sizeof(png_bytep) * height);
- for (int y = 0; y < height; y++)
- image[y] = (png_bytep) malloc(png_get_rowbytes(png, png_info));
-
- png_read_image(png, (png_bytepp) image);
-
- unsigned short *glyph = (unsigned short *) calloc(16, 2);
-
- for(int y = 0; y < height; y++) {
- for(int x = 0; x < (int)png_get_rowbytes(png, png_info); x += 3) {
- unsigned char r = image[y][x + 0],
- g = image[y][x + 1],
- b = image[y][x + 2];
-
- if(r + g + b >= 11) {
- glyph[y] |= 1 << (width - x / 3);
- }
- }
- }
-
- for (int y = 0; y < height; y++)
- free(image[y]);
- free(image);
-
- fclose(fp);
-
- png_destroy_read_struct(&png, &png_info, NULL);
-
- return glyph;
-}
-
-const static unsigned short replacement[16] = {
- 0xAAAA, 0xAAAA, 0xAAAA, 0xAAAA,
- 0xAAAA, 0xAAAA, 0xAAAA, 0xAAAA,
- 0xAAAA, 0xAAAA, 0xAAAA, 0xAAAA,
- 0xAAAA, 0xAAAA, 0xAAAA, 0xAAAA,
-};
-
-struct CodepointProperties {
- bool exists:1;
- bool isWide:1;
-};
-
-int main(int argc, char** argv) {
- if(argc < 3) {
- fprintf(stderr, "Usage: %s <header/source out> <unifont.hex> <png glyph>...\n"
- " where <png glyph>s are mapped into private use area\n"
- " starting at U+E000.\n",
- argv[0]);
- return 1;
- }
-
- const int codepoint_count = 0x10000;
- unsigned short **font =
- (unsigned short **)calloc(sizeof(unsigned short*), codepoint_count);
- CodepointProperties *properties =
- (CodepointProperties *)calloc(sizeof(CodepointProperties), codepoint_count);
-
- const int private_start = 0xE000;
- for(int i = 3; i < argc; i++) {
- int codepoint = private_start + i - 3;
- font[codepoint] = read_png(argv[i]);
- properties[codepoint].exists = true;
- }
-
- gzFile unifont = gzopen(argv[2], "rb");
- if(!unifont)
- die("unifont fopen failed");
-
- while(1) {
- char buf[100];
- if(!gzgets(unifont, buf, sizeof(buf))){
- if(gzeof(unifont)) {
- break;
- } else {
- die("unifont gzgets failed");
- }
- }
-
- unsigned short codepoint;
- unsigned short *glyph = (unsigned short *) calloc(32, 1);
- bool isWide;
- if( sscanf(buf, "%4hx:%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx"
- "%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx\n",
- &codepoint,
- &glyph[0], &glyph[1], &glyph[2], &glyph[3],
- &glyph[4], &glyph[5], &glyph[6], &glyph[7],
- &glyph[8], &glyph[9], &glyph[10], &glyph[11],
- &glyph[12], &glyph[13], &glyph[14], &glyph[15]) == 17) {
- /* read 16x16 character */
- isWide = true;
- } else if(sscanf(buf, "%4hx:%2hx%2hx%2hx%2hx%2hx%2hx%2hx%2hx"
- "%2hx%2hx%2hx%2hx%2hx%2hx%2hx%2hx\n",
- &codepoint,
- &glyph[0], &glyph[1], &glyph[2], &glyph[3],
- &glyph[4], &glyph[5], &glyph[6], &glyph[7],
- &glyph[8], &glyph[9], &glyph[10], &glyph[11],
- &glyph[12], &glyph[13], &glyph[14], &glyph[15]) == 17) {
- /* read 8x16 character */
- for(int i = 0; i < 16; i++)
- glyph[i] <<= 8;
- isWide = false;
- } else {
- die("parse unifont character");
- }
-
- font[codepoint] = glyph;
- properties[codepoint].exists = true;
- properties[codepoint].isWide = isWide;
- }
-
- gzclose(unifont);
-
- FILE *source = fopen(argv[1], "wt");
- if(!source)
- die("source fopen failed");
-
- const int chunk_size = 64 * 64,
- chunks = codepoint_count / chunk_size;
-
- const int chunk_input_size = chunk_size * 16 * 16;
- unsigned int chunk_output_size[chunks] = {};
-
- unsigned char *chunk_data = (unsigned char *)calloc(1, chunk_input_size);
- unsigned int chunk_data_index;
-
- fprintf(source, "/**** This is a generated file - do not edit ****/\n\n");
-
- for(int chunk_index = 0; chunk_index < chunks; chunk_index++) {
- chunk_data_index = 0;
-
- const int chunk_start = chunk_index * chunk_size;
- for(int codepoint = chunk_start; codepoint < chunk_start + chunk_size; codepoint++) {
- const unsigned short *glyph = font[codepoint] != NULL ? font[codepoint] : replacement;
- for(int x = 15; x >= 0; x--) {
- for(int y = 0; y < 16; y++) {
- chunk_data[chunk_data_index++] = (glyph[y] & (1 << x)) ? 0xff : 0;
- }
- }
-
- if(font[codepoint] != NULL)
- free(font[codepoint]);
- }
-
- fprintf(source, "static const uint8_t CompressedFontTextureChunk%d[] = {\n",
- chunk_start / chunk_size);
-
- z_stream stream;
- stream.zalloc = Z_NULL;
- stream.zfree = Z_NULL;
- stream.opaque = Z_NULL;
- if(deflateInit(&stream, COMPRESSION_LEVEL) != Z_OK)
- die("deflateInit failed");
-
- stream.next_in = chunk_data;
- stream.avail_in = chunk_input_size;
-
- do {
- unsigned char compressed_chunk_data[16384] = {};
- stream.next_out = compressed_chunk_data;
- stream.avail_out = sizeof(compressed_chunk_data);
- deflate(&stream, Z_FINISH);
-
- chunk_output_size[chunk_index] += sizeof(compressed_chunk_data) - stream.avail_out;
- for(size_t i = 0; i < sizeof(compressed_chunk_data) - stream.avail_out; i += 16) {
- unsigned char *d = &compressed_chunk_data[i];
- fprintf(source, " %3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d, "
- "%3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d,\n",
- d[ 0], d[ 1], d[ 2], d[ 3], d[ 4], d[ 5], d[ 6], d[ 7],
- d[ 8], d[ 9], d[10], d[11], d[12], d[13], d[14], d[15]);
- }
- } while(stream.avail_out == 0);
-
- deflateEnd(&stream);
-
- fprintf(source, "};\n\n");
- }
-
- free(chunk_data);
- free(font);
-
- fprintf(source, "static const struct {\n"
- " size_t length;"
- " const uint8_t *data;"
- "} CompressedFontTexture[%d] = {\n", chunks);
- for(int i = 0; i < chunks; i++) {
- fprintf(source, " { %d, CompressedFontTextureChunk%d },\n",
- chunk_output_size[i], i);
- }
- fprintf(source, "};\n\n");
-
- fprintf(source, "struct GlyphProperties {\n"
- " bool exists:1;\n"
- " bool isWide:1;\n"
- "} CodepointProperties[%d] = {\n", codepoint_count);
- for(int i = 0; i < codepoint_count; i++) {
- fprintf(source, " { %s, %s },\n",
- properties[i].exists ? "true" : "false",
- properties[i].isWide ? "true" : "false");
- }
- fprintf(source, "};\n");
-
- free(properties);
-
- fclose(source);
-}